wp/wp-admin/js/theme-plugin-editor.js
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
     8 	window.wp = {};
     8 	window.wp = {};
     9 }
     9 }
    10 
    10 
    11 wp.themePluginEditor = (function( $ ) {
    11 wp.themePluginEditor = (function( $ ) {
    12 	'use strict';
    12 	'use strict';
    13 	var component, TreeLinks;
    13 	var component, TreeLinks,
       
    14 		__ = wp.i18n.__, _n = wp.i18n._n, sprintf = wp.i18n.sprintf;
    14 
    15 
    15 	component = {
    16 	component = {
    16 		l10n: {
       
    17 			lintError: {
       
    18 				singular: '',
       
    19 				plural: ''
       
    20 			},
       
    21 			saveAlert: '',
       
    22 			saveError: ''
       
    23 		},
       
    24 		codeEditor: {},
    17 		codeEditor: {},
    25 		instance: null,
    18 		instance: null,
    26 		noticeElements: {},
    19 		noticeElements: {},
    27 		dirty: false,
    20 		dirty: false,
    28 		lintErrors: []
    21 		lintErrors: []
    32 	 * Initialize component.
    25 	 * Initialize component.
    33 	 *
    26 	 *
    34 	 * @since 4.9.0
    27 	 * @since 4.9.0
    35 	 *
    28 	 *
    36 	 * @param {jQuery}         form - Form element.
    29 	 * @param {jQuery}         form - Form element.
    37 	 * @param {object}         settings - Settings.
    30 	 * @param {Object}         settings - Settings.
    38 	 * @param {object|boolean} settings.codeEditor - Code editor settings (or `false` if syntax highlighting is disabled).
    31 	 * @param {Object|boolean} settings.codeEditor - Code editor settings (or `false` if syntax highlighting is disabled).
    39 	 * @returns {void}
    32 	 * @return {void}
    40 	 */
    33 	 */
    41 	component.init = function init( form, settings ) {
    34 	component.init = function init( form, settings ) {
    42 
    35 
    43 		component.form = form;
    36 		component.form = form;
    44 		if ( settings ) {
    37 		if ( settings ) {
    73 
    66 
    74 		$( component.initFileBrowser );
    67 		$( component.initFileBrowser );
    75 
    68 
    76 		$( window ).on( 'beforeunload', function() {
    69 		$( window ).on( 'beforeunload', function() {
    77 			if ( component.dirty ) {
    70 			if ( component.dirty ) {
    78 				return component.l10n.saveAlert;
    71 				return __( 'The changes you made will be lost if you navigate away from this page.' );
    79 			}
    72 			}
    80 			return undefined;
    73 			return undefined;
    81 		} );
    74 		} );
    82 
    75 
    83 		component.docsLookUpList.on( 'change', function() {
    76 		component.docsLookUpList.on( 'change', function() {
    92 
    85 
    93 	/**
    86 	/**
    94 	 * Set up and display the warning modal.
    87 	 * Set up and display the warning modal.
    95 	 *
    88 	 *
    96 	 * @since 4.9.0
    89 	 * @since 4.9.0
    97 	 * @returns {void}
    90 	 * @return {void}
    98 	 */
    91 	 */
    99 	component.showWarning = function() {
    92 	component.showWarning = function() {
   100 		// Get the text within the modal.
    93 		// Get the text within the modal.
   101 		var rawMessage = component.warning.find( '.file-editor-warning-message' ).text();
    94 		var rawMessage = component.warning.find( '.file-editor-warning-message' ).text();
   102 		// Hide all the #wpwrap content from assistive technologies.
    95 		// Hide all the #wpwrap content from assistive technologies.
   122 
   115 
   123 	/**
   116 	/**
   124 	 * Constrain tabbing within the warning modal.
   117 	 * Constrain tabbing within the warning modal.
   125 	 *
   118 	 *
   126 	 * @since 4.9.0
   119 	 * @since 4.9.0
   127 	 * @param {object} event jQuery event object.
   120 	 * @param {Object} event jQuery event object.
   128 	 * @returns {void}
   121 	 * @return {void}
   129 	 */
   122 	 */
   130 	component.constrainTabbing = function( event ) {
   123 	component.constrainTabbing = function( event ) {
   131 		var firstTabbable, lastTabbable;
   124 		var firstTabbable, lastTabbable;
   132 
   125 
   133 		if ( 9 !== event.which ) {
   126 		if ( 9 !== event.which ) {
   148 
   141 
   149 	/**
   142 	/**
   150 	 * Dismiss the warning modal.
   143 	 * Dismiss the warning modal.
   151 	 *
   144 	 *
   152 	 * @since 4.9.0
   145 	 * @since 4.9.0
   153 	 * @returns {void}
   146 	 * @return {void}
   154 	 */
   147 	 */
   155 	component.dismissWarning = function() {
   148 	component.dismissWarning = function() {
   156 
   149 
   157 		wp.ajax.post( 'dismiss-wp-pointer', {
   150 		wp.ajax.post( 'dismiss-wp-pointer', {
   158 			pointer: component.themeOrPlugin + '_editor_notice'
   151 			pointer: component.themeOrPlugin + '_editor_notice'
   166 
   159 
   167 	/**
   160 	/**
   168 	 * Callback for when a change happens.
   161 	 * Callback for when a change happens.
   169 	 *
   162 	 *
   170 	 * @since 4.9.0
   163 	 * @since 4.9.0
   171 	 * @returns {void}
   164 	 * @return {void}
   172 	 */
   165 	 */
   173 	component.onChange = function() {
   166 	component.onChange = function() {
   174 		component.dirty = true;
   167 		component.dirty = true;
   175 		component.removeNotice( 'file_saved' );
   168 		component.removeNotice( 'file_saved' );
   176 	};
   169 	};
   178 	/**
   171 	/**
   179 	 * Submit file via Ajax.
   172 	 * Submit file via Ajax.
   180 	 *
   173 	 *
   181 	 * @since 4.9.0
   174 	 * @since 4.9.0
   182 	 * @param {jQuery.Event} event - Event.
   175 	 * @param {jQuery.Event} event - Event.
   183 	 * @returns {void}
   176 	 * @return {void}
   184 	 */
   177 	 */
   185 	component.submit = function( event ) {
   178 	component.submit = function( event ) {
   186 		var data = {}, request;
   179 		var data = {}, request;
   187 		event.preventDefault(); // Prevent form submission in favor of Ajax below.
   180 		event.preventDefault(); // Prevent form submission in favor of Ajax below.
   188 		$.each( component.form.serializeArray(), function() {
   181 		$.each( component.form.serializeArray(), function() {
   231 
   224 
   232 		request.fail( function( response ) {
   225 		request.fail( function( response ) {
   233 			var notice = $.extend(
   226 			var notice = $.extend(
   234 				{
   227 				{
   235 					code: 'save_error',
   228 					code: 'save_error',
   236 					message: component.l10n.saveError
   229 					message: __( 'Something went wrong. Your change may not have been saved. Please try again. There is also a chance that you may need to manually fix and upload the file over FTP.' )
   237 				},
   230 				},
   238 				response,
   231 				response,
   239 				{
   232 				{
   240 					type: 'error',
   233 					type: 'error',
   241 					dismissible: true
   234 					dismissible: true
   259 	/**
   252 	/**
   260 	 * Add notice.
   253 	 * Add notice.
   261 	 *
   254 	 *
   262 	 * @since 4.9.0
   255 	 * @since 4.9.0
   263 	 *
   256 	 *
   264 	 * @param {object}   notice - Notice.
   257 	 * @param {Object}   notice - Notice.
   265 	 * @param {string}   notice.code - Code.
   258 	 * @param {string}   notice.code - Code.
   266 	 * @param {string}   notice.type - Type.
   259 	 * @param {string}   notice.type - Type.
   267 	 * @param {string}   notice.message - Message.
   260 	 * @param {string}   notice.message - Message.
   268 	 * @param {boolean}  [notice.dismissible=false] - Dismissible.
   261 	 * @param {boolean}  [notice.dismissible=false] - Dismissible.
   269 	 * @param {Function} [notice.onDismiss] - Callback for when a user dismisses the notice.
   262 	 * @param {Function} [notice.onDismiss] - Callback for when a user dismisses the notice.
   270 	 * @returns {jQuery} Notice element.
   263 	 * @return {jQuery} Notice element.
   271 	 */
   264 	 */
   272 	component.addNotice = function( notice ) {
   265 	component.addNotice = function( notice ) {
   273 		var noticeElement;
   266 		var noticeElement;
   274 
   267 
   275 		if ( ! notice.code ) {
   268 		if ( ! notice.code ) {
   301 	 * Remove notice.
   294 	 * Remove notice.
   302 	 *
   295 	 *
   303 	 * @since 4.9.0
   296 	 * @since 4.9.0
   304 	 *
   297 	 *
   305 	 * @param {string} code - Notice code.
   298 	 * @param {string} code - Notice code.
   306 	 * @returns {boolean} Whether a notice was removed.
   299 	 * @return {boolean} Whether a notice was removed.
   307 	 */
   300 	 */
   308 	component.removeNotice = function( code ) {
   301 	component.removeNotice = function( code ) {
   309 		if ( component.noticeElements[ code ] ) {
   302 		if ( component.noticeElements[ code ] ) {
   310 			component.noticeElements[ code ].slideUp( 'fast', function() {
   303 			component.noticeElements[ code ].slideUp( 'fast', function() {
   311 				$( this ).remove();
   304 				$( this ).remove();
   318 
   311 
   319 	/**
   312 	/**
   320 	 * Initialize code editor.
   313 	 * Initialize code editor.
   321 	 *
   314 	 *
   322 	 * @since 4.9.0
   315 	 * @since 4.9.0
   323 	 * @returns {void}
   316 	 * @return {void}
   324 	 */
   317 	 */
   325 	component.initCodeEditor = function initCodeEditor() {
   318 	component.initCodeEditor = function initCodeEditor() {
   326 		var codeEditorSettings, editor;
   319 		var codeEditorSettings, editor;
   327 
   320 
   328 		codeEditorSettings = $.extend( {}, component.codeEditor );
   321 		codeEditorSettings = $.extend( {}, component.codeEditor );
   330 		/**
   323 		/**
   331 		 * Handle tabbing to the field before the editor.
   324 		 * Handle tabbing to the field before the editor.
   332 		 *
   325 		 *
   333 		 * @since 4.9.0
   326 		 * @since 4.9.0
   334 		 *
   327 		 *
   335 		 * @returns {void}
   328 		 * @return {void}
   336 		 */
   329 		 */
   337 		codeEditorSettings.onTabPrevious = function() {
   330 		codeEditorSettings.onTabPrevious = function() {
   338 			$( '#templateside' ).find( ':tabbable' ).last().focus();
   331 			$( '#templateside' ).find( ':tabbable' ).last().focus();
   339 		};
   332 		};
   340 
   333 
   341 		/**
   334 		/**
   342 		 * Handle tabbing to the field after the editor.
   335 		 * Handle tabbing to the field after the editor.
   343 		 *
   336 		 *
   344 		 * @since 4.9.0
   337 		 * @since 4.9.0
   345 		 *
   338 		 *
   346 		 * @returns {void}
   339 		 * @return {void}
   347 		 */
   340 		 */
   348 		codeEditorSettings.onTabNext = function() {
   341 		codeEditorSettings.onTabNext = function() {
   349 			$( '#template' ).find( ':tabbable:not(.CodeMirror-code)' ).first().focus();
   342 			$( '#template' ).find( ':tabbable:not(.CodeMirror-code)' ).first().focus();
   350 		};
   343 		};
   351 
   344 
   353 		 * Handle change to the linting errors.
   346 		 * Handle change to the linting errors.
   354 		 *
   347 		 *
   355 		 * @since 4.9.0
   348 		 * @since 4.9.0
   356 		 *
   349 		 *
   357 		 * @param {Array} errors - List of linting errors.
   350 		 * @param {Array} errors - List of linting errors.
   358 		 * @returns {void}
   351 		 * @return {void}
   359 		 */
   352 		 */
   360 		codeEditorSettings.onChangeLintingErrors = function( errors ) {
   353 		codeEditorSettings.onChangeLintingErrors = function( errors ) {
   361 			component.lintErrors = errors;
   354 			component.lintErrors = errors;
   362 
   355 
   363 			// Only disable the button in onUpdateErrorNotice when there are errors so users can still feel they can click the button.
   356 			// Only disable the button in onUpdateErrorNotice when there are errors so users can still feel they can click the button.
   370 		 * Update error notice.
   363 		 * Update error notice.
   371 		 *
   364 		 *
   372 		 * @since 4.9.0
   365 		 * @since 4.9.0
   373 		 *
   366 		 *
   374 		 * @param {Array} errorAnnotations - Error annotations.
   367 		 * @param {Array} errorAnnotations - Error annotations.
   375 		 * @returns {void}
   368 		 * @return {void}
   376 		 */
   369 		 */
   377 		codeEditorSettings.onUpdateErrorNotice = function onUpdateErrorNotice( errorAnnotations ) {
   370 		codeEditorSettings.onUpdateErrorNotice = function onUpdateErrorNotice( errorAnnotations ) {
   378 			var message, noticeElement;
   371 			var noticeElement;
   379 
   372 
   380 			component.submitButton.toggleClass( 'disabled', errorAnnotations.length > 0 );
   373 			component.submitButton.toggleClass( 'disabled', errorAnnotations.length > 0 );
   381 
   374 
   382 			if ( 0 !== errorAnnotations.length ) {
   375 			if ( 0 !== errorAnnotations.length ) {
   383 				if ( 1 === errorAnnotations.length ) {
       
   384 					message = component.l10n.lintError.singular.replace( '%d', '1' );
       
   385 				} else {
       
   386 					message = component.l10n.lintError.plural.replace( '%d', String( errorAnnotations.length ) );
       
   387 				}
       
   388 				noticeElement = component.addNotice({
   376 				noticeElement = component.addNotice({
   389 					code: 'lint_errors',
   377 					code: 'lint_errors',
   390 					type: 'error',
   378 					type: 'error',
   391 					message: message,
   379 					message: sprintf(
       
   380 						/* translators: %s: Error count. */
       
   381 						_n(
       
   382 							'There is %s error which must be fixed before you can update this file.',
       
   383 							'There are %s errors which must be fixed before you can update this file.',
       
   384 							errorAnnotations.length
       
   385 						),
       
   386 						String( errorAnnotations.length )
       
   387 					),
   392 					dismissible: false
   388 					dismissible: false
   393 				});
   389 				});
   394 				noticeElement.find( 'input[type=checkbox]' ).on( 'click', function() {
   390 				noticeElement.find( 'input[type=checkbox]' ).on( 'click', function() {
   395 					codeEditorSettings.onChangeLintingErrors( [] );
   391 					codeEditorSettings.onChangeLintingErrors( [] );
   396 					component.removeNotice( 'lint_errors' );
   392 					component.removeNotice( 'lint_errors' );
   422 
   418 
   423 	/**
   419 	/**
   424 	 * Initialization of the file browser's folder states.
   420 	 * Initialization of the file browser's folder states.
   425 	 *
   421 	 *
   426 	 * @since 4.9.0
   422 	 * @since 4.9.0
   427 	 * @returns {void}
   423 	 * @return {void}
   428 	 */
   424 	 */
   429 	component.initFileBrowser = function initFileBrowser() {
   425 	component.initFileBrowser = function initFileBrowser() {
   430 
   426 
   431 		var $templateside = $( '#templateside' );
   427 		var $templateside = $( '#templateside' );
   432 
   428 
   489 		 *       An element with the role=tree attribute
   485 		 *       An element with the role=tree attribute
   490 		 */
   486 		 */
   491 
   487 
   492 		var TreeitemLink = function (node, treeObj, group) {
   488 		var TreeitemLink = function (node, treeObj, group) {
   493 
   489 
   494 			// Check whether node is a DOM element
   490 			// Check whether node is a DOM element.
   495 			if (typeof node !== 'object') {
   491 			if (typeof node !== 'object') {
   496 				return;
   492 				return;
   497 			}
   493 			}
   498 
   494 
   499 			node.tabIndex = -1;
   495 			node.tabIndex = -1;
   694 			}
   690 			}
   695 		};
   691 		};
   696 
   692 
   697 		TreeitemLink.prototype.handleClick = function (event) {
   693 		TreeitemLink.prototype.handleClick = function (event) {
   698 
   694 
   699 			// only process click events that directly happened on this treeitem
   695 			// Only process click events that directly happened on this treeitem.
   700 			if (event.target !== this.domNode && event.target !== this.domNode.firstElementChild) {
   696 			if (event.target !== this.domNode && event.target !== this.domNode.firstElementChild) {
   701 				return;
   697 				return;
   702 			}
   698 			}
   703 
   699 
   704 			if (this.isExpandable) {
   700 			if (this.isExpandable) {
   771 		 *   @param node
   767 		 *   @param node
   772 		 *       An element with the role=tree attribute
   768 		 *       An element with the role=tree attribute
   773 		 */
   769 		 */
   774 
   770 
   775 		var TreeLinks = function (node) {
   771 		var TreeLinks = function (node) {
   776 			// Check whether node is a DOM element
   772 			// Check whether node is a DOM element.
   777 			if (typeof node !== 'object') {
   773 			if (typeof node !== 'object') {
   778 				return;
   774 				return;
   779 			}
   775 			}
   780 
   776 
   781 			this.domNode = node;
   777 			this.domNode = node;
   810 
   806 
   811 					elem = elem.nextElementSibling;
   807 					elem = elem.nextElementSibling;
   812 				}
   808 				}
   813 			}
   809 			}
   814 
   810 
   815 			// initialize pop up menus
   811 			// Initialize pop up menus.
   816 			if (!this.domNode.getAttribute('role')) {
   812 			if (!this.domNode.getAttribute('role')) {
   817 				this.domNode.setAttribute('role', 'tree');
   813 				this.domNode.setAttribute('role', 'tree');
   818 			}
   814 			}
   819 
   815 
   820 			findTreeitems(this.domNode, this, false);
   816 			findTreeitems(this.domNode, this, false);
   962 
   958 
   963 		TreeLinks.prototype.setFocusByFirstCharacter = function (currentItem, _char) {
   959 		TreeLinks.prototype.setFocusByFirstCharacter = function (currentItem, _char) {
   964 			var start, index;
   960 			var start, index;
   965 			_char = _char.toLowerCase();
   961 			_char = _char.toLowerCase();
   966 
   962 
   967 			// Get start index for search based on position of currentItem
   963 			// Get start index for search based on position of currentItem.
   968 			start = this.treeitems.indexOf(currentItem) + 1;
   964 			start = this.treeitems.indexOf(currentItem) + 1;
   969 			if (start === this.treeitems.length) {
   965 			if (start === this.treeitems.length) {
   970 				start = 0;
   966 				start = 0;
   971 			}
   967 			}
   972 
   968 
   973 			// Check remaining slots in the menu
   969 			// Check remaining slots in the menu.
   974 			index = this.getIndexFirstChars(start, _char);
   970 			index = this.getIndexFirstChars(start, _char);
   975 
   971 
   976 			// If not found in remaining slots, check from beginning
   972 			// If not found in remaining slots, check from beginning.
   977 			if (index === -1) {
   973 			if (index === -1) {
   978 				index = this.getIndexFirstChars(0, _char);
   974 				index = this.getIndexFirstChars(0, _char);
   979 			}
   975 			}
   980 
   976 
   981 			// If match was found...
   977 			// If match was found...
  1002 	/* jscs:enable */
   998 	/* jscs:enable */
  1003 	/* eslint-enable */
   999 	/* eslint-enable */
  1004 
  1000 
  1005 	return component;
  1001 	return component;
  1006 })( jQuery );
  1002 })( jQuery );
       
  1003 
       
  1004 /**
       
  1005  * Removed in 5.5.0, needed for back-compatibility.
       
  1006  *
       
  1007  * @since 4.9.0
       
  1008  * @deprecated 5.5.0
       
  1009  *
       
  1010  * @type {object}
       
  1011  */
       
  1012 wp.themePluginEditor.l10n = wp.themePluginEditor.l10n || {
       
  1013 	saveAlert: '',
       
  1014 	saveError: '',
       
  1015 	lintError: {
       
  1016 		alternative: 'wp.i18n',
       
  1017 		func: function() {
       
  1018 			return {
       
  1019 				singular: '',
       
  1020 				plural: ''
       
  1021 			};
       
  1022 		}
       
  1023 	}
       
  1024 };
       
  1025 
       
  1026 wp.themePluginEditor.l10n = window.wp.deprecateL10nObject( 'wp.themePluginEditor.l10n', wp.themePluginEditor.l10n );