<commit_before>/* exported ampInvalidUrlPostEditScreen */

var ampInvalidUrlPostEditScreen = ( function() { // eslint-disable-line no-unused-vars
	var component;

	component = {
		data: {
			l10n: {
				unsaved_changes: '',
				showing_number_errors: '',
				page_heading: '',
				show_all: ''
			}
		}
	};

	/**
	 * The id for the 'Showing x of y errors' notice.
	 *
	 * @var {string}
	 */
	component.idNumberErrors = 'number-errors';

	/**
	 * The id for the 'Show all' button.
	 *
	 * @var {string}
	 */
	component.showAllId = 'show-all-errors';

	/**
	 * Boot.
	 *
	 * @param {Object} data Data.
	 * @param {Object} data.l10n Translations.
	 */
	component.boot = function boot( data ) {
		Object.assign( component.data, data );
		component.handleShowAll();
		component.handleFiltering();
		component.handleSearching();
		component.handleStatusChange();
		component.handleBulkActions();
		component.changeHeading();
		component.watchForUnsavedChanges();
	};

	/**
	 * Add prompt when leaving page due to unsaved changes.
	 */
	component.addBeforeUnloadPrompt = function addBeforeUnloadPrompt() {
		if ( component.beforeUnloadPromptAdded ) {
			return;
		}
		window.addEventListener( 'beforeunload', component.onBeforeUnload );

		// Remove prompt when clicking trash or update.
		document.querySelector( '#major-publishing-actions' ).addEventListener( 'click', function() {
			window.removeEventListener( 'beforeunload', component.onBeforeUnload );
		} );

		component.beforeUnloadPromptAdded = true;
	};

	/**
	 * Watch for unsaved changes.
	 *
	 * Add an beforeunload warning when attempting to leave the page when there are unsaved changes,
	 * unless the user is pressing the trash link or update button.
	 */
	component.watchForUnsavedChanges = function watchForUnsavedChanges() {
		const onChange = function( event ) {
			if ( event.target.matches( 'select' ) ) {
				document.getElementById( 'post' ).removeEventListener( 'change', onChange );
				component.addBeforeUnloadPrompt();
			}
		};
		document.getElementById( 'post' ).addEventListener( 'change', onChange );
	};

	/**
	 * Show message at beforeunload.
	 *
	 * @param {Event} event - The beforeunload event.
	 * @return {string} Message.
	 */
	component.onBeforeUnload = function onBeforeUnload( event ) {
		event.preventDefault();
		event.returnValue = component.data.l10n.unsaved_changes;
		return component.data.l10n.unsaved_changes;
	};

	/**
	 * Updates the <tr> with 'Showing x of y validation errors' at the top of the list table with the current count.
	 * If this does not exist yet, it creates the element.
	 *
	 * @param {number} numberErrorsDisplaying - The number of errors displaying.
	 * @param {number} totalErrors - The total number of errors, displaying or not.
	 */
	component.updateShowingErrorsRow = function updateShowingErrorsRow( numberErrorsDisplaying, totalErrors ) {
		const showAllButton = document.getElementById( component.showAllId );
		let thead, th,
			tr = document.getElementById( component.idNumberErrors );
		const theadQuery = document.getElementsByTagName( 'thead' );

		// Only create the <tr> if it does not exist yet.
		if ( theadQuery[ 0 ] && ! tr ) {
			thead = theadQuery[ 0 ];
			tr = document.createElement( 'tr' );
			th = document.createElement( 'th' );
			th.setAttribute( 'id', component.idNumberErrors );
			th.setAttribute( 'colspan', '6' );
			tr.appendChild( th );
			thead.appendChild( tr );
		}

		// If all of the errors are displaying, hide the 'Show all' button and the count notice.
		if ( showAllButton && numberErrorsDisplaying === totalErrors ) {
			showAllButton.classList.add( 'hidden' );
			tr.classList.add( 'hidden' );
		} else if ( null !== numberErrorsDisplaying ) {
			// Update the number of errors displaying and create a 'Show all' button if it does not exist yet.
			document.getElementById( component.idNumberErrors ).innerText = component.data.l10n.showing_number_errors.replace( '%', numberErrorsDisplaying );
			document.getElementById( component.idNumberErrors ).classList.remove( 'hidden' );
			component.conditionallyCreateShowAllButton();
			if ( showAllButton ) {
				showAllButton.classList.remove( 'hidden' );
			}
		}
	};

	/**
	 * Conditionally creates and appends a 'Show all' button.
	 */
	component.conditionallyCreateShowAllButton = function conditionallyCreateShowAllButton() {
		const buttonContainer = document.getElementById( 'url-post-filter' );
		let showAllButton = document.getElementById( component.showAllId );

		// There is no 'Show all' <button> yet, but there is a container element for it, create the <button>
		if ( ! showAllButton && buttonContainer ) {
			showAllButton = document.createElement( 'button' );
			showAllButton.id = component.showAllId;
			showAllButton.classList.add( 'button' );
			showAllButton.innerText = component.data.l10n.show_all;
			buttonContainer.appendChild( showAllButton );
		}
	};

	/**
	 * On clicking the 'Show all' <button>, this displays all of the validation errors.
	 * Then, it hides this 'Show all' <button> and the notice for the number of errors showing.
	 */
	component.handleShowAll = function handleShowAll() {
		const onClick = function( event ) {
			const validationErrors = document.querySelectorAll( '[data-error-type]' );
			if ( ! event.target.matches( '#' + component.showAllId ) ) {
				return;
			}
			event.preventDefault();

			// Iterate through all of the errors, and remove the 'hidden' class.
			validationErrors.forEach( function( element ) {
				element.parentElement.parentElement.classList.remove( 'hidden' );
			} );

			/*
			 * Update the notice to indicate that all of the errors are displaying.
			 * Like 'Showing 5 of 5 validation errors'.
 			 */
			component.updateShowingErrorsRow( validationErrors.length, validationErrors.length );

			// Hide this 'Show all' button.
			event.target.classList.add( 'hidden' );

			// Change the value of the error type <select> element to 'All Error Types'.
			document.getElementById( 'amp_validation_error_type' ).value = '-1';
		};

		document.getElementById( 'url-post-filter' ).addEventListener( 'click', onClick );
	};

	/**
	 * Handles filtering by error type, triggered by clicking 'Apply Filter'.
	 *
	 * Gets the value of the error type <select> element.
	 * And hides all <tr> elements that do not have the same type of this value.
	 * If 'All Error Types' is selected, this displays all errors.
	 */
	component.handleFiltering = function handleFiltering() {
		const onChange = function( event ) {
			const showAllButton = document.getElementById( component.showAllId );
			if ( ! event.target.matches( 'select' ) ) {
				return;
			}

			event.preventDefault();

			const isAllErrorTypesSelected = ( '-1' === event.target.value );
			const errorTypeQuery = document.querySelectorAll( '[data-error-type]' );

			// If the user has chosen 'All Error Types' from the <select>, hide the 'Show all' button.
			if ( isAllErrorTypesSelected && showAllButton ) {
				showAllButton.classList.add( 'hidden' );
			}

			/*
			 * Iterate through all of the <tr> elements in the list table.
			 * If the error type does not match the value (selected error type), hide them.
			 */
			let numberErrorsDisplaying = 0;
			errorTypeQuery.forEach( function( element ) {
				const errorType = element.getAttribute( 'data-error-type' );

				// If 'All Error Types' was selected, this should display all errors.
				if ( isAllErrorTypesSelected || ! event.target.value || event.target.value === errorType ) {
					element.parentElement.parentElement.classList.remove( 'hidden' );
					numberErrorsDisplaying++;
				} else {
					element.parentElement.parentElement.classList.add( 'hidden' );
				}
			} );

			component.updateShowingErrorsRow( numberErrorsDisplaying, errorTypeQuery.length );
		};

		document.getElementById( 'amp_validation_error_type' ).addEventListener( 'change', onChange );
	};

	/**
	 * Handles searching for errors via the <input> and the 'Search Errors' <button>.
	 */
	component.handleSearching = function handleSearching() {
		const onClick = function( event ) {
			event.preventDefault();
			if ( ! event.target.matches( 'input' ) ) {
				return;
			}

			const searchQuery = document.getElementById( 'invalid-url-search-search-input' ).value;
			const detailsQuery = document.querySelectorAll( 'tbody .column-details' );

			/*
			 * Iterate through the 'Details' column of each row.
			 * If the search query is not present, hide the row.
			 */
			let numberErrorsDisplaying = 0;
			detailsQuery.forEach( function( element ) {
				let isSearchQueryPresent = false;

				element.querySelectorAll( '.detailed' ).forEach( function( detailed ) {
					if ( -1 !== detailed.innerText.indexOf( searchQuery ) ) {
						isSearchQueryPresent = true;
					}
				} );

				if ( isSearchQueryPresent ) {
					element.parentElement.classList.remove( 'hidden' );
					numberErrorsDisplaying++;
				} else {
					element.parentElement.classList.add( 'hidden' );
				}
			} );

			component.updateShowingErrorsRow( numberErrorsDisplaying, detailsQuery.length );
		};

		document.getElementById( 'search-submit' ).addEventListener( 'click', onClick );
	};

	/**
	 * Update icon for select element.
	 *
	 * @param {HTMLSelectElement} select Select element.
	 */
	component.updateSelectIcon = function updateSelectIcon( select ) {
		const newOption = select.options[ select.selectedIndex ];
		if ( newOption ) {
			const iconSrc = newOption.getAttribute( 'data-status-icon' );
			select.parentNode.querySelector( 'img' ).setAttribute( 'src', iconSrc );
		}
	};

	/**
	 * Handles a change in the error status, like from 'New' to 'Accepted'.
	 *
	 * Gets the data-status-icon value from the newly-selected <option>.
	 * And sets this as the src of the status icon <img>.
	 */
	component.handleStatusChange = function handleStatusChange() {
		const onChange = function( event ) {
			if ( event.target.matches( 'select' ) ) {
				component.updateSelectIcon( event.target );
			}
		};

		document.querySelectorAll( '.amp-validation-error-status' ).forEach( function( element ) {
			element.addEventListener( 'change', onChange );
		} );
	};

	/**
	 * On checking a bulk action checkbox, this ensures that the 'Accept' and 'Reject' buttons are present. Handle clicking on buttons.
	 *
	 * They're hidden until one of these boxes is checked.
	 * Also, on unchecking the last checked box, this hides these buttons.
	 */
	component.handleBulkActions = function handleBulkActions() {
		const acceptButton = document.querySelector( 'button.action.accept' );
		const rejectButton = document.querySelector( 'button.action.reject' );

		const onChange = function( event ) {
			let areThereCheckedBoxes;

			if ( ! event.target.matches( '[type=checkbox]' ) ) {
				return;
			}

			if ( event.target.checked ) {
				// This checkbox was checked, so ensure the buttons display.
				acceptButton.classList.remove( 'hidden' );
				rejectButton.classList.remove( 'hidden' );
			} else {
				/*
				 * This checkbox was unchecked.
				 * So find if there are any other checkboxes that are checked.
				 * If not, hide the 'Accept' and 'Reject' buttons.
				 */
				areThereCheckedBoxes = false;
				document.querySelectorAll( '.check-column [type=checkbox]' ).forEach( function( element ) {
					if ( element.checked ) {
						areThereCheckedBoxes = true;
					}
				} );
				if ( ! areThereCheckedBoxes ) {
					acceptButton.classList.add( 'hidden' );
					rejectButton.classList.add( 'hidden' );
				}
			}
		};

		document.querySelectorAll( '.check-column [type=checkbox]' ).forEach( function( element ) {
			element.addEventListener( 'change', onChange );
		} );

		// Handle click on accept button.
		acceptButton.addEventListener( 'click', function() {
			Array.prototype.forEach.call( document.querySelectorAll( 'select.amp-validation-error-status' ), function( select ) {
				if ( select.closest( 'tr' ).querySelector( '.check-column input[type=checkbox]' ).checked ) {
					select.value = 1;
					component.updateSelectIcon( select );
					component.addBeforeUnloadPrompt();
				}
			} );
		} );

		// Handle click on reject button.
		rejectButton.addEventListener( 'click', function() {
			Array.prototype.forEach.call( document.querySelectorAll( 'select.amp-validation-error-status' ), function( select ) {
				if ( select.closest( 'tr' ).querySelector( '.check-column input[type=checkbox]' ).checked ) {
					select.value = 2; // @todo Update to 3 when merging with <https://github.com/Automattic/amp-wp/pull/1429>.
					component.updateSelectIcon( select );
					component.addBeforeUnloadPrompt();
				}
			} );
		} );
	};

	/**
	 * Changes the page heading, as this doesn't look to be possible with a PHP filter.
	 */
	component.changeHeading = function changeHeading() {
		const headingQuery = document.getElementsByClassName( 'wp-heading-inline' );
		if ( headingQuery[ 0 ] && component.data.l10n.page_heading ) {
			headingQuery[ 0 ].innerText = component.data.l10n.page_heading;
		}
	};

	return component;
}() );
<commit_msg>Because the show all button might be newly created, do a new query for it<commit_after>/* exported ampInvalidUrlPostEditScreen */

var ampInvalidUrlPostEditScreen = ( function() { // eslint-disable-line no-unused-vars
	var component;

	component = {
		data: {
			l10n: {
				unsaved_changes: '',
				showing_number_errors: '',
				page_heading: '',
				show_all: ''
			}
		}
	};

	/**
	 * The id for the 'Showing x of y errors' notice.
	 *
	 * @var {string}
	 */
	component.idNumberErrors = 'number-errors';

	/**
	 * The id for the 'Show all' button.
	 *
	 * @var {string}
	 */
	component.showAllId = 'show-all-errors';

	/**
	 * Boot.
	 *
	 * @param {Object} data Data.
	 * @param {Object} data.l10n Translations.
	 */
	component.boot = function boot( data ) {
		Object.assign( component.data, data );
		component.handleShowAll();
		component.handleFiltering();
		component.handleSearching();
		component.handleStatusChange();
		component.handleBulkActions();
		component.changeHeading();
		component.watchForUnsavedChanges();
	};

	/**
	 * Add prompt when leaving page due to unsaved changes.
	 */
	component.addBeforeUnloadPrompt = function addBeforeUnloadPrompt() {
		if ( component.beforeUnloadPromptAdded ) {
			return;
		}
		window.addEventListener( 'beforeunload', component.onBeforeUnload );

		// Remove prompt when clicking trash or update.
		document.querySelector( '#major-publishing-actions' ).addEventListener( 'click', function() {
			window.removeEventListener( 'beforeunload', component.onBeforeUnload );
		} );

		component.beforeUnloadPromptAdded = true;
	};

	/**
	 * Watch for unsaved changes.
	 *
	 * Add an beforeunload warning when attempting to leave the page when there are unsaved changes,
	 * unless the user is pressing the trash link or update button.
	 */
	component.watchForUnsavedChanges = function watchForUnsavedChanges() {
		const onChange = function( event ) {
			if ( event.target.matches( 'select' ) ) {
				document.getElementById( 'post' ).removeEventListener( 'change', onChange );
				component.addBeforeUnloadPrompt();
			}
		};
		document.getElementById( 'post' ).addEventListener( 'change', onChange );
	};

	/**
	 * Show message at beforeunload.
	 *
	 * @param {Event} event - The beforeunload event.
	 * @return {string} Message.
	 */
	component.onBeforeUnload = function onBeforeUnload( event ) {
		event.preventDefault();
		event.returnValue = component.data.l10n.unsaved_changes;
		return component.data.l10n.unsaved_changes;
	};

	/**
	 * Updates the <tr> with 'Showing x of y validation errors' at the top of the list table with the current count.
	 * If this does not exist yet, it creates the element.
	 *
	 * @param {number} numberErrorsDisplaying - The number of errors displaying.
	 * @param {number} totalErrors - The total number of errors, displaying or not.
	 */
	component.updateShowingErrorsRow = function updateShowingErrorsRow( numberErrorsDisplaying, totalErrors ) {
		const showAllButton = document.getElementById( component.showAllId );
		let thead, th,
			tr = document.getElementById( component.idNumberErrors );
		const theadQuery = document.getElementsByTagName( 'thead' );

		// Only create the <tr> if it does not exist yet.
		if ( theadQuery[ 0 ] && ! tr ) {
			thead = theadQuery[ 0 ];
			tr = document.createElement( 'tr' );
			th = document.createElement( 'th' );
			th.setAttribute( 'id', component.idNumberErrors );
			th.setAttribute( 'colspan', '6' );
			tr.appendChild( th );
			thead.appendChild( tr );
		}

		// If all of the errors are displaying, hide the 'Show all' button and the count notice.
		if ( showAllButton && numberErrorsDisplaying === totalErrors ) {
			showAllButton.classList.add( 'hidden' );
			tr.classList.add( 'hidden' );
		} else if ( null !== numberErrorsDisplaying ) {
			// Update the number of errors displaying and create a 'Show all' button if it does not exist yet.
			document.getElementById( component.idNumberErrors ).innerText = component.data.l10n.showing_number_errors.replace( '%', numberErrorsDisplaying );
			document.getElementById( component.idNumberErrors ).classList.remove( 'hidden' );
			component.conditionallyCreateShowAllButton();
			if ( document.getElementById( component.showAllId ) ) {
				document.getElementById( component.showAllId ).classList.remove( 'hidden' );
			}
		}
	};

	/**
	 * Conditionally creates and appends a 'Show all' button.
	 */
	component.conditionallyCreateShowAllButton = function conditionallyCreateShowAllButton() {
		const buttonContainer = document.getElementById( 'url-post-filter' );
		let showAllButton = document.getElementById( component.showAllId );

		// There is no 'Show all' <button> yet, but there is a container element for it, create the <button>
		if ( ! showAllButton && buttonContainer ) {
			showAllButton = document.createElement( 'button' );
			showAllButton.id = component.showAllId;
			showAllButton.classList.add( 'button' );
			showAllButton.innerText = component.data.l10n.show_all;
			buttonContainer.appendChild( showAllButton );
		}
	};

	/**
	 * On clicking the 'Show all' <button>, this displays all of the validation errors.
	 * Then, it hides this 'Show all' <button> and the notice for the number of errors showing.
	 */
	component.handleShowAll = function handleShowAll() {
		const onClick = function( event ) {
			const validationErrors = document.querySelectorAll( '[data-error-type]' );
			if ( ! event.target.matches( '#' + component.showAllId ) ) {
				return;
			}
			event.preventDefault();

			// Iterate through all of the errors, and remove the 'hidden' class.
			validationErrors.forEach( function( element ) {
				element.parentElement.parentElement.classList.remove( 'hidden' );
			} );

			/*
			 * Update the notice to indicate that all of the errors are displaying.
			 * Like 'Showing 5 of 5 validation errors'.
 			 */
			component.updateShowingErrorsRow( validationErrors.length, validationErrors.length );

			// Hide this 'Show all' button.
			event.target.classList.add( 'hidden' );

			// Change the value of the error type <select> element to 'All Error Types'.
			document.getElementById( 'amp_validation_error_type' ).value = '-1';
		};

		document.getElementById( 'url-post-filter' ).addEventListener( 'click', onClick );
	};

	/**
	 * Handles filtering by error type, triggered by clicking 'Apply Filter'.
	 *
	 * Gets the value of the error type <select> element.
	 * And hides all <tr> elements that do not have the same type of this value.
	 * If 'All Error Types' is selected, this displays all errors.
	 */
	component.handleFiltering = function handleFiltering() {
		const onChange = function( event ) {
			const showAllButton = document.getElementById( component.showAllId );
			if ( ! event.target.matches( 'select' ) ) {
				return;
			}

			event.preventDefault();

			const isAllErrorTypesSelected = ( '-1' === event.target.value );
			const errorTypeQuery = document.querySelectorAll( '[data-error-type]' );

			// If the user has chosen 'All Error Types' from the <select>, hide the 'Show all' button.
			if ( isAllErrorTypesSelected && showAllButton ) {
				showAllButton.classList.add( 'hidden' );
			}

			/*
			 * Iterate through all of the <tr> elements in the list table.
			 * If the error type does not match the value (selected error type), hide them.
			 */
			let numberErrorsDisplaying = 0;
			errorTypeQuery.forEach( function( element ) {
				const errorType = element.getAttribute( 'data-error-type' );

				// If 'All Error Types' was selected, this should display all errors.
				if ( isAllErrorTypesSelected || ! event.target.value || event.target.value === errorType ) {
					element.parentElement.parentElement.classList.remove( 'hidden' );
					numberErrorsDisplaying++;
				} else {
					element.parentElement.parentElement.classList.add( 'hidden' );
				}
			} );

			component.updateShowingErrorsRow( numberErrorsDisplaying, errorTypeQuery.length );
		};

		document.getElementById( 'amp_validation_error_type' ).addEventListener( 'change', onChange );
	};

	/**
	 * Handles searching for errors via the <input> and the 'Search Errors' <button>.
	 */
	component.handleSearching = function handleSearching() {
		const onClick = function( event ) {
			event.preventDefault();
			if ( ! event.target.matches( 'input' ) ) {
				return;
			}

			const searchQuery = document.getElementById( 'invalid-url-search-search-input' ).value;
			const detailsQuery = document.querySelectorAll( 'tbody .column-details' );

			/*
			 * Iterate through the 'Details' column of each row.
			 * If the search query is not present, hide the row.
			 */
			let numberErrorsDisplaying = 0;
			detailsQuery.forEach( function( element ) {
				let isSearchQueryPresent = false;

				element.querySelectorAll( '.detailed' ).forEach( function( detailed ) {
					if ( -1 !== detailed.innerText.indexOf( searchQuery ) ) {
						isSearchQueryPresent = true;
					}
				} );

				if ( isSearchQueryPresent ) {
					element.parentElement.classList.remove( 'hidden' );
					numberErrorsDisplaying++;
				} else {
					element.parentElement.classList.add( 'hidden' );
				}
			} );

			component.updateShowingErrorsRow( numberErrorsDisplaying, detailsQuery.length );
		};

		document.getElementById( 'search-submit' ).addEventListener( 'click', onClick );
	};

	/**
	 * Update icon for select element.
	 *
	 * @param {HTMLSelectElement} select Select element.
	 */
	component.updateSelectIcon = function updateSelectIcon( select ) {
		const newOption = select.options[ select.selectedIndex ];
		if ( newOption ) {
			const iconSrc = newOption.getAttribute( 'data-status-icon' );
			select.parentNode.querySelector( 'img' ).setAttribute( 'src', iconSrc );
		}
	};

	/**
	 * Handles a change in the error status, like from 'New' to 'Accepted'.
	 *
	 * Gets the data-status-icon value from the newly-selected <option>.
	 * And sets this as the src of the status icon <img>.
	 */
	component.handleStatusChange = function handleStatusChange() {
		const onChange = function( event ) {
			if ( event.target.matches( 'select' ) ) {
				component.updateSelectIcon( event.target );
			}
		};

		document.querySelectorAll( '.amp-validation-error-status' ).forEach( function( element ) {
			element.addEventListener( 'change', onChange );
		} );
	};

	/**
	 * On checking a bulk action checkbox, this ensures that the 'Accept' and 'Reject' buttons are present. Handle clicking on buttons.
	 *
	 * They're hidden until one of these boxes is checked.
	 * Also, on unchecking the last checked box, this hides these buttons.
	 */
	component.handleBulkActions = function handleBulkActions() {
		const acceptButton = document.querySelector( 'button.action.accept' );
		const rejectButton = document.querySelector( 'button.action.reject' );

		const onChange = function( event ) {
			let areThereCheckedBoxes;

			if ( ! event.target.matches( '[type=checkbox]' ) ) {
				return;
			}

			if ( event.target.checked ) {
				// This checkbox was checked, so ensure the buttons display.
				acceptButton.classList.remove( 'hidden' );
				rejectButton.classList.remove( 'hidden' );
			} else {
				/*
				 * This checkbox was unchecked.
				 * So find if there are any other checkboxes that are checked.
				 * If not, hide the 'Accept' and 'Reject' buttons.
				 */
				areThereCheckedBoxes = false;
				document.querySelectorAll( '.check-column [type=checkbox]' ).forEach( function( element ) {
					if ( element.checked ) {
						areThereCheckedBoxes = true;
					}
				} );
				if ( ! areThereCheckedBoxes ) {
					acceptButton.classList.add( 'hidden' );
					rejectButton.classList.add( 'hidden' );
				}
			}
		};

		document.querySelectorAll( '.check-column [type=checkbox]' ).forEach( function( element ) {
			element.addEventListener( 'change', onChange );
		} );

		// Handle click on accept button.
		acceptButton.addEventListener( 'click', function() {
			Array.prototype.forEach.call( document.querySelectorAll( 'select.amp-validation-error-status' ), function( select ) {
				if ( select.closest( 'tr' ).querySelector( '.check-column input[type=checkbox]' ).checked ) {
					select.value = 1;
					component.updateSelectIcon( select );
					component.addBeforeUnloadPrompt();
				}
			} );
		} );

		// Handle click on reject button.
		rejectButton.addEventListener( 'click', function() {
			Array.prototype.forEach.call( document.querySelectorAll( 'select.amp-validation-error-status' ), function( select ) {
				if ( select.closest( 'tr' ).querySelector( '.check-column input[type=checkbox]' ).checked ) {
					select.value = 2; // @todo Update to 3 when merging with <https://github.com/Automattic/amp-wp/pull/1429>.
					component.updateSelectIcon( select );
					component.addBeforeUnloadPrompt();
				}
			} );
		} );
	};

	/**
	 * Changes the page heading, as this doesn't look to be possible with a PHP filter.
	 */
	component.changeHeading = function changeHeading() {
		const headingQuery = document.getElementsByClassName( 'wp-heading-inline' );
		if ( headingQuery[ 0 ] && component.data.l10n.page_heading ) {
			headingQuery[ 0 ].innerText = component.data.l10n.page_heading;
		}
	};

	return component;
}() );
<|endoftext|>


<commit_before>var imageManagerInput = {
	baseUrl: null,
	//language
	message: null,
	//init imageManagerInput
	init: function(){
		//create modal
		imageManagerInput.initModal();
	},
	//creat image Manager modal
	initModal: function(){
		//check if modal not jet exists
		if($("#modal-imagemanager").length === 0){
			//set html modal in var
			var sModalHtml = '
';
				sModalHtml += '
';
					sModalHtml += '
';
						sModalHtml += '
';
							sModalHtml += '<button aria-hidden="true" data-dismiss="modal" class="close" type="button">Ã—</button>';
							sModalHtml += '
Image manager
';
						sModalHtml += '
';
						sModalHtml += '
';
							sModalHtml += '';
						sModalHtml += '
';
					sModalHtml += '
';
				sModalHtml += '
';
			sModalHtml += '
';
			//prepend data to body
			$('body').prepend(sModalHtml);
		}
	},
	//open media manager modal
	openModal: function(inputId, aspectRatio, cropViewMode){
		//get selected item
		var iImageId = $("#"+inputId).val();
		var srcImageIdQueryString = "";
		if(iImageId !== ""){
			srcImageIdQueryString = "&image-id="+iImageId;
		}
		//create iframe url
		var queryStringStartCharacter = ((imageManagerInput.baseUrl).indexOf('?') == -1) ? '?' : '&';
		var imageManagerUrl = imageManagerInput.baseUrl+queryStringStartCharacter+"view-mode=iframe&input-id="+inputId+"&aspect-ratio="+aspectRatio+"&crop-view-mode="+cropViewMode+srcImageIdQueryString;
		//set iframe path
		$("#modal-imagemanager iframe").attr("src",imageManagerUrl);
		//open modal
		$("#modal-imagemanager").modal("show");
	},
	//close media manager modal
	closeModal: function(){
		$("#modal-imagemanager").modal("hide");
	},
	//delete picked image
	deletePickedImage: function(inputId){
		//remove value of the input field
		var sFieldId = inputId;
		var sFieldNameId = sFieldId+"_name";
		var sImagePreviewId = sFieldId+"_image";
		var bShowConfirm = JSON.parse($(".delete-selected-image[data-input-id='"+inputId+"']").data("show-delete-confirm"));
		//show warning if bShowConfirm == true
		if(bShowConfirm){
			if(confirm(imageManagerInput.message.detachWarningMessage) == false){
				return false;
			}
		}
		//set input data
		$('#'+sFieldId).val("");
		$('#'+sFieldNameId).val("");
		//trigger change
		$('#'+sFieldId).trigger("change");
		//hide image
		$('#'+sImagePreviewId).attr("src","").parent().addClass("hide");
		//delete hide class
		$(".delete-selected-image[data-input-id='"+inputId+"']").addClass("hide");
	}
};

$(document).ready(function () {
	//init Image manage
	imageManagerInput.init();

	//open media manager modal
	$(document).on("click", ".open-modal-imagemanager", function () {
		var aspectRatio = $(this).data("aspect-ratio");
		var cropViewMode = $(this).data("crop-view-mode");
		var inputId = $(this).data("input-id");
		//open selector id
		imageManagerInput.openModal(inputId, aspectRatio, cropViewMode);
	});

	//delete picked image
	$(document).on("click", ".delete-selected-image", function () {
		var inputId = $(this).data("input-id");
		//open selector id
		imageManagerInput.deletePickedImage(inputId);
	});
});<commit_msg>fix: add ×<commit_after>var imageManagerInput = {
	baseUrl: null,
	//language
	message: null,
	//init imageManagerInput
	init: function(){
		//create modal
		imageManagerInput.initModal();
	},
	//creat image Manager modal
	initModal: function(){
		//check if modal not jet exists
		if($("#modal-imagemanager").length === 0){
			//set html modal in var
			var sModalHtml = '
';
				sModalHtml += '
';
					sModalHtml += '
';
						sModalHtml += '
';
							sModalHtml += '<button aria-hidden="true" data-dismiss="modal" class="close" type="button">×</button>';
							sModalHtml += '
Image manager
';
						sModalHtml += '
';
						sModalHtml += '
';
							sModalHtml += '';
						sModalHtml += '
';
					sModalHtml += '
';
				sModalHtml += '
';
			sModalHtml += '
';
			//prepend data to body
			$('body').prepend(sModalHtml);
		}
	},
	//open media manager modal
	openModal: function(inputId, aspectRatio, cropViewMode){
		//get selected item
		var iImageId = $("#"+inputId).val();
		var srcImageIdQueryString = "";
		if(iImageId !== ""){
			srcImageIdQueryString = "&image-id="+iImageId;
		}
		//create iframe url
		var queryStringStartCharacter = ((imageManagerInput.baseUrl).indexOf('?') == -1) ? '?' : '&';
		var imageManagerUrl = imageManagerInput.baseUrl+queryStringStartCharacter+"view-mode=iframe&input-id="+inputId+"&aspect-ratio="+aspectRatio+"&crop-view-mode="+cropViewMode+srcImageIdQueryString;
		//set iframe path
		$("#modal-imagemanager iframe").attr("src",imageManagerUrl);
		//open modal
		$("#modal-imagemanager").modal("show");
	},
	//close media manager modal
	closeModal: function(){
		$("#modal-imagemanager").modal("hide");
	},
	//delete picked image
	deletePickedImage: function(inputId){
		//remove value of the input field
		var sFieldId = inputId;
		var sFieldNameId = sFieldId+"_name";
		var sImagePreviewId = sFieldId+"_image";
		var bShowConfirm = JSON.parse($(".delete-selected-image[data-input-id='"+inputId+"']").data("show-delete-confirm"));
		//show warning if bShowConfirm == true
		if(bShowConfirm){
			if(confirm(imageManagerInput.message.detachWarningMessage) == false){
				return false;
			}
		}
		//set input data
		$('#'+sFieldId).val("");
		$('#'+sFieldNameId).val("");
		//trigger change
		$('#'+sFieldId).trigger("change");
		//hide image
		$('#'+sImagePreviewId).attr("src","").parent().addClass("hide");
		//delete hide class
		$(".delete-selected-image[data-input-id='"+inputId+"']").addClass("hide");
	}
};

$(document).ready(function () {
	//init Image manage
	imageManagerInput.init();

	//open media manager modal
	$(document).on("click", ".open-modal-imagemanager", function () {
		var aspectRatio = $(this).data("aspect-ratio");
		var cropViewMode = $(this).data("crop-view-mode");
		var inputId = $(this).data("input-id");
		//open selector id
		imageManagerInput.openModal(inputId, aspectRatio, cropViewMode);
	});

	//delete picked image
	$(document).on("click", ".delete-selected-image", function () {
		var inputId = $(this).data("input-id");
		//open selector id
		imageManagerInput.deletePickedImage(inputId);
	});
});<|endoftext|>


<commit_before>define([
    'uuid',
    'common/appUtils',
    'common/utils',
    'common/runtime',
    'common/busEventManager',
    'base/js/namespace'
], (
    Uuid,
    AppUtils,
    Utils,
    Runtime,
    BusEventManager,
    Jupyter
) => {
    'use strict';
    const CELL_TYPE = 'app-bulk-import';

    /**
     * This class creates and manages the bulk import cell. This works with, and wraps around,
     * the Jupyter Cell object.
     */
    class BulkImportCell {
        /**
         * The constructor does the work of initializing the bulk import cell module. It
         * modifies the cell metadata, if the initialize parameter is truthy, and extends the
         * cell object to do things we need it to, like respond to minimization requests and
         * render its icon. This modification also includes the contents of typesToFiles if
         * present.
         *
         * If initialize is falsy, no changes are made to the structure, even if typesToFiles is
         * present and contains new data.
         * @param {Cell} cell a Jupyter notebook cell. This should be a code cell, and will throw
         * an Error if it is not.
         * @param {boolean} initialize if true, this will initialize the bulk import cell
         * structure that gets serialized in the cell metadata. This should ONLY be set true when
         * creating a new bulk import cell, not loading a narrative that already contains one
         * @param {object} typesToFiles keys = data type strings, values = arrays of file paths
         * to import
         * e.g.: {
         *     'fastq_reads': ['file1.fq', 'file2.fq']
         * }
         */
        constructor(cell, initialize, typesToFiles) {
            if (cell.cell_type !== 'code') {
                throw new Error('Can only create Bulk Import Cells out of code cells!');
            }
            this.cell = cell;
            // this is the DOM element used as the container for everything controlled by this cell.
            this.kbaseNode = null;
            this.runtime = Runtime.make();
            this.cellBus = null;
            this.busEventManager = BusEventManager.make({
                bus: this.runtime.bus()
            });
            if (initialize) {
                this.initialize(typesToFiles);
            }
            this.setupCell();
        }

        /**
         * Returns true if the given cell should be treated as a bulk import cell
         * @param {Cell} cell - a Jupyter Notebook cell
         */
        static isBulkImportCell(cell) {
            if (cell.cell_type !== 'code' || !cell.metadata.kbase) {
                return false;
            }
            return cell.metadata.kbase.type === CELL_TYPE;
        }

        /**
         * Does the initial pass on newly created cells to initialize its metadata and get it
         * set up for a new life as a Bulk Import Cell.
         *
         * @param {object} typesToFiles keys = data type strings, values = arrays of file paths
         * to import
         * e.g.: {
         *     'fastq_reads': ['file1.fq', 'file2.fq']
         * }
         */
        initialize(typesToFiles) {
            const meta = {
                kbase: {
                    attributes: {
                        id: new Uuid(4).format(),
                        status: 'new',
                        created: (new Date()).toUTCString(),
                        title: 'Import from Staging Area',
                        subtitle: 'Import files into your Narrative as data objects'
                    },
                    type: CELL_TYPE,
                    bulkImportCell: {
                        'user-settings': {
                            showCodeInputArea: false
                        },
                        inputs: typesToFiles
                    }
                }
            };
            this.cell.metadata = meta;
        }

        /**
         * This specializes this BulkImportCell's existing Jupyter Cell object to have several
         * extra functions that the Narrative can call.
         */
        specializeCell() {
            // returns a DOM node with an icon to be rendered elsewhere
            this.cell.getIcon = function() {
                return AppUtils.makeGenericIcon('upload', '#bf6c97');
            };

            // this renders the cell's icon in its toolbar
            this.cell.renderIcon = function() {
                const iconNode = this.element[0].querySelector('.celltoolbar [data-element="icon"]');
                if (iconNode) {
                    iconNode.innerHTML = this.getIcon();
                }
            };
        }

        /**
         * Initializes the base BulkImportCell's message bus and binds responses to external messages.
         */
        setupMessageBus() {
            this.cellBus = this.runtime.bus().makeChannelBus({
                name: {
                    cell: Utils.getMeta(this.cell, 'attributes', 'id')
                },
                description: 'parent bus for BulkImportCell'
            });
            this.busEventManager.add(this.cellBus.on('delete-cell', () => this.deleteCell()));
        }

        /**
         * Initializes the DOM node (kbaseNode) for rendering.
         */
        setupDomNode() {
            this.kbaseNode = document.createElement('div');
            // inserting after, with raw dom, means telling the parent node
            // to insert a node before the node following the one we are
            // referencing. If there is no next sibling, the null value
            // causes insertBefore to actually ... insert at the end!
            this.cell.input[0].parentNode.insertBefore(this.kbaseNode, this.cell.input[0].nextSibling);
            this.kbaseNode.innerHTML = 'I am a bulk import cell!';
        }

        /**
         * This sets up the bulk import components.
         * This should only be called after a Bulk Import cell is initialized structurally -
         * i.e. if a new one is created, or if a page is loaded that already has one.
         */
        setupCell() {
            if (!BulkImportCell.isBulkImportCell(this.cell)) {
                throw new Error('Can only set up real bulk import cells');
            }

            // set up various cell function extensions
            this.specializeCell();

            // set up the message bus and bind various commands
            this.setupMessageBus();

            this.setupDomNode();

            // finalize by updating the lastLoaded attribute, which triggers a toolbar re-render
            let meta = this.cell.metadata;
            meta.kbase.attributes.lastLoaded = new Date().toUTCString();
            this.cell.metadata = meta;

            this.render();
        }

        /**
         * Deletes the cell from the notebook after doing internal cleanup.
         */
        deleteCell() {
            this.busEventManager.removeAll();
            const cellIndex = Jupyter.notebook.find_cell_index(this.cell);
            Jupyter.notebook.delete_cell(cellIndex);
        }

        /**
         * Renders the view.
         */
        render() {
            this.kbaseNode.innerHTML = 'I am a bulk import cell!';
        }

    }

    return BulkImportCell;
});
<commit_msg>first steps toward tabs and action button<commit_after>define([
    'uuid',
    'common/appUtils',
    'common/utils',
    'common/runtime',
    'common/busEventManager',
    'common/ui',
    'common/events',
    'base/js/namespace',
    'kb_common/html',
], (
    Uuid,
    AppUtils,
    Utils,
    Runtime,
    BusEventManager,
    UI,
    Events,
    Jupyter,
    html
) => {
    'use strict';
    const CELL_TYPE = 'app-bulk-import';

    const tag = html.tag,
        div = tag('div'),
        span = tag('span'),
        a = tag('a');

    /**
     * This class creates and manages the bulk import cell. This works with, and wraps around,
     * the Jupyter Cell object.
     */
    class BulkImportCell {
        /**
         * The constructor does the work of initializing the bulk import cell module. It
         * modifies the cell metadata, if the initialize parameter is truthy, and extends the
         * cell object to do things we need it to, like respond to minimization requests and
         * render its icon. This modification also includes the contents of typesToFiles if
         * present.
         *
         * If initialize is falsy, no changes are made to the structure, even if typesToFiles is
         * present and contains new data.
         * @param {Cell} cell a Jupyter notebook cell. This should be a code cell, and will throw
         * an Error if it is not.
         * @param {boolean} initialize if true, this will initialize the bulk import cell
         * structure that gets serialized in the cell metadata. This should ONLY be set true when
         * creating a new bulk import cell, not loading a narrative that already contains one
         * @param {object} typesToFiles keys = data type strings, values = arrays of file paths
         * to import
         * e.g.: {
         *     'fastq_reads': ['file1.fq', 'file2.fq']
         * }
         */
        constructor(cell, initialize, typesToFiles) {
            if (cell.cell_type !== 'code') {
                throw new Error('Can only create Bulk Import Cells out of code cells!');
            }
            this.cell = cell;
            // this is the DOM element used as the container for everything controlled by this cell.
            this.kbaseNode = null;
            this.runtime = Runtime.make();
            this.cellBus = null;
            this.busEventManager = BusEventManager.make({
                bus: this.runtime.bus()
            });
            this.ui = null;
            this.actionButtons = {
                current: {
                    name: null,
                    disabled: null
                },
                availableButtons: {
                    runApp: {
                        help: 'Run the app',
                        type: 'success',
                        classes: ['-run'],
                        label: 'Run'
                    },
                    cancel: {
                        help: 'Cancel the running app',
                        type: 'danger',
                        classes: ['-cancel'],
                        label: 'Cancel'
                    },
                    reRunApp: {
                        help: 'Edit and re-run the app',
                        type: 'default',
                        classes: ['-rerun'],
                        label: 'Reset'
                    },
                    resetApp: {
                        help: 'Reset the app and return to Edit mode',
                        type: 'default',
                        classes: ['-reset'],
                        label: 'Reset'
                    },
                    offline: {
                        help: 'Currently disconnected from the server.',
                        type: 'danger',
                        classes: ['-cancel'],
                        label: 'Offline'
                    }
                }
            };

            this.controlBarTabs = {
                selectedTab: null,
                tabs: {
                    configure: {
                        label: 'Configure',
                        // widget: configureWidget()
                    },
                    viewConfigure: {
                        label: 'View Configure',
                        // widget: viewConfigureWidget()
                    },
                    batchConfigure: {
                        label: 'Configure Batch',
                        // widget: batchConfigureWidget()
                    },
                    viewBatchConfigure: {
                        label: 'View Batch Configure',
                        // widget: viewBatchConfigureWidget()
                    },
                    info: {
                        label: 'Info',
                        // widget: infoTabWidget,
                    },
                    logs: {
                        label: 'Job Status',
                        // widget: logTabWidget
                    },
                    results: {
                        label: 'Result',
                        // widget: resultsTabWidget
                    },
                    error: {
                        label: 'Error',
                        type: 'danger',
                        // widget: errorTabWidget
                    }
                }
            };

            if (initialize) {
                this.initialize(typesToFiles);
            }
            this.setupCell();
        }

        /**
         * Returns true if the given cell should be treated as a bulk import cell
         * @param {Cell} cell - a Jupyter Notebook cell
         */
        static isBulkImportCell(cell) {
            if (cell.cell_type !== 'code' || !cell.metadata.kbase) {
                return false;
            }
            return cell.metadata.kbase.type === CELL_TYPE;
        }

        /**
         * Does the initial pass on newly created cells to initialize its metadata and get it
         * set up for a new life as a Bulk Import Cell.
         *
         * @param {object} typesToFiles keys = data type strings, values = arrays of file paths
         * to import
         * e.g.: {
         *     'fastq_reads': ['file1.fq', 'file2.fq']
         * }
         */
        initialize(typesToFiles) {
            const meta = {
                kbase: {
                    attributes: {
                        id: new Uuid(4).format(),
                        status: 'new',
                        created: (new Date()).toUTCString(),
                        title: 'Import from Staging Area',
                        subtitle: 'Import files into your Narrative as data objects'
                    },
                    type: CELL_TYPE,
                    bulkImportCell: {
                        'user-settings': {
                            showCodeInputArea: false
                        },
                        inputs: typesToFiles
                    }
                }
            };
            this.cell.metadata = meta;
        }

        /**
         * This specializes this BulkImportCell's existing Jupyter Cell object to have several
         * extra functions that the Narrative can call.
         */
        specializeCell() {
            // returns a DOM node with an icon to be rendered elsewhere
            this.cell.getIcon = function() {
                return AppUtils.makeGenericIcon('upload', '#bf6c97');
            };

            // this renders the cell's icon in its toolbar
            this.cell.renderIcon = function() {
                const iconNode = this.element[0].querySelector('.celltoolbar [data-element="icon"]');
                if (iconNode) {
                    iconNode.innerHTML = this.getIcon();
                }
            };
        }

        /**
         * Initializes the base BulkImportCell's message bus and binds responses to external messages.
         */
        setupMessageBus() {
            this.cellBus = this.runtime.bus().makeChannelBus({
                name: {
                    cell: Utils.getMeta(this.cell, 'attributes', 'id')
                },
                description: 'parent bus for BulkImportCell'
            });
            this.busEventManager.add(this.cellBus.on('delete-cell', () => this.deleteCell()));
        }

        /**
         * Initializes the DOM node (kbaseNode) for rendering.
         */
        setupDomNode() {
            this.kbaseNode = document.createElement('div');
            // inserting after, with raw dom, means telling the parent node
            // to insert a node before the node following the one we are
            // referencing. If there is no next sibling, the null value
            // causes insertBefore to actually ... insert at the end!
            this.cell.input[0].parentNode.insertBefore(this.kbaseNode, this.cell.input[0].nextSibling);

            this.ui = UI.make({
                node: this.kbaseNode,
                bus: this.cellBus
            });
        }

        /**
         * This sets up the bulk import components.
         * This should only be called after a Bulk Import cell is initialized structurally -
         * i.e. if a new one is created, or if a page is loaded that already has one.
         */
        setupCell() {
            if (!BulkImportCell.isBulkImportCell(this.cell)) {
                throw new Error('Can only set up real bulk import cells');
            }

            // set up various cell function extensions
            this.specializeCell();

            // set up the message bus and bind various commands
            this.setupMessageBus();

            this.setupDomNode();

            // finalize by updating the lastLoaded attribute, which triggers a toolbar re-render
            let meta = this.cell.metadata;
            meta.kbase.attributes.lastLoaded = new Date().toUTCString();
            this.cell.metadata = meta;

            this.render();
        }

        /**
         * Deletes the cell from the notebook after doing internal cleanup.
         */
        deleteCell() {
            this.busEventManager.removeAll();
            const cellIndex = Jupyter.notebook.find_cell_index(this.cell);
            Jupyter.notebook.delete_cell(cellIndex);
        }

        toggleTab(tab) {
            alert(tab);
        }

        buildRunControlPanelRunButtons(events) {
            const style = {
                padding: '6px'
            };
            const buttonList = Object.keys(this.actionButtons.availableButtons).map((key) => {
                const button = this.actionButtons.availableButtons[key],
                    classes = [].concat(button.classes);
                let icon;
                if (button.icon) {
                    icon = {
                        name: button.icon.name,
                        size: 2
                    };
                }
                return this.ui.buildButton({
                    tip: button.help,
                    name: key,
                    events: events,
                    type: button.type || 'default',
                    classes: classes,
                    hidden: true,
                    // Overriding button class styles for this context.
                    style: {
                        width: '80px'
                    },
                    event: {
                        type: 'actionButton',
                        data: {
                            action: key
                        }
                    },
                    icon: icon,
                    label: button.label
                });
            });

            var buttonDiv = div({
                class: 'btn-group',
                style: style
            }, buttonList);
            return buttonDiv;
        }

        buildRunControlPanelDisplayButtons(events) {
            const buttons = Object.keys(this.controlBarTabs.tabs).map((key) => {
                const tab = this.controlBarTabs.tabs[key];
                let icon;
                if (!tab) {
                    console.warn('Tab not defined: ' + key);
                    return;
                }
                if (tab.icon) {
                    if (typeof tab.icon === 'string') {
                        icon = {
                            name: tab.icon,
                            size: 2
                        };
                    } else {
                        icon.size = 2;
                    }
                }
                return this.ui.buildButton({
                    label: tab.label,
                    name: key,
                    events: events,
                    type: tab.type || 'primary',
                    // hidden: true,
                    features: tab.features,
                    classes: ['kb-app-cell-btn'],
                    event: {
                        type: 'control-panel-tab',
                        data: {
                            tab: key
                        }
                    },
                    icon: icon
                });
            }).filter(function(x) {
                return x ? true : false;
            });
            this.cellBus.on('control-panel-tab', (message) => {
                var tab = message.data.tab;
                this.toggleTab(tab);
            });

            var outdatedBtn = a({
                tabindex: '0',
                type: 'button',
                class: 'btn hidden',
                dataContainer: 'body',
                container: 'body',
                dataToggle: 'popover',
                dataPlacement: 'bottom',
                dataTrigger: 'focus',
                dataElement: 'outdated',
                role: 'button',
                title: 'New version available',
                style: {
                    color: '#f79b22',
                    padding: '6px 0 0 0'
                }
            }, span({
                class: 'fa fa-exclamation-triangle fa-2x'
            }));
            buttons.unshift(outdatedBtn);

            return buttons;
        }

        buildRunControlPanel(events) {
            return div({ dataElement: 'run-control-panel' }, [
                div({
                    style: { border: '1px silver solid', height: '50px', position: 'relative', display: 'flex', flexDirection: 'row' }
                }, [
                    div({
                        style: {
                            height: '50px',
                            overflow: 'hidden',
                            textAlign: 'left',
                            lineHeight: '50px',
                            verticalAlign: 'middle',
                            display: 'flex',
                            flexDirection: 'row'
                        }
                    }, [
                        this.buildRunControlPanelRunButtons(events)
                    ]),
                    div({
                        dataElement: 'status',
                        style: {
                            width: '450px',
                            height: '50px',
                            overflow: 'hidden'
                        }
                    }, [
                        div({
                            style: {
                                height: '50px',
                                marginTop: '0px',
                                textAlign: 'left',
                                lineHeight: '50px',
                                verticalAlign: 'middle'
                            }
                        }, [
                            div([
                                span({ dataElement: 'execMessage' })
                            ])
                        ])
                    ]),
                    div({
                        dataElement: 'toolbar',
                        style: {
                            position: 'absolute',
                            right: '0',
                            top: '0',
                            height: '50px'
                        }
                    }, [
                        div({
                            style: {
                                display: 'inline-block',
                                right: '0',
                                height: '50px',
                                lineHeight: '50px',
                                paddingRight: '15px',
                                verticalAlign: 'bottom'
                            }
                        }, [
                            div({
                                class: 'btn-toolbar',
                                style: {
                                    display: 'inline-block',
                                    verticalAlign: 'bottom'
                                }
                            }, this.buildRunControlPanelDisplayButtons(events))
                        ])
                    ])
                ]),
                div({
                    dataElement: 'tab-pane'
                }, [
                    div({ dataElement: 'widget' })
                ])
            ]);
        }

        renderLayout() {
            const events = Events.make(),
                content = div({ class: 'kbase-extension kb-app-cell', style: { display: 'flex', alignItems: 'stretch' } }, [
                    div({ class: 'prompt', dataElement: 'prompt', style: { display: 'flex', alignItems: 'stretch', flexDirection: 'column' } }, [
                        div({ dataElement: 'status' })
                    ]),
                    div({
                        class: 'body',
                        dataElement: 'body',
                        style: { display: 'flex', alignItems: 'stretch', flexDirection: 'column', flex: '1', width: '100%' }
                    }, [
                        div({ dataElement: 'widget', style: { display: 'block', width: '100%' } }, [
                            div({ class: 'container-fluid' }, [
                                this.buildRunControlPanel(events)
                            ])
                        ])
                    ])
                ]);
            return {
                content: content,
                events: events
            };

        }

        /**
         * Renders the view.
         */
        render() {
            const layout = this.renderLayout();
            this.kbaseNode.innerHTML = layout.content;
            layout.events.attachEvents(this.kbaseNode);

        }

    }

    return BulkImportCell;
});
<|endoftext|>


<commit_before>var counter = 0;

function escapeHtml(s) {
    return s.replace(/"/g,""")
            .replace(/</g,"<")
            .replace(/>/g,">")
            .replace(/&/g,"&");
}

(function($){
     window.initQuery = function() {

         function createRemoveButton(callback)
         {
             var removeButton = document.createElement("input");
             removeButton.type = "button";
             removeButton.value = "-";
             removeButton.onclick = function() { callback($(this)); };
             return removeButton;
         }

         function createSelect(name, options, optional, value) {
             var e = document.createElement("select");
             if (name) {e.name = name;e.id=name;}
             if (optional) e.options[0] = new Option("(any)", "");
             if (options) {
                 var selected = 0;
                 for (var i = 0; i < options.length; i++) {
                     var option;
                     if (typeof(options[i]) == "object") {
                         option = new Option(options[i][1], options[i][0]);
                     } else {
                         option = new Option(options[i], options[i]);
                     }
                     if(value == option.value)
                         selected = e.options.length;
                     e.options[e.options.length] = option;
                 }
                 e.selectedIndex = selected;
             }
             return e;
         }

         var tbody = $("#species > tbody");

         function addSpecieOr(value) {
             var numrow = tbody.get(0).rows.length;

             var selects = createSelect('specie_' + (++counter), options['species'], true, value)
             selects.setAttribute("class","speciesSelect");

             var tr = $('
' + (numrow > 0 ? 'or' : '') + '
')
                 .append($('').append(selects))
                 .append($('')
                         .append(createRemoveButton(function (where) {
                                                        var tr = where.parents('tr:first');
                                                        var tbody = tr.parents('tbody:first').get(0);
                                                        if(tbody.rows.length == 1)
                                                            return;

                                                        if(tr.get(0).sectionRowIndex == tbody.rows.length - 1) {
                                                            $('td.addbuttons', tr.prev())
                                                                .replaceWith($('')
                                                                             .append($('<input type="button" value=" or ">')
                                                                                .bind('click', addSpecieOr)));
                                                        } else if(tr.get(0).sectionRowIndex == 0) {
                                                            $('td.prefix', tr.next()).html('');
                                                        }
                                                        tbody.deleteRow(tr.get(0).sectionRowIndex);
                                                    })));

             if(numrow > 0) {
                 $('tr:last > td.addbuttons', tbody).empty();
             }

             tr.append($('').append($('<input type="button" value=" or ">').bind('click', addSpecieOr)));
             tbody.append(tr);
         };

         function addConditionAnd(where, condition) {
             var tbody = $('#conditions > tbody');

             var andid = ++counter;

             var tr = $('
');
             var fval = $('');

             var factor = createSelect("fact_" + andid, options['factors'], true, condition && condition.factor);

             var loadValues = function (what,callback) {
                                               if(factor.options[factor.selectedIndex].value == "")
                                                   return;
                                               $.ajax({
                                                          // try to leverage ajaxQueue plugin to abort previous requests
                                                          mode: "abort",
                                                          // limit abortion to this input
                                                          port: "fvalues",
                                                          url: "fval",
                                                          data: { q: '', limit: 1000, factor: factor.options[factor.selectedIndex].value },
                                                          success: function(data) {
                                                              var rows = data.split("\n");
                                                              var list = [];
                                                              for (var i=0; i < rows.length; i++) {
                                                                  var row = $.trim(rows[i]);
                                                                  if (row) {
                                                                      row = row.split("|");
                                                                      list[list.length] = row[0];
                                                                  }
                                                              }
                                                              var sel = createSelect(what.attr('name'), list);
                                                              what.replaceWith(sel);
                                                              callback(sel);
                                                          }
                                                      });
                                           }

             factor.className = 'factor';
             factor.onchange = function() {
                 if (this.selectedIndex < 0) return;

                 var newv = this.options[this.selectedIndex].value;
                 fval.find('input.value').focus().attr('value', '').blur()
                     .setOptions({ extraParams: { factor : newv } }).flushCache();
                 loadValues(fval.find('select'), function() { });
                 tr.find("td.expansion").empty();
                 if(this.selectedIndex == 0)
                    fval.find('input.choose').attr('disabled', 'disabled');
                 else
                     fval.find('input.choose').removeAttr('disabled');
             };

             var expr = createSelect("gexp_" + andid, options['expressions'], false, condition && condition.expression);
             tr.append($('').append(expr).append(' in ').append(factor))
                 .append(fval)
                 .append($('')
                         .append($('
').append($('<input type="button" value=" and ">')
                                                    .bind('click', function() { addConditionAnd($(this)); } ))))
                     .append($('').html(condition != null ? condition.expansion : ''));

             function addConditionOr(where, value) {
                 var orid = ++counter;

                 var input = $('<input type="text" class="value"/>').attr('name', "fval_" + andid + '_' + orid)
                     .autocomplete("fval", {
                                       minChars:1,
                                       matchCase: true,
                                       matchSubset: false,
                                       multiple: true,
                                       multipleSeparator: " ",
                                       scroll: false,
                                       scrollHeight: 180,
                                       max: 10,
                                       extraParams: { 'factor' : factor.options[factor.selectedIndex].value },
                                       formatItem:function(row) {return row[0];}
                                   }).flushCache().keydown(function () {tr.find("td.expansion").text('');});
                 if(value != null)
                     input.val(value);

                 var newval = $('
')
                     .append($('
')
                             .append(input)
                             .append($('<input type="button" value="..." title="choose from list..." class="choose" />')
                                     .attr('disabled', 'disabled')
                                     .bind('click', function() {
                                                                 var vbutt = $(this);
                                                                 var oldval = input.val();
                                                                 loadValues(input, function(sel) {
                                                                      tr.find("td.expansion").text('');
                                                                      //
                                                                      for(var i = 0; i < sel.options.length; ++i)
                                                                          if(sel.options[i].value.indexOf(oldval) >= 0)
                                                                          {
                                                                               sel.selectedIndex = i;
                                                                               break;
                                                                          }
                                                                      vbutt.remove();
                                                                 });
                                                               })))
                     .append($('
')
                             .append(createRemoveButton(function (where) {
                                                            tr.find("td.expansion").text('');
                                                            if(fval.find('div.value').length == 1)
                                                            {
                                                                if(tbody.find('tr').length != 1)
                                                                    tr.remove();
                                                                else {
                                                                    factor.selectedIndex = 0;
                                                                    expr.selectedIndex = 0;
                                                                    input.focus().attr('value', '').blur()
                                                                            .setOptions({ extraParams: { factor : '' } }).flushCache();
                                                                }
                                                            } else {
                                                                newval.remove();
                                                            }
                                                        }))
                             .append($('<input type="button" value=" or ">')
                                     .bind('click', function() { addConditionOr($(this)); } )))
                     .append('
');

                 if(where)
                     where.parents('div.value:first').after(newval);
                 else
                     fval.append(newval);
             }

             if(condition && condition.values.length) {
                 for(var i = 0; i < condition.values.length; ++i)
                     addConditionOr(null, condition.values[i]);
             } else {
                 addConditionOr();
             }

             if(where)
                 where.parents('tr:first').after(tr);
             else
                 tbody.append(tr);
         }

         if(lastquery && lastquery.species.length) {
             for(var i = 0; i < lastquery.species.length; ++i)
                 addSpecieOr(lastquery.species[i]);
         } else {
             addSpecieOr();
         }

         if(lastquery && lastquery.conditions.length) {
             for(var i = 0; i < lastquery.conditions.length; ++i)
                 addConditionAnd(null, lastquery.conditions[i]);
         } else {
             addConditionAnd();
         }
     };

     window.renumberAll = function() {
         var i = 0;
         $('#species select').each(function(){ this.name = this.name.replace(/_\d+/, '_' + (++i)); });
         i = 0;
         $('#conditions tr').each(function(){
                                      $('td.common select', this).each(function(){ this.name = this.name.replace(/_\d+/, '_' + i); });
                                      var j = 0;
                                      $('td.factorvalue div.value', this)
                                          .each(function(){
                                                    $('input,select', this)
                                                        .each(function(){ this.name = this.name.replace(/_\d+_\d+/, '_' + i + '_' + j); });
                                                    ++j;
                                                });
                                      ++i;
                                  });
     };

     $.fn.extend({
         setonclick: function(callback) {
             return this.each(function() { this.onclick = callback; })
         }
     });

     function adjustExpandAll()
     {
         if($('#squery > tbody > tr.loaded').length == 0)
             $('#squery > thead img.expexp').setonclick(function(){loadExperiments()}).attr('src','expandopen.gif');
         else
             $('#squery > thead img.expexp').setonclick(function(){hideExperiments()}).attr('src','expandclose.gif');
     }

     window.hideExperiments = function(index) {
         var i = index;
         var tr = $(index == null ? '#squery > tbody > tr' : 'tr#squeryrow' + index).filter('.loaded').removeClass('loaded');
         tr.find('img.expexp').setonclick(function(){loadExperiments(i)}).attr('src','expandopen.gif');
         tr.find('td.counter div.exps').remove();
         adjustExpandAll();
     };

     window.loadExperiments = function(index) {
         var tr = $(index == null ? '#squery > tbody > tr' : 'tr#squeryrow' + index).not('.loaded').addClass('loaded');
         tr.find('img.expexp').attr('src','expandwait.gif');
         tr.each(function() {
             var tr = $(this);
             var i = this.id.substr(9);
             var geneDwId = resultGenes[i].geneDwId;
             $.ajax({
                 mode: "queue",
                 port: "sexpt",
                 url: exptUrlBase + '&gene=' + encodeURI(resultGenes[i].geneAtlasId),
                 dataType: "json",
                 success: function(o) {
                     if(o.empty)
                         return;

                     var k = 0;
                     tr.find('td.counter').each(function () {

                         var makeExps = function (exp, clas) {
                             var c = $('
').addClass(clas);
                             if(exp.length == 0)
                                 c.append('

')
                             for(var j = 0; j < exp.length; ++j)
                                 c.append('
'
                                         + exp[j].experimentAccessment + '
');
                             return c;
                         }

                         if(o[k].ups.length > 0 || o[k].downs.length > 0)
                             $(this).append($('
')
                                     .append(makeExps(o[k].ups, "upexp")).append(makeExps(o[k].downs, "dnexp")));
                         ++k;
                     });
                     tr.find('img.expexp').setonclick(function(){hideExperiments(i)}).attr('src','expandclose.gif');

                 }
             });
         });
         adjustExpandAll();
     }

 })(jQuery);
<commit_msg>- Fix "..." button initial state for chosen factors<commit_after>var counter = 0;

function escapeHtml(s) {
    return s.replace(/"/g,""")
            .replace(/</g,"<")
            .replace(/>/g,">")
            .replace(/&/g,"&");
}

(function($){
     window.initQuery = function() {

         function createRemoveButton(callback)
         {
             var removeButton = document.createElement("input");
             removeButton.type = "button";
             removeButton.value = "-";
             removeButton.onclick = function() { callback($(this)); };
             return removeButton;
         }

         function createSelect(name, options, optional, value) {
             var e = document.createElement("select");
             if (name) {e.name = name;e.id=name;}
             if (optional) e.options[0] = new Option("(any)", "");
             if (options) {
                 var selected = 0;
                 for (var i = 0; i < options.length; i++) {
                     var option;
                     if (typeof(options[i]) == "object") {
                         option = new Option(options[i][1], options[i][0]);
                     } else {
                         option = new Option(options[i], options[i]);
                     }
                     if(value == option.value)
                         selected = e.options.length;
                     e.options[e.options.length] = option;
                 }
                 e.selectedIndex = selected;
             }
             return e;
         }

         var tbody = $("#species > tbody");

         function addSpecieOr(value) {
             var numrow = tbody.get(0).rows.length;

             var selects = createSelect('specie_' + (++counter), options['species'], true, value)
             selects.setAttribute("class","speciesSelect");

             var tr = $('
' + (numrow > 0 ? 'or' : '') + '
')
                 .append($('').append(selects))
                 .append($('')
                         .append(createRemoveButton(function (where) {
                                                        var tr = where.parents('tr:first');
                                                        var tbody = tr.parents('tbody:first').get(0);
                                                        if(tbody.rows.length == 1)
                                                            return;

                                                        if(tr.get(0).sectionRowIndex == tbody.rows.length - 1) {
                                                            $('td.addbuttons', tr.prev())
                                                                .replaceWith($('')
                                                                             .append($('<input type="button" value=" or ">')
                                                                                .bind('click', addSpecieOr)));
                                                        } else if(tr.get(0).sectionRowIndex == 0) {
                                                            $('td.prefix', tr.next()).html('');
                                                        }
                                                        tbody.deleteRow(tr.get(0).sectionRowIndex);
                                                    })));

             if(numrow > 0) {
                 $('tr:last > td.addbuttons', tbody).empty();
             }

             tr.append($('').append($('<input type="button" value=" or ">').bind('click', addSpecieOr)));
             tbody.append(tr);
         };

         function addConditionAnd(where, condition) {
             var tbody = $('#conditions > tbody');

             var andid = ++counter;

             var tr = $('
');
             var fval = $('');

             var factor = createSelect("fact_" + andid, options['factors'], true, condition && condition.factor);

             var loadValues = function (what,callback) {
                                               if(factor.options[factor.selectedIndex].value == "")
                                                   return;
                                               $.ajax({
                                                          // try to leverage ajaxQueue plugin to abort previous requests
                                                          mode: "abort",
                                                          // limit abortion to this input
                                                          port: "fvalues",
                                                          url: "fval",
                                                          data: { q: '', limit: 1000, factor: factor.options[factor.selectedIndex].value },
                                                          success: function(data) {
                                                              var rows = data.split("\n");
                                                              var list = [];
                                                              for (var i=0; i < rows.length; i++) {
                                                                  var row = $.trim(rows[i]);
                                                                  if (row) {
                                                                      row = row.split("|");
                                                                      list[list.length] = row[0];
                                                                  }
                                                              }
                                                              var sel = createSelect(what.attr('name'), list);
                                                              what.replaceWith(sel);
                                                              callback(sel);
                                                          }
                                                      });
                                           }

             factor.className = 'factor';
             factor.onchange = function() {
                 if (this.selectedIndex < 0) return;

                 var newv = this.options[this.selectedIndex].value;
                 fval.find('input.value').focus().attr('value', '').blur()
                     .setOptions({ extraParams: { factor : newv } }).flushCache();
                 loadValues(fval.find('select'), function() { });
                 tr.find("td.expansion").empty();
                 if(this.selectedIndex == 0)
                    fval.find('input.choose').attr('disabled', 'disabled');
                 else
                     fval.find('input.choose').removeAttr('disabled');
             };

             var expr = createSelect("gexp_" + andid, options['expressions'], false, condition && condition.expression);
             tr.append($('').append(expr).append(' in ').append(factor))
                 .append(fval)
                 .append($('')
                         .append($('
').append($('<input type="button" value=" and ">')
                                                    .bind('click', function() { addConditionAnd($(this)); } ))))
                     .append($('').html(condition != null ? condition.expansion : ''));

             function addConditionOr(where, value) {
                 var orid = ++counter;

                 var input = $('<input type="text" class="value"/>').attr('name', "fval_" + andid + '_' + orid)
                     .autocomplete("fval", {
                                       minChars:1,
                                       matchCase: true,
                                       matchSubset: false,
                                       multiple: true,
                                       multipleSeparator: " ",
                                       scroll: false,
                                       scrollHeight: 180,
                                       max: 10,
                                       extraParams: { 'factor' : factor.options[factor.selectedIndex].value },
                                       formatItem:function(row) {return row[0];}
                                   }).flushCache().keydown(function () {tr.find("td.expansion").text('');});
                 if(value != null)
                     input.val(value);

                 var choose = $('<input type="button" value="..." title="choose from list..." class="choose" />')
                                     .bind('click', function() {
                                                                 var vbutt = $(this);
                                                                 var oldval = input.val();
                                                                 loadValues(input, function(sel) {
                                                                      tr.find("td.expansion").text('');
                                                                      //
                                                                      for(var i = 0; i < sel.options.length; ++i)
                                                                          if(sel.options[i].value.indexOf(oldval) >= 0)
                                                                          {
                                                                               sel.selectedIndex = i;
                                                                               break;
                                                                          }
                                                                      vbutt.remove();
                                                                 });
                                                               });
                 if(factor.selectedIndex == 0)
                     choose.attr('disabled', 'disabled');

                 var newval = $('
')
                     .append($('
')
                             .append(input)
                             .append(choose))
                     .append($('
')
                             .append(createRemoveButton(function (where) {
                                                            tr.find("td.expansion").text('');
                                                            if(fval.find('div.value').length == 1)
                                                            {
                                                                if(tbody.find('tr').length != 1)
                                                                    tr.remove();
                                                                else {
                                                                    factor.selectedIndex = 0;
                                                                    expr.selectedIndex = 0;
                                                                    input.focus().attr('value', '').blur()
                                                                            .setOptions({ extraParams: { factor : '' } }).flushCache();
                                                                }
                                                            } else {
                                                                newval.remove();
                                                            }
                                                        }))
                             .append($('<input type="button" value=" or ">')
                                     .bind('click', function() { addConditionOr($(this)); } )))
                     .append('
');

                 if(where)
                     where.parents('div.value:first').after(newval);
                 else
                     fval.append(newval);
             }

             if(condition && condition.values.length) {
                 for(var i = 0; i < condition.values.length; ++i)
                     addConditionOr(null, condition.values[i]);
             } else {
                 addConditionOr();
             }

             if(where)
                 where.parents('tr:first').after(tr);
             else
                 tbody.append(tr);
         }

         if(lastquery && lastquery.species.length) {
             for(var i = 0; i < lastquery.species.length; ++i)
                 addSpecieOr(lastquery.species[i]);
         } else {
             addSpecieOr();
         }

         if(lastquery && lastquery.conditions.length) {
             for(var i = 0; i < lastquery.conditions.length; ++i)
                 addConditionAnd(null, lastquery.conditions[i]);
         } else {
             addConditionAnd();
         }
     };

     window.renumberAll = function() {
         var i = 0;
         $('#species select').each(function(){ this.name = this.name.replace(/_\d+/, '_' + (++i)); });
         i = 0;
         $('#conditions tr').each(function(){
                                      $('td.common select', this).each(function(){ this.name = this.name.replace(/_\d+/, '_' + i); });
                                      var j = 0;
                                      $('td.factorvalue div.value', this)
                                          .each(function(){
                                                    $('input,select', this)
                                                        .each(function(){ this.name = this.name.replace(/_\d+_\d+/, '_' + i + '_' + j); });
                                                    ++j;
                                                });
                                      ++i;
                                  });
     };

     $.fn.extend({
         setonclick: function(callback) {
             return this.each(function() { this.onclick = callback; })
         }
     });

     function adjustExpandAll()
     {
         if($('#squery > tbody > tr.loaded').length == 0)
             $('#squery > thead img.expexp').setonclick(function(){loadExperiments()}).attr('src','expandopen.gif');
         else
             $('#squery > thead img.expexp').setonclick(function(){hideExperiments()}).attr('src','expandclose.gif');
     }

     window.hideExperiments = function(index) {
         var i = index;
         var tr = $(index == null ? '#squery > tbody > tr' : 'tr#squeryrow' + index).filter('.loaded').removeClass('loaded');
         tr.find('img.expexp').setonclick(function(){loadExperiments(i)}).attr('src','expandopen.gif');
         tr.find('td.counter div.exps').remove();
         adjustExpandAll();
     };

     window.loadExperiments = function(index) {
         var tr = $(index == null ? '#squery > tbody > tr' : 'tr#squeryrow' + index).not('.loaded').addClass('loaded');
         tr.find('img.expexp').attr('src','expandwait.gif');
         tr.each(function() {
             var tr = $(this);
             var i = this.id.substr(9);
             var geneDwId = resultGenes[i].geneDwId;
             $.ajax({
                 mode: "queue",
                 port: "sexpt",
                 url: exptUrlBase + '&gene=' + encodeURI(resultGenes[i].geneAtlasId),
                 dataType: "json",
                 success: function(o) {
                     if(o.empty)
                         return;

                     var k = 0;
                     tr.find('td.counter').each(function () {

                         var makeExps = function (exp, clas) {
                             var c = $('
').addClass(clas);
                             if(exp.length == 0)
                                 c.append('

')
                             for(var j = 0; j < exp.length; ++j)
                                 c.append('
'
                                         + exp[j].experimentAccessment + '
');
                             return c;
                         }

                         if(o[k].ups.length > 0 || o[k].downs.length > 0)
                             $(this).append($('
')
                                     .append(makeExps(o[k].ups, "upexp")).append(makeExps(o[k].downs, "dnexp")));
                         ++k;
                     });
                     tr.find('img.expexp').setonclick(function(){hideExperiments(i)}).attr('src','expandclose.gif');

                 }
             });
         });
         adjustExpandAll();
     }

 })(jQuery);
<|endoftext|>