From 30df6bda6c0ad060c8cbdb8ca670155c4e1e0bcd Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Thu, 28 Jul 2016 20:29:12 +0530
Subject: [PATCH 01/59] [WIP] Schedule UI for snapshot in customizer

---
 css/customize-snapshots.css              |  70 +++++++++++++-
 js/customize-snapshots.js                |  32 +++++--
 php/class-customize-snapshot-manager.php | 112 ++++++++++++++++++++++-
 3 files changed, 202 insertions(+), 12 deletions(-)

diff --git a/css/customize-snapshots.css b/css/customize-snapshots.css
index 53c8ee3a..ccdb2469 100644
--- a/css/customize-snapshots.css
+++ b/css/customize-snapshots.css
@@ -1,10 +1,10 @@
-#snapshot-preview-link, #snapshot-edit-link {
+#snapshot-preview-link, #snapshot-toggle-button {
 	float: right;
 	margin-top: 13px;
 	margin-right: 4px;
 	color: #656a6f;
 }
-#snapshot-edit-link{
+#snapshot-toggle-button{
 	display: block;
 }
 
@@ -53,4 +53,70 @@
 		margin-top: 6px;
 		margin-right: 6px;
 	}
+}
+
+#customize-snapshot-control select,
+#customize-snapshot-control input[type="text"] {
+	min-width: 10%;
+	width: auto;
+}
+
+#customize-snapshot-control{
+	padding: 5px;
+}
+
+#customize-snapshot-control input[type="text"].error {
+	border-color: #dc3232 !important;
+	-webkit-box-shadow: 0 0 2px rgba( 204, 0, 0, 0.8 );
+	box-shadow: 0 0 2px rgba( 204, 0, 0, 0.8 );
+}
+
+#customize-schedule-box{
+	background: #fff !important;
+	margin-bottom: 10px;
+	padding-bottom: 10px;
+}
+
+#customize-schedule-box .accordion-section-title{
+	background: #fff;
+	color: #555;
+	border-left: none;
+	border-right: none;
+	border-bottom: none;
+	cursor: default;
+}
+
+#customize-schedule-box .accordion-section-title:after{
+	z-index: -1;
+}
+#customize-schedule-box a{
+	position: absolute;
+	top: 4px;
+	right: 1px;
+	width: 20px;
+	height: 20px;
+	cursor: pointer;
+	-webkit-box-shadow: none;
+	box-shadow: none;
+	-webkit-appearance: none;
+	background: transparent;
+	color: #555;
+	border: none;
+	padding: 10px;
+}
+
+#customize-schedule-box span{
+	font-size: 13px;
+	line-height: 24px;
+}
+#customize-schedule-box .customize-snapshot-control{
+	padding: 5px;
+}
+#customize-schedule-box .accordion-section-title{
+	padding: 10px;
+}
+.in-sub-panel #customize-schedule-box {
+	left: -100%;
+	width: 100%;
+	position: relative;
 }
\ No newline at end of file
diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 946b2430..a0fed96b 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -159,8 +159,9 @@
 	component.addButtons = function() {
 		var header = $( '#customize-header-actions' ),
 			publishButton = header.find( '#save' ),
-			snapshotEditLinkTemplate = wp.template( 'snapshot-edit-link' ),
-			snapshotButton, submitButton, data, setPreviewLinkHref, snapshotEditLinkEl;
+			snapshotDropDownToggleTemplate = wp.template( 'snapshot-toggle-button' ),
+			snapshotScheduleBoxTemplate = wp.template( 'snapshot-schedule-accordion' ),
+			snapshotScheduleBox, snapshotButton, submitButton, data, setPreviewLinkHref, snapshotDropDownToggle, snapshotEditLink;
 
 		// Save/update button.
 		snapshotButton = wp.template( 'snapshot-save' );
@@ -174,19 +175,34 @@
 		snapshotButton.prop( 'disabled', true );
 		snapshotButton.insertAfter( publishButton );
 
-		snapshotEditLinkEl = $( $.trim( snapshotEditLinkTemplate( component.data ) ) );
-		snapshotEditLinkEl.insertAfter( snapshotButton );
+		snapshotDropDownToggle = $( $.trim( snapshotDropDownToggleTemplate( {} ) ) );
+		snapshotEditLink = snapshotDropDownToggle.find( 'a' );
+
+		snapshotDropDownToggle.click( function( e ) {
+			var customizeInfo = $( '#customize-info' );
+			if ( ! snapshotScheduleBox ) {
+				snapshotScheduleBox = $( $.trim( snapshotScheduleBoxTemplate( component.data ) ) );
+				snapshotScheduleBox.insertBefore( customizeInfo );
+			} else {
+
+				// Todo need to update in case of dynamic section.
+				snapshotScheduleBox.slideToggle();
+			}
+			e.preventDefault();
+		} );
+
+		snapshotDropDownToggle.insertAfter( snapshotButton );
 		if ( ! component.data.editLink ) {
-			snapshotEditLinkEl.hide();
+			snapshotDropDownToggle.hide();
 		}
 		api.state.bind( 'change', function() {
-			snapshotEditLinkEl.toggle( api.state( 'snapshot-saved' ).get() && api.state( 'snapshot-exists' ).get() );
+			snapshotDropDownToggle.toggle( api.state( 'snapshot-saved' ).get() && api.state( 'snapshot-exists' ).get() );
 		} );
 
 		api.state( 'snapshot-saved' ).bind( function( saved ) {
 			snapshotButton.prop( 'disabled', saved );
 			if ( saved ) {
-				snapshotEditLinkEl.attr( 'href', component.data.editLink );
+				snapshotEditLink.attr( 'href', component.data.editLink );
 			}
 		} );
 
@@ -205,7 +221,7 @@
 				buttonText = component.data.i18n.updateButton;
 				permsMsg = component.data.i18n.permsMsg.update;
 				if ( component.data.editLink ) {
-					snapshotEditLinkEl.attr( 'href', component.data.editLink );
+					snapshotEditLink.attr( 'href', component.data.editLink );
 				}
 			} else {
 				buttonText = component.data.i18n.saveButton;
diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index b8bd4ebb..ab29e932 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -1058,6 +1058,7 @@ public function replace_customize_link( $wp_admin_bar ) {
 	 * Underscore (JS) templates for dialog windows.
 	 */
 	public function render_templates() {
+		$data = $this->data_json();
 		?>
 		<script type="text/html" id="tmpl-snapshot-preview-link">
 			<a href="#" target="frontend-preview" id="snapshot-preview-link" class="dashicons dashicons-welcome-view-site" title="<?php esc_attr_e( 'View on frontend', 'customize-snapshots' ) ?>">
@@ -1065,8 +1066,74 @@ public function render_templates() {
 			</a>
 		</script>
 
-		<script type="text/html" id="tmpl-snapshot-edit-link">
-			<a href="{{ data.editLink }}" id="snapshot-edit-link" class="dashicons dashicons-calendar-alt" title="<?php esc_attr_e( 'Edit Snapshot','customize-snapshots' ); ?>"></a>
+		<script type="text/html" id="tmpl-snapshot-toggle-button">
+			<a href="#" id="snapshot-toggle-button" class="dashicons dashicons-calendar-alt" title="<?php esc_attr_e( 'Schedule Snapshot','customize-snapshots' ); ?>"></a>
+		</script>
+
+		<script type="text/html" id="tmpl-snapshot-schedule-accordion">
+			<div id="customize-schedule-box" class="accordion-section">
+				<div class="accordion-section-title">
+					<span class="preview-notice"><strong class="panel-title site-title"><?php esc_html_e( 'Schedule Snapshot', 'customize-snapshots' ); ?></strong></span>
+					<a href={{ data.editLink }} class="dashicons dashicons-edit" aria-expanded="false"></a>
+				</div>
+				<div class="customize-snapshot-control">
+					<#
+						_.defaults( data, <?php echo wp_json_encode( $data ) ?> );
+						data.input_id_post_date = 'input-' + String( Math.random() );
+						data.input_id_post_date_gmt = 'input-' + String( Math.random() );
+						#>
+						<# _.each( data.date_inputs, function( width, type ) { #>
+							<# if ( 'month' === type  ) { #>
+								<select class="date-input {{ type }}">
+									<# _.each( data.month_choices, function( choice ) { #>
+										<# if ( _.isObject( choice ) && ! _.isUndefined( choice.text ) && ! _.isUndefined( choice.value ) ) {
+												text = choice.text;
+												value = choice.value;
+											} #>
+										<option value="{{ value }}">{{ text }}</option>
+									<# } ); #>
+								</select>
+							<# } else { #>
+								<input
+									type="text"
+									size="{{ width }}"
+									maxlength="{{ width }}"
+									autocomplete="off"
+									class="date-input {{ type }}"
+								/>
+									<# if ( 'year' === type ) { #>
+										&nbsp;@&nbsp;
+									<# } #>
+							<# } #>
+						<# }); #>
+						<input
+							id=""
+							type="hidden"
+							class="post-date"
+							<# if ( data.setting_property ) { #>
+								data-customize-setting-property-link="post_date"
+							<# } #>
+							/>
+						<input
+							id=""
+							type="hidden"
+							class="post-date-gmt"
+							value=""
+							<# if ( data.setting_property ) { #>
+								data-customize-setting-property-link="post_date_gmt"
+							<# } #>
+							/>
+						<input
+							id=""
+							type="hidden"
+							class="post-status"
+							value=""
+							<# if ( data.setting_property ) { #>
+								data-customize-setting-property-link="post_status"
+							<# } #>
+							/>
+				</div>
+			</div>
 		</script>
 
 		<script type="text/html" id="tmpl-snapshot-save">
@@ -1088,4 +1155,45 @@ public function render_templates() {
 		</script>
 		<?php
 	}
+
+	/**
+	 * Get the data to export to the client via JSON.
+	 *
+	 * @return array Array of parameters passed to the JavaScript.
+	 */
+	public function data_json() {
+		$exported['month_choices'] = $this->get_month_choices();
+		// Type / width pairs.
+		$exported['date_inputs'] = array(
+			'month' => null,
+			'day' => 2,
+			'year' => 4,
+			'hour' => 2,
+			'min' => 2,
+		);
+		return $exported;
+	}
+
+	/**
+	 * Generate options for the month Select.
+	 *
+	 * Based on touch_time().
+	 *
+	 * @see touch_time()
+	 *
+	 * @return array
+	 */
+	public function get_month_choices() {
+		global $wp_locale;
+		$months = array();
+		for ( $i = 1; $i < 13; $i = $i + 1 ) {
+			$month_number = zeroise( $i, 2 );
+			$month_text = $wp_locale->get_month_abbrev( $wp_locale->get_month( $i ) );
+
+			/* translators: 1: month number (01, 02, etc.), 2: month abbreviation */
+			$months[ $i ]['text'] = sprintf( __( '%1$s-%2$s', 'customize-snapshots' ), $month_number, $month_text );
+			$months[ $i ]['value'] = $month_number;
+		}
+		return $months;
+	}
 }

From b01a91e8b7bee4e92fa772022627bc82cbec5804 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Fri, 29 Jul 2016 15:14:06 +0530
Subject: [PATCH 02/59] Template and css update

---
 css/customize-snapshots.css              | 18 +++--
 php/class-customize-snapshot-manager.php | 97 ++++++------------------
 2 files changed, 35 insertions(+), 80 deletions(-)

diff --git a/css/customize-snapshots.css b/css/customize-snapshots.css
index ccdb2469..39d3ef12 100644
--- a/css/customize-snapshots.css
+++ b/css/customize-snapshots.css
@@ -55,22 +55,24 @@
 	}
 }
 
-#customize-snapshot-control select,
-#customize-snapshot-control input[type="text"] {
+.customize-snapshot-control select,
+.customize-snapshot-control input.date-input {
 	min-width: 10%;
 	width: auto;
 }
+.customize-snapshot-control input.date-input {
+	-moz-appearance: textfield;
+}
+.customize-snapshot-control input.date-input::-webkit-outer-spin-button,
+.customize-snapshot-control input.date-input::-webkit-inner-spin-button {
+	-webkit-appearance: none;
+	margin: 0;
+}
 
 #customize-snapshot-control{
 	padding: 5px;
 }
 
-#customize-snapshot-control input[type="text"].error {
-	border-color: #dc3232 !important;
-	-webkit-box-shadow: 0 0 2px rgba( 204, 0, 0, 0.8 );
-	box-shadow: 0 0 2px rgba( 204, 0, 0, 0.8 );
-}
-
 #customize-schedule-box{
 	background: #fff !important;
 	margin-bottom: 10px;
diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index ab29e932..cc4b5959 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -533,6 +533,7 @@ public function enqueue_scripts() {
 			'action' => self::AJAX_ACTION,
 			'uuid' => $this->snapshot ? $this->snapshot->uuid() : self::generate_uuid(),
 			'editLink' => $this->snapshot ? get_edit_post_link( $this->snapshot->post(), 'raw' ) : '',
+			'snapshotPublishDate' => $this->snapshot ? $this->snapshot->post()->post_date_gmt : '',
 			'currentUserCanPublish' => current_user_can( 'customize_publish' ),
 			'i18n' => array(
 				'saveButton' => __( 'Save', 'customize-snapshots' ),
@@ -938,6 +939,7 @@ function( $value ) {
 		$post = $this->snapshot->post();
 		if ( $post ) {
 			$data['edit_link'] = get_edit_post_link( $post, 'raw' );
+			$data['snapshotPublishDate'] = $post->post_date_gmt;
 		}
 
 		if ( is_wp_error( $r ) ) {
@@ -1058,7 +1060,7 @@ public function replace_customize_link( $wp_admin_bar ) {
 	 * Underscore (JS) templates for dialog windows.
 	 */
 	public function render_templates() {
-		$data = $this->data_json();
+		$data = $this->get_month_choices();
 		?>
 		<script type="text/html" id="tmpl-snapshot-preview-link">
 			<a href="#" target="frontend-preview" id="snapshot-preview-link" class="dashicons dashicons-welcome-view-site" title="<?php esc_attr_e( 'View on frontend', 'customize-snapshots' ) ?>">
@@ -1074,64 +1076,33 @@ public function render_templates() {
 			<div id="customize-schedule-box" class="accordion-section">
 				<div class="accordion-section-title">
 					<span class="preview-notice"><strong class="panel-title site-title"><?php esc_html_e( 'Schedule Snapshot', 'customize-snapshots' ); ?></strong></span>
+					<# if ( data.description ) { #>
+						<span class="description customize-control-description">
+							{{ data.description }}
+						</span>
+					<# } #>
 					<a href={{ data.editLink }} class="dashicons dashicons-edit" aria-expanded="false"></a>
 				</div>
 				<div class="customize-snapshot-control">
-					<#
-						_.defaults( data, <?php echo wp_json_encode( $data ) ?> );
+					<# _.defaults( data, <?php echo wp_json_encode( $data ) ?> );
 						data.input_id_post_date = 'input-' + String( Math.random() );
 						data.input_id_post_date_gmt = 'input-' + String( Math.random() );
 						#>
-						<# _.each( data.date_inputs, function( width, type ) { #>
-							<# if ( 'month' === type  ) { #>
-								<select class="date-input {{ type }}">
-									<# _.each( data.month_choices, function( choice ) { #>
-										<# if ( _.isObject( choice ) && ! _.isUndefined( choice.text ) && ! _.isUndefined( choice.value ) ) {
-												text = choice.text;
-												value = choice.value;
-											} #>
-										<option value="{{ value }}">{{ text }}</option>
-									<# } ); #>
-								</select>
-							<# } else { #>
-								<input
-									type="text"
-									size="{{ width }}"
-									maxlength="{{ width }}"
-									autocomplete="off"
-									class="date-input {{ type }}"
-								/>
-									<# if ( 'year' === type ) { #>
-										&nbsp;@&nbsp;
-									<# } #>
-							<# } #>
-						<# }); #>
-						<input
-							id=""
-							type="hidden"
-							class="post-date"
-							<# if ( data.setting_property ) { #>
-								data-customize-setting-property-link="post_date"
-							<# } #>
-							/>
-						<input
-							id=""
-							type="hidden"
-							class="post-date-gmt"
-							value=""
-							<# if ( data.setting_property ) { #>
-								data-customize-setting-property-link="post_date_gmt"
-							<# } #>
-							/>
-						<input
-							id=""
-							type="hidden"
-							class="post-status"
-							value=""
-							<# if ( data.setting_property ) { #>
-								data-customize-setting-property-link="post_status"
-							<# } #>
-							/>
+						<select id="{{ data.input_id }}" class="date-input month" data-component="month">
+							<# _.each( data.month_choices, function( choice ) { #>
+								<# if ( _.isObject( choice ) && ! _.isUndefined( choice.text ) && ! _.isUndefined( choice.value ) ) {
+									text = choice.text;
+									value = choice.value;
+									}
+									#>
+								<option value="{{ value }}">{{ text }}</option>
+							<# } ); #>
+						</select>
+
+						<input type="number" size="2" maxlength="2" autocomplete="off" class="date-input day" data-component="day" min="1" max="31" />,
+						<input type="number" size="4" maxlength="4" autocomplete="off" class="date-input year" data-component="year" min="<?php echo esc_attr( date( 'Y' ) ); ?>" max="9999" />
+						@ <input type="number" size="2" maxlength="2" autocomplete="off" class="date-input hour" data-component="hour" min="0" max="23" />:<?php
+						?><input type="number" size="2" maxlength="2" autocomplete="off" class="date-input minute" data-component="minute" min="0" max="59" />
 				</div>
 			</div>
 		</script>
@@ -1156,24 +1127,6 @@ class="post-status"
 		<?php
 	}
 
-	/**
-	 * Get the data to export to the client via JSON.
-	 *
-	 * @return array Array of parameters passed to the JavaScript.
-	 */
-	public function data_json() {
-		$exported['month_choices'] = $this->get_month_choices();
-		// Type / width pairs.
-		$exported['date_inputs'] = array(
-			'month' => null,
-			'day' => 2,
-			'year' => 4,
-			'hour' => 2,
-			'min' => 2,
-		);
-		return $exported;
-	}
-
 	/**
 	 * Generate options for the month Select.
 	 *
@@ -1194,6 +1147,6 @@ public function get_month_choices() {
 			$months[ $i ]['text'] = sprintf( __( '%1$s-%2$s', 'customize-snapshots' ), $month_number, $month_text );
 			$months[ $i ]['value'] = $month_number;
 		}
-		return $months;
+		return array( 'month_choices' => $months );
 	}
 }

From 069fbbeb8f2811580ca716e3efaadec608ba1037 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Mon, 1 Aug 2016 17:58:08 +0530
Subject: [PATCH 03/59] Update template, refactor js for slidedown and add js
 function for date manipulation

---
 js/customize-snapshots.js                | 119 ++++++++++++++++++-----
 php/class-customize-snapshot-manager.php |  93 +++++++++++++++---
 2 files changed, 177 insertions(+), 35 deletions(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index a0fed96b..dc64e5ee 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -40,6 +40,7 @@
 
 			component.extendPreviewerQuery();
 			component.addButtons();
+			component.addSlideDown();
 
 			$( '#snapshot-save' ).on( 'click', function( event ) {
 				event.preventDefault();
@@ -151,6 +152,32 @@
 		return a.href;
 	};
 
+	component.dateComponentInputs = {};
+
+	component.addSlideDown = function slideDown() {
+		var snapshotScheduleBoxTemplate = wp.template( 'snapshot-schedule-accordion' ),
+			snapshotScheduleBox;
+		component.snapshotSlideDownToggle.click( function( e ) {
+			var customizeInfo = $( '#customize-info' );
+			if ( ! snapshotScheduleBox ) {
+				component.data = _.extend( component.data, component.parseDateTime( component.data.snapshotPublishDate ) );
+				snapshotScheduleBox = $( $.trim( snapshotScheduleBoxTemplate( component.data ) ) );
+				component.dateInputs = snapshotScheduleBox.find( '.date-input' );
+				component.dateInputs.each( function() {
+						var input = $( this ), componentName;
+						componentName = input.data( 'component' );
+						component.dateComponentInputs[ componentName ] = input;
+				} );
+				snapshotScheduleBox.insertBefore( customizeInfo );
+			} else {
+
+				// Todo need to update in case of dynamic section.
+				snapshotScheduleBox.slideToggle();
+			}
+			e.preventDefault();
+		} );
+	};
+
 	/**
 	 * Create the snapshot buttons.
 	 *
@@ -159,9 +186,8 @@
 	component.addButtons = function() {
 		var header = $( '#customize-header-actions' ),
 			publishButton = header.find( '#save' ),
-			snapshotDropDownToggleTemplate = wp.template( 'snapshot-toggle-button' ),
-			snapshotScheduleBoxTemplate = wp.template( 'snapshot-schedule-accordion' ),
-			snapshotScheduleBox, snapshotButton, submitButton, data, setPreviewLinkHref, snapshotDropDownToggle, snapshotEditLink;
+			snapshotSlideDownToggleTemplate = wp.template( 'snapshot-toggle-button' ),
+			snapshotButton, submitButton, data, setPreviewLinkHref, snapshotSlideDownToggle, snapshotEditLink;
 
 		// Save/update button.
 		snapshotButton = wp.template( 'snapshot-save' );
@@ -175,28 +201,15 @@
 		snapshotButton.prop( 'disabled', true );
 		snapshotButton.insertAfter( publishButton );
 
-		snapshotDropDownToggle = $( $.trim( snapshotDropDownToggleTemplate( {} ) ) );
-		snapshotEditLink = snapshotDropDownToggle.find( 'a' );
-
-		snapshotDropDownToggle.click( function( e ) {
-			var customizeInfo = $( '#customize-info' );
-			if ( ! snapshotScheduleBox ) {
-				snapshotScheduleBox = $( $.trim( snapshotScheduleBoxTemplate( component.data ) ) );
-				snapshotScheduleBox.insertBefore( customizeInfo );
-			} else {
-
-				// Todo need to update in case of dynamic section.
-				snapshotScheduleBox.slideToggle();
-			}
-			e.preventDefault();
-		} );
-
-		snapshotDropDownToggle.insertAfter( snapshotButton );
+		snapshotSlideDownToggle = $( $.trim( snapshotSlideDownToggleTemplate( {} ) ) );
+		snapshotSlideDownToggle.insertAfter( snapshotButton );
+		snapshotEditLink = snapshotSlideDownToggle.find( 'a' );
+		component.snapshotSlideDownToggle = snapshotSlideDownToggle;
 		if ( ! component.data.editLink ) {
-			snapshotDropDownToggle.hide();
+			snapshotSlideDownToggle.hide();
 		}
 		api.state.bind( 'change', function() {
-			snapshotDropDownToggle.toggle( api.state( 'snapshot-saved' ).get() && api.state( 'snapshot-exists' ).get() );
+			snapshotSlideDownToggle.toggle( api.state( 'snapshot-saved' ).get() && api.state( 'snapshot-exists' ).get() );
 		} );
 
 		api.state( 'snapshot-saved' ).bind( function( saved ) {
@@ -415,6 +428,68 @@
 		} );
 	};
 
+	/**
+	 * Get date from inputs.
+	 *
+	 * @returns {Date|null} Date created from inputs or null if invalid date.
+	 */
+	component.getDateFromInputs = function getDateFromInputs() {
+		var control = component, date;
+		date = new Date(
+			parseInt( control.dateComponentInputs.year.val(), 10 ),
+			parseInt( control.dateComponentInputs.month.val(), 10 ) - 1,
+			parseInt( control.dateComponentInputs.day.val(), 10 ),
+			parseInt( control.dateComponentInputs.hour.val(), 10 ),
+			parseInt( control.dateComponentInputs.minute.val(), 10 )
+		);
+		if ( isNaN( date.valueOf() ) ) {
+			return null;
+		}
+		return date;
+	};
+
+	/**
+	 * Parse datetime string.
+	 *
+	 * @param {string} datetime Date/Time string.
+	 * @returns {object|null} Returns object containing date components or null if parse error.
+	 */
+	component.parseDateTime = function parseDateTime( datetime ) {
+		var matches = datetime.match( /^(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)$/ );
+		if ( ! matches ) {
+			return null;
+		}
+		matches.shift();
+		return {
+			year: matches.shift(),
+			month: matches.shift(),
+			day: matches.shift(),
+			hour: matches.shift(),
+			minute: matches.shift(),
+			second: matches.shift()
+		};
+	};
+
+	/**
+	 * Format a Date Object. Returns 'Y-m-d H:i:s' format.
+	 *
+	 * @param {Date} date A Date object.
+	 * @returns {string} A formatted date String.
+	 */
+	component.formatDate = function formatDate( date ) {
+		var formattedDate, yearLength = 4, nonYearLength = 2;
+
+		// Props: http://stackoverflow.com/questions/10073699/pad-a-number-with-leading-zeros-in-javascript#comment33639551_10073699
+		formattedDate = ( '0000' + date.getFullYear() ).substr( -yearLength, yearLength );
+		formattedDate += '-' + ( '00' + ( date.getMonth() + 1 ) ).substr( -nonYearLength, nonYearLength );
+		formattedDate += '-' + ( '00' + date.getDate() ).substr( -nonYearLength, nonYearLength );
+		formattedDate += ' ' + ( '00' + date.getHours() ).substr( -nonYearLength, nonYearLength );
+		formattedDate += ':' + ( '00' + date.getMinutes() ).substr( -nonYearLength, nonYearLength );
+		formattedDate += ':' + ( '00' + date.getSeconds() ).substr( -nonYearLength, nonYearLength );
+
+		return formattedDate;
+	};
+
 	component.init();
 
 } )( wp.customize, jQuery );
diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index cc4b5959..74d8db2c 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -527,13 +527,17 @@ public function enqueue_scripts() {
 
 		wp_enqueue_style( $this->plugin->slug );
 		wp_enqueue_script( $this->plugin->slug );
+		if ( $this->snapshot ) {
+			$post = $this->snapshot->post();
+			$this->override_post_date_default_data( $post );
+		}
 
 		// Script data array.
 		$exports = apply_filters( 'customize_snapshots_export_data', array(
 			'action' => self::AJAX_ACTION,
 			'uuid' => $this->snapshot ? $this->snapshot->uuid() : self::generate_uuid(),
-			'editLink' => $this->snapshot ? get_edit_post_link( $this->snapshot->post(), 'raw' ) : '',
-			'snapshotPublishDate' => $this->snapshot ? $this->snapshot->post()->post_date_gmt : '',
+			'editLink' => $this->snapshot ? get_edit_post_link( $post, 'raw' ) : '',
+			'snapshotPublishDate' => $this->snapshot ? $post->post_date_gmt : '',
 			'currentUserCanPublish' => current_user_can( 'customize_publish' ),
 			'i18n' => array(
 				'saveButton' => __( 'Save', 'customize-snapshots' ),
@@ -1076,11 +1080,25 @@ public function render_templates() {
 			<div id="customize-schedule-box" class="accordion-section">
 				<div class="accordion-section-title">
 					<span class="preview-notice"><strong class="panel-title site-title"><?php esc_html_e( 'Schedule Snapshot', 'customize-snapshots' ); ?></strong></span>
-					<# if ( data.description ) { #>
-						<span class="description customize-control-description">
-							{{ data.description }}
-						</span>
-					<# } #>
+					<span class="description customize-control-description">
+						<?php
+						$tz_string = get_option( 'timezone_string' );
+						if ( $tz_string ) {
+							$tz = new \DateTimezone( $tz_string );
+							$formatted_gmt_offset = $this->format_gmt_offset( $tz->getOffset( new \DateTime() ) / 3600 );
+							$tz_name = str_replace( '_', ' ', $tz->getName() );
+
+							/* translators: 1: timezone name, 2: gmt offset  */
+							$date_control_description = sprintf( __( 'This site\'s dates are in the %1$s timezone (currently UTC%2$s).', 'customize-snapshots' ), $tz_name, $formatted_gmt_offset );
+						} else {
+							$formatted_gmt_offset = $this->format_gmt_offset( get_option( 'gmt_offset' ) );
+
+							/* translators: %s: gmt offset  */
+							$date_control_description = sprintf( __( 'Dates are in UTC%s.', 'customize-snapshots' ), $formatted_gmt_offset );
+						}
+						?>
+						<span class="timezone-info"><?php echo esc_html( $date_control_description ); ?></span>
+					</span>
 					<a href={{ data.editLink }} class="dashicons dashicons-edit" aria-expanded="false"></a>
 				</div>
 				<div class="customize-snapshot-control">
@@ -1095,14 +1113,15 @@ public function render_templates() {
 									value = choice.value;
 									}
 									#>
-								<option value="{{ value }}">{{ text }}</option>
+								<option value="{{ value }}" <# if (choice.value == data.month) { #>
+										selected="selected"
+										<# } #>>{{ text }}</option>
 							<# } ); #>
 						</select>
-
-						<input type="number" size="2" maxlength="2" autocomplete="off" class="date-input day" data-component="day" min="1" max="31" />,
-						<input type="number" size="4" maxlength="4" autocomplete="off" class="date-input year" data-component="year" min="<?php echo esc_attr( date( 'Y' ) ); ?>" max="9999" />
-						@ <input type="number" size="2" maxlength="2" autocomplete="off" class="date-input hour" data-component="hour" min="0" max="23" />:<?php
-						?><input type="number" size="2" maxlength="2" autocomplete="off" class="date-input minute" data-component="minute" min="0" max="59" />
+						<input type="number" size="2" maxlength="2" autocomplete="off" class="date-input day" data-component="day" min="1" max="31" value="{{ data.day }}" />,
+						<input type="number" size="4" maxlength="4" autocomplete="off" class="date-input year" data-component="year" min="<?php echo esc_attr( date( 'Y' ) ); ?>" value="{{ data.year }}" max="9999" />
+						@ <input type="number" size="2" maxlength="2" autocomplete="off" class="date-input hour" data-component="hour" min="0" max="23" value="{{ data.hour }}" />:<?php
+						?><input type="number" size="2" maxlength="2" autocomplete="off" class="date-input minute" data-component="minute" min="0" max="59" value="{{ data.minute }}" />
 				</div>
 			</div>
 		</script>
@@ -1127,6 +1146,28 @@ public function render_templates() {
 		<?php
 	}
 
+
+	/**
+	 * Format GMT Offset.
+	 *
+	 * @see wp_timezone_choice()
+	 * @param float $offset Offset in hours.
+	 * @return string Formatted offset.
+	 */
+	public function format_gmt_offset( $offset ) {
+		if ( 0 <= $offset ) {
+			$formatted_offset = '+' . (string) $offset;
+		} else {
+			$formatted_offset = (string) $offset;
+		}
+		$formatted_offset = str_replace(
+			array( '.25', '.5', '.75' ),
+			array( ':15', ':30', ':45' ),
+			$formatted_offset
+		);
+		return $formatted_offset;
+	}
+
 	/**
 	 * Generate options for the month Select.
 	 *
@@ -1149,4 +1190,30 @@ public function get_month_choices() {
 		}
 		return array( 'month_choices' => $months );
 	}
+
+	/**
+	 * Override default date values to a post.
+	 *
+	 * @param \WP_Post $post Post.
+	 * @return \WP_Post Object if the post data did not apply.
+	 */
+	public function override_post_date_default_data( \WP_Post &$post ) {
+		if ( ! is_array( $post ) ) {
+			// Make sure that empty dates are not used in case of setting invalidity.
+			$empty_date = '0000-00-00 00:00:00';
+			if ( $empty_date === $post->post_date ) {
+				$post->post_date = current_time( 'mysql', false );
+			}
+			if ( $empty_date === $post->post_date_gmt ) {
+				$post->post_date_gmt = current_time( 'mysql', true );
+			}
+			if ( $empty_date === $post->post_modified ) {
+				$post->post_modified = current_time( 'mysql', false );
+			}
+			if ( $empty_date === $post->post_modified_gmt ) {
+				$post->post_modified_gmt = current_time( 'mysql', true );
+			}
+		}
+		return $post;
+	}
 }

From c744de879e214ef225a910651c5696ac876340d6 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Tue, 2 Aug 2016 13:25:16 +0530
Subject: [PATCH 04/59] Update publish date from update call and add js update
 function to update UI

---
 js/customize-snapshots.js                | 57 +++++++++++++++++++-----
 php/class-customize-snapshot-manager.php |  2 +-
 2 files changed, 46 insertions(+), 13 deletions(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index dc64e5ee..5efe655a 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -154,21 +154,25 @@
 
 	component.dateComponentInputs = {};
 
+	component.snapshotScheduleBox = {};
+
 	component.addSlideDown = function slideDown() {
 		var snapshotScheduleBoxTemplate = wp.template( 'snapshot-schedule-accordion' ),
-			snapshotScheduleBox;
+			snapshotScheduleBox = component.snapshotScheduleBox;
 		component.snapshotSlideDownToggle.click( function( e ) {
 			var customizeInfo = $( '#customize-info' );
-			if ( ! snapshotScheduleBox ) {
+			if ( _.isEmpty( snapshotScheduleBox ) ) {
 				component.data = _.extend( component.data, component.parseDateTime( component.data.snapshotPublishDate ) );
 				snapshotScheduleBox = $( $.trim( snapshotScheduleBoxTemplate( component.data ) ) );
 				component.dateInputs = snapshotScheduleBox.find( '.date-input' );
 				component.dateInputs.each( function() {
-						var input = $( this ), componentName;
-						componentName = input.data( 'component' );
-						component.dateComponentInputs[ componentName ] = input;
+					var input = $( this ), componentName;
+					componentName = input.data( 'component' );
+					component.dateComponentInputs[componentName] = input;
 				} );
 				snapshotScheduleBox.insertBefore( customizeInfo );
+				component.snapshotScheduleBox = snapshotScheduleBox;
+				component.snapshotEditLink = snapshotScheduleBox.find( 'a' );
 			} else {
 
 				// Todo need to update in case of dynamic section.
@@ -176,6 +180,39 @@
 			}
 			e.preventDefault();
 		} );
+
+		api.state( 'snapshot-saved' ).bind( function( saved ) {
+			if ( saved ) {
+				component.updateSnapshotScheduleBox();
+			}
+		} );
+
+		api.state( 'saved' ).bind( function( saved ) {
+			if ( saved && ! _.isEmpty( component.snapshotScheduleBox ) ) {
+				component.snapshotScheduleBox.hide();
+			}
+		} );
+
+		api.state( 'snapshot-exists' ).bind( function( exists ) {
+			if ( exists && ! _.isEmpty( component.snapshotScheduleBox ) ) {
+				component.updateSnapshotScheduleBox();
+			}
+		} );
+
+	};
+
+	component.updateSnapshotScheduleBox = function updateSnapshotScheduleBox() {
+		var parsed;
+		if ( _.isEmpty( component.snapshotScheduleBox ) ) {
+			return;
+		}
+
+		// Update date controls.
+		component.snapshotEditLink.attr( 'href', component.data.editLink );
+		parsed = component.parseDateTime( component.data.snapshotPublishDate );
+		_.each( component.snapshotScheduleBox, function populateInput( node, component ) {
+			$( node ).val( parsed[component] );
+		} );
 	};
 
 	/**
@@ -203,7 +240,6 @@
 
 		snapshotSlideDownToggle = $( $.trim( snapshotSlideDownToggleTemplate( {} ) ) );
 		snapshotSlideDownToggle.insertAfter( snapshotButton );
-		snapshotEditLink = snapshotSlideDownToggle.find( 'a' );
 		component.snapshotSlideDownToggle = snapshotSlideDownToggle;
 		if ( ! component.data.editLink ) {
 			snapshotSlideDownToggle.hide();
@@ -214,9 +250,6 @@
 
 		api.state( 'snapshot-saved' ).bind( function( saved ) {
 			snapshotButton.prop( 'disabled', saved );
-			if ( saved ) {
-				snapshotEditLink.attr( 'href', component.data.editLink );
-			}
 		} );
 
 		api.state( 'saved' ).bind( function( saved ) {
@@ -233,9 +266,6 @@
 			if ( exists ) {
 				buttonText = component.data.i18n.updateButton;
 				permsMsg = component.data.i18n.permsMsg.update;
-				if ( component.data.editLink ) {
-					snapshotEditLink.attr( 'href', component.data.editLink );
-				}
 			} else {
 				buttonText = component.data.i18n.saveButton;
 				permsMsg = component.data.i18n.permsMsg.save;
@@ -336,6 +366,9 @@
 			if ( response.edit_link ) {
 				component.data.editLink = response.edit_link;
 			}
+			if ( response.snapshot_publish_date ) {
+				component.data.snapshotPublishDate = response.snapshot_publish_date;
+			}
 
 			// @todo Remove privateness from _handleSettingValidities in Core.
 			if ( api._handleSettingValidities && response.setting_validities ) {
diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index 74d8db2c..4618ac04 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -943,7 +943,7 @@ function( $value ) {
 		$post = $this->snapshot->post();
 		if ( $post ) {
 			$data['edit_link'] = get_edit_post_link( $post, 'raw' );
-			$data['snapshotPublishDate'] = $post->post_date_gmt;
+			$data['snapshot_publish_date'] = $post->post_date_gmt;
 		}
 
 		if ( is_wp_error( $r ) ) {

From 9766502d153601c17fc874daa332082a003df8ad Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Tue, 2 Aug 2016 21:37:27 +0530
Subject: [PATCH 05/59] Working prototype for scheduling UI in customizer

---
 js/customize-snapshots.js                | 108 +++++++++++++++++++++--
 php/class-customize-snapshot-manager.php |  27 ++++--
 php/class-post-type.php                  |   4 +
 3 files changed, 127 insertions(+), 12 deletions(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 5efe655a..b5d4c1e8 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -12,7 +12,17 @@
 
 	component = api.Snapshots;
 
-	component.data = {};
+	component.data = {
+		action: '',
+		uuid: '',
+		editLink: '',
+		snapshotPublishDate: '',
+		currentUserCanPublish: '',
+		initialServerDate: '',
+		initialServerTimestamp: 0,
+		initialClientTimestamp: ( new Date() ).valueOf(),
+		i18n: ''
+	};
 
 	if ( 'undefined' !== typeof _customizeSnapshots ) {
 		_.extend( component.data, _customizeSnapshots );
@@ -43,8 +53,19 @@
 			component.addSlideDown();
 
 			$( '#snapshot-save' ).on( 'click', function( event ) {
+				var scheduleDate;
 				event.preventDefault();
-				component.sendUpdateSnapshotRequest( { status: 'draft' } );
+				if ( $( this ).html() === component.data.i18n.scheduleButton && ! _.isEmpty( component.snapshotScheduleBox ) && component.getDateFromInputs() ) {
+
+					// Todo: If date is in future make status schedule and pass date with it. Or maybe handle that in php?
+					scheduleDate = component.getDateFromInputs();
+					component.sendUpdateSnapshotRequest( {
+						status: 'future',
+						publish_date: component.formatDate( scheduleDate )
+					} );
+				} else {
+					component.sendUpdateSnapshotRequest( { status: 'draft' } );
+				}
 			} );
 			$( '#snapshot-submit' ).on( 'click', function( event ) {
 				event.preventDefault();
@@ -162,6 +183,9 @@
 		component.snapshotSlideDownToggle.click( function( e ) {
 			var customizeInfo = $( '#customize-info' );
 			if ( _.isEmpty( snapshotScheduleBox ) ) {
+				if ( '0000-00-00 00:00:00' === component.data.snapshotPublishDate ) {
+					component.data.snapshotPublishDate = component.getCurrentTime();
+				}
 				component.data = _.extend( component.data, component.parseDateTime( component.data.snapshotPublishDate ) );
 				snapshotScheduleBox = $( $.trim( snapshotScheduleBoxTemplate( component.data ) ) );
 				component.dateInputs = snapshotScheduleBox.find( '.date-input' );
@@ -173,6 +197,22 @@
 				snapshotScheduleBox.insertBefore( customizeInfo );
 				component.snapshotScheduleBox = snapshotScheduleBox;
 				component.snapshotEditLink = snapshotScheduleBox.find( 'a' );
+				component.dateInputs.on( 'input', function hydrateInputValues() {
+					var parsed, setComponentInputValue;
+
+					// Todo Check if date is in future?.
+					if ( '0000-00-00 00:00:00' === component.data.snapshotPublishDate ) {
+						parsed = component.parseDateTime( component.getCurrentTime() );
+						setComponentInputValue = function( value, inputName ) {
+							var input = component.dateComponentInputs[inputName];
+							if ( input && ! input.is( 'select' ) && '' === input.val() ) {
+								input.val( value );
+							}
+						};
+						_.each( parsed, setComponentInputValue );
+					}
+					component.populateSetting();
+				} );
 			} else {
 
 				// Todo need to update in case of dynamic section.
@@ -206,6 +246,9 @@
 		if ( _.isEmpty( component.snapshotScheduleBox ) ) {
 			return;
 		}
+		if ( '0000-00-00 00:00:00' === component.data.snapshotPublishDate ) {
+			component.data.snapshotPublishDate = component.getCurrentTime();
+		}
 
 		// Update date controls.
 		component.snapshotEditLink.attr( 'href', component.data.editLink );
@@ -349,14 +392,15 @@
 			options
 		);
 
+		data = _.extend( args, {
+			nonce: api.settings.nonce.snapshot,
+			customize_snapshot_uuid: component.data.uuid
+		} );
+
 		data = _.extend(
 			{},
 			api.previewer.query(),
-			{
-				nonce: api.settings.nonce.snapshot,
-				customize_snapshot_uuid: component.data.uuid,
-				status: args.status
-			}
+			data
 		);
 		request = wp.ajax.post( 'customize_update_snapshot', data );
 
@@ -523,6 +567,56 @@
 		return formattedDate;
 	};
 
+	/**
+	 * Populate setting value from the inputs.
+	 *
+	 * @returns {boolean} Whether the date inputs currently represent a valid date.
+	 */
+	component.populateSetting = function populateSetting() {
+		var date, remainingTime, save;
+		date = component.getDateFromInputs();
+		if ( ! date ) {
+			return false;
+		} else {
+			remainingTime = ( new Date( date ) ).valueOf();
+			remainingTime -= ( new Date( component.getCurrentTime() ) ).valueOf();
+			remainingTime = Math.ceil( remainingTime / 1000 );
+			save = $( '#snapshot-save' );
+			if ( remainingTime > 0 ) {
+
+				// Change update button to schedule.
+				if ( save.length ) {
+					save.html( component.data.i18n.scheduleButton );
+					save.prop( 'disabled', false );
+				}
+			} else {
+				if ( save.length ) {
+					save.html( component.data.i18n.updateButton );
+
+					//Todo If snapshot don't have changes make button disable.
+				}
+			}
+			date.setSeconds( 0 );
+			component.data.snapshotPublishDate = component.formatDate( date );
+			return true;
+		}
+	};
+
+	/**
+	 * Get current date/time in the site's timezone, as does the current_time( 'mysql', false ) function in PHP.
+	 *
+	 * @returns {string} Current datetime string.
+	 */
+	component.getCurrentTime = function getCurrentTime() {
+		var currentDate, currentTimestamp, timestampDifferential;
+		currentTimestamp = ( new Date() ).valueOf();
+		currentDate = new Date( component.data.initialServerDate );
+		timestampDifferential = currentTimestamp - component.data.initialClientTimestamp;
+		timestampDifferential += component.data.initialClientTimestamp - component.data.initialServerTimestamp;
+		currentDate.setTime( currentDate.getTime() + timestampDifferential );
+		return component.formatDate( currentDate );
+	};
+
 	component.init();
 
 } )( wp.customize, jQuery );
diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index 4618ac04..6d8179d9 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -537,11 +537,14 @@ public function enqueue_scripts() {
 			'action' => self::AJAX_ACTION,
 			'uuid' => $this->snapshot ? $this->snapshot->uuid() : self::generate_uuid(),
 			'editLink' => $this->snapshot ? get_edit_post_link( $post, 'raw' ) : '',
-			'snapshotPublishDate' => $this->snapshot ? $post->post_date_gmt : '',
+			'snapshotPublishDate' => $this->snapshot ? $post->post_date : '',
 			'currentUserCanPublish' => current_user_can( 'customize_publish' ),
+			'initialServerDate' => current_time( 'mysql', false ),
+			'initialServerTimestamp' => floor( microtime( true ) * 1000 ),
 			'i18n' => array(
 				'saveButton' => __( 'Save', 'customize-snapshots' ),
 				'updateButton' => __( 'Update', 'customize-snapshots' ),
+				'scheduleButton' => __( 'Schedule', 'customize-snapshots' ),
 				'submit' => __( 'Submit', 'customize-snapshots' ),
 				'submitted' => __( 'Submitted', 'customize-snapshots' ),
 				'publish' => __( 'Publish', 'customize-snapshots' ),
@@ -886,10 +889,19 @@ public function handle_update_snapshot_request() {
 		} else {
 			$status = 'draft';
 		}
-		if ( ! in_array( $status, array( 'draft', 'pending' ), true ) ) {
+		if ( ! in_array( $status, array( 'draft', 'pending', 'future' ), true ) ) {
 			status_header( 400 );
 			wp_send_json_error( 'bad_status' );
 		}
+		$publish_date = isset( $_POST['publish_date'] ) ? $_POST['publish_date'] : '';
+		if ( 'future' === $status ) {
+			$publish_date_obj = new \DateTime( $publish_date );
+			$current_date = new \DateTime();
+			if ( empty( $publish_date ) || ! $publish_date_obj || $publish_date > $current_date ) {
+				status_header( 400 );
+				wp_send_json_error( 'bad_schedule_time' );
+			}
+		}
 
 		// Prevent attempting to modify a "locked" snapshot (a published one).
 		$post = $this->snapshot->post();
@@ -935,10 +947,15 @@ function( $value ) {
 			$data['errors'] = $this->prepare_errors_for_response( $r['errors'] );
 			wp_send_json_error( $data );
 		}
-
-		$r = $this->snapshot->save( array(
+		$args = array(
 			'status' => $status,
-		) );
+		);
+
+		if ( isset( $publish_date_obj ) && 'future' === $status ) {
+			$args['post_date'] = $publish_date_obj->format( 'Y-m-d H:i:s' );
+			$args['edit_date'] = current_time( 'mysql' );
+		}
+		$r = $this->snapshot->save( $args );
 
 		$post = $this->snapshot->post();
 		if ( $post ) {
diff --git a/php/class-post-type.php b/php/class-post-type.php
index e137b988..575cf48a 100644
--- a/php/class-post-type.php
+++ b/php/class-post-type.php
@@ -508,6 +508,10 @@ public function save( array $args ) {
 			),
 		);
 		if ( ! empty( $args['status'] ) ) {
+			if ( isset( $args['post_date'], $post_arr['edit_date'] ) && 'future' === $args['status'] ) {
+				$post_arr['post_date'] = $args['post_date'];
+				$post_arr['edit_date'] = $args['edit_date'];
+			}
 			if ( ! get_post_status_object( $args['status'] ) ) {
 				return new \WP_Error( 'bad_status' );
 			}

From 95e05739734418a5c7ab640d1fb8b30ef9f11ac6 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Tue, 2 Aug 2016 21:47:59 +0530
Subject: [PATCH 06/59] Fix scheduling edit_date issue

---
 php/class-post-type.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/php/class-post-type.php b/php/class-post-type.php
index 575cf48a..349f6b6a 100644
--- a/php/class-post-type.php
+++ b/php/class-post-type.php
@@ -508,7 +508,7 @@ public function save( array $args ) {
 			),
 		);
 		if ( ! empty( $args['status'] ) ) {
-			if ( isset( $args['post_date'], $post_arr['edit_date'] ) && 'future' === $args['status'] ) {
+			if ( isset( $args['post_date'], $args['edit_date'] ) && 'future' === $args['status'] ) {
 				$post_arr['post_date'] = $args['post_date'];
 				$post_arr['edit_date'] = $args['edit_date'];
 			}

From 6492b665c31cc152f738715e1ae18ca3d85561d9 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Wed, 3 Aug 2016 16:38:07 +0530
Subject: [PATCH 07/59] Disable update button if snapshot schedule date changed

---
 js/customize-snapshots.js | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index b5d4c1e8..1706f292 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -21,7 +21,8 @@
 		initialServerDate: '',
 		initialServerTimestamp: 0,
 		initialClientTimestamp: ( new Date() ).valueOf(),
-		i18n: ''
+		i18n: {},
+		snapshotHaveChange: false
 	};
 
 	if ( 'undefined' !== typeof _customizeSnapshots ) {
@@ -227,6 +228,10 @@
 			}
 		} );
 
+		api.bind( 'change', function() {
+			component.data.snapshotHaveChange = true;
+		} );
+
 		api.state( 'saved' ).bind( function( saved ) {
 			if ( saved && ! _.isEmpty( component.snapshotScheduleBox ) ) {
 				component.snapshotScheduleBox.hide();
@@ -592,8 +597,7 @@
 			} else {
 				if ( save.length ) {
 					save.html( component.data.i18n.updateButton );
-
-					//Todo If snapshot don't have changes make button disable.
+					save.prop( 'disabled', ! component.data.snapshotHaveChange );
 				}
 			}
 			date.setSeconds( 0 );

From 7deb45669e2ce1a91b80dc625ef69b2b18c94c65 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Wed, 3 Aug 2016 16:57:01 +0530
Subject: [PATCH 08/59] Schedule request only if schedule date is in future

---
 js/customize-snapshots.js | 27 +++++++++++++++++++--------
 1 file changed, 19 insertions(+), 8 deletions(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 1706f292..5e900342 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -56,9 +56,7 @@
 			$( '#snapshot-save' ).on( 'click', function( event ) {
 				var scheduleDate;
 				event.preventDefault();
-				if ( $( this ).html() === component.data.i18n.scheduleButton && ! _.isEmpty( component.snapshotScheduleBox ) && component.getDateFromInputs() ) {
-
-					// Todo: If date is in future make status schedule and pass date with it. Or maybe handle that in php?
+				if ( $( this ).html() === component.data.i18n.scheduleButton && ! _.isEmpty( component.snapshotScheduleBox ) && component.getDateFromInputs() && component.isScheduleDateFuture() ) {
 					scheduleDate = component.getDateFromInputs();
 					component.sendUpdateSnapshotRequest( {
 						status: 'future',
@@ -578,16 +576,13 @@
 	 * @returns {boolean} Whether the date inputs currently represent a valid date.
 	 */
 	component.populateSetting = function populateSetting() {
-		var date, remainingTime, save;
+		var date, save;
 		date = component.getDateFromInputs();
 		if ( ! date ) {
 			return false;
 		} else {
-			remainingTime = ( new Date( date ) ).valueOf();
-			remainingTime -= ( new Date( component.getCurrentTime() ) ).valueOf();
-			remainingTime = Math.ceil( remainingTime / 1000 );
 			save = $( '#snapshot-save' );
-			if ( remainingTime > 0 ) {
+			if ( component.isScheduleDateFuture() ) {
 
 				// Change update button to schedule.
 				if ( save.length ) {
@@ -606,6 +601,22 @@
 		}
 	};
 
+	/**
+	 * Check if snapshot schedule date is in future date
+	 * @returns {boolean}
+	 */
+	component.isScheduleDateFuture = function isScheduleDateFuture() {
+		var date, remainingTime;
+		date = component.getDateFromInputs();
+		if ( ! date ) {
+			return false;
+		}
+		remainingTime = ( new Date( date ) ).valueOf();
+		remainingTime -= ( new Date( component.getCurrentTime() ) ).valueOf();
+		remainingTime = Math.ceil( remainingTime / 1000 );
+		return remainingTime > 0;
+	};
+
 	/**
 	 * Get current date/time in the site's timezone, as does the current_time( 'mysql', false ) function in PHP.
 	 *

From 136ed7d808094d667099de854f1d6af57dbc7bd9 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Wed, 3 Aug 2016 16:59:11 +0530
Subject: [PATCH 09/59] rename variable to isSnapshotHasUnsavedChanges

---
 js/customize-snapshots.js | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 5e900342..3c458c15 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -22,7 +22,7 @@
 		initialServerTimestamp: 0,
 		initialClientTimestamp: ( new Date() ).valueOf(),
 		i18n: {},
-		snapshotHaveChange: false
+		isSnapshotHasUnsavedChanges: false
 	};
 
 	if ( 'undefined' !== typeof _customizeSnapshots ) {
@@ -227,7 +227,7 @@
 		} );
 
 		api.bind( 'change', function() {
-			component.data.snapshotHaveChange = true;
+			component.data.isSnapshotHasUnsavedChanges = true;
 		} );
 
 		api.state( 'saved' ).bind( function( saved ) {
@@ -592,7 +592,7 @@
 			} else {
 				if ( save.length ) {
 					save.html( component.data.i18n.updateButton );
-					save.prop( 'disabled', ! component.data.snapshotHaveChange );
+					save.prop( 'disabled', ! component.data.isSnapshotHasUnsavedChanges );
 				}
 			}
 			date.setSeconds( 0 );

From 9e043955514ee464f037845010b18c0ee39b8ebf Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Wed, 3 Aug 2016 18:22:05 +0530
Subject: [PATCH 10/59] Add populateInputs and refactor populateSetting

---
 js/customize-snapshots.js | 42 +++++++++++++++++++++++++--------------
 1 file changed, 27 insertions(+), 15 deletions(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 3c458c15..05767780 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -197,21 +197,11 @@
 				component.snapshotScheduleBox = snapshotScheduleBox;
 				component.snapshotEditLink = snapshotScheduleBox.find( 'a' );
 				component.dateInputs.on( 'input', function hydrateInputValues() {
-					var parsed, setComponentInputValue;
-
-					// Todo Check if date is in future?.
-					if ( '0000-00-00 00:00:00' === component.data.snapshotPublishDate ) {
-						parsed = component.parseDateTime( component.getCurrentTime() );
-						setComponentInputValue = function( value, inputName ) {
-							var input = component.dateComponentInputs[inputName];
-							if ( input && ! input.is( 'select' ) && '' === input.val() ) {
-								input.val( value );
-							}
-						};
-						_.each( parsed, setComponentInputValue );
-					}
 					component.populateSetting();
 				} );
+				component.dateInputs.on( 'blur', function hydrateInputValues() {
+					component.populateInputs();
+				} );
 			} else {
 
 				// Todo need to update in case of dynamic section.
@@ -570,6 +560,30 @@
 		return formattedDate;
 	};
 
+	/**
+	 * Populate inputs from the setting value, if none of them are currently focused.
+	 *
+	 * @returns {boolean} Whether the inputs were populated.
+	 */
+	component.populateInputs = function populateInputs() {
+		var parsed, setComponentInputValue;
+		if ( component.dateInputs.is( ':focus' ) || '0000-00-00 00:00:00' === component.data.snapshotPublishDate ) {
+			return false;
+		}
+		parsed = component.parseDateTime( component.data.snapshotPublishDate );
+		if ( ! parsed ) {
+			return false;
+		}
+		setComponentInputValue = function( value, inputName ) {
+			var input = component.dateComponentInputs[inputName];
+			if ( input && ! input.is( 'select' ) && '' === input.val() ) {
+				input.val( value );
+			}
+		};
+		_.each( parsed, setComponentInputValue );
+		return true;
+	};
+
 	/**
 	 * Populate setting value from the inputs.
 	 *
@@ -596,8 +610,6 @@
 				}
 			}
 			date.setSeconds( 0 );
-			component.data.snapshotPublishDate = component.formatDate( date );
-			return true;
 		}
 	};
 

From 132dc8a1c7dec506d2927c8ad4c8ad047c75cb21 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Wed, 3 Aug 2016 20:23:56 +0530
Subject: [PATCH 11/59] Change button text to schedule if snapshot post status
 is schedule

---
 js/customize-snapshots.js                | 14 ++++++++++++--
 php/class-customize-snapshot-manager.php |  1 +
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 05767780..d44ecaed 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -17,6 +17,7 @@
 		uuid: '',
 		editLink: '',
 		snapshotPublishDate: '',
+		snapshotStatus: '',
 		currentUserCanPublish: '',
 		initialServerDate: '',
 		initialServerTimestamp: 0,
@@ -260,12 +261,21 @@
 		var header = $( '#customize-header-actions' ),
 			publishButton = header.find( '#save' ),
 			snapshotSlideDownToggleTemplate = wp.template( 'snapshot-toggle-button' ),
-			snapshotButton, submitButton, data, setPreviewLinkHref, snapshotSlideDownToggle, snapshotEditLink;
+			snapshotButton, submitButton, data, setPreviewLinkHref, snapshotSlideDownToggle, snapshotButtonText;
 
 		// Save/update button.
 		snapshotButton = wp.template( 'snapshot-save' );
+		if ( api.state( 'snapshot-exists' ).get() ) {
+			if ( 'future' === component.data.snapshotStatus ) {
+				snapshotButtonText = component.data.i18n.scheduleButton;
+			} else {
+				snapshotButtonText = component.data.i18n.updateButton;
+			}
+		} else {
+			snapshotButtonText = component.data.i18n.saveButton;
+		}
 		data = {
-			buttonText: api.state( 'snapshot-exists' ).get() ? component.data.i18n.updateButton : component.data.i18n.saveButton
+			buttonText: snapshotButtonText
 		};
 		snapshotButton = $( $.trim( snapshotButton( data ) ) );
 		if ( ! component.data.currentUserCanPublish ) {
diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index 6d8179d9..aaba0dbb 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -538,6 +538,7 @@ public function enqueue_scripts() {
 			'uuid' => $this->snapshot ? $this->snapshot->uuid() : self::generate_uuid(),
 			'editLink' => $this->snapshot ? get_edit_post_link( $post, 'raw' ) : '',
 			'snapshotPublishDate' => $this->snapshot ? $post->post_date : '',
+			'snapshotStatus' => $this->snapshot ? $post->post_status : '',
 			'currentUserCanPublish' => current_user_can( 'customize_publish' ),
 			'initialServerDate' => current_time( 'mysql', false ),
 			'initialServerTimestamp' => floor( microtime( true ) * 1000 ),

From 8f20ab70e55870ec689c5a62cb662449a6c61b75 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Wed, 3 Aug 2016 23:58:51 +0530
Subject: [PATCH 12/59] Add remain time to publish text

---
 css/customize-snapshots.css              |  5 +++-
 js/customize-snapshots.js                | 30 ++++++++++++++++++++++--
 php/class-customize-snapshot-manager.php | 26 ++++++++++++++++++--
 3 files changed, 56 insertions(+), 5 deletions(-)

diff --git a/css/customize-snapshots.css b/css/customize-snapshots.css
index 39d3ef12..6a02926a 100644
--- a/css/customize-snapshots.css
+++ b/css/customize-snapshots.css
@@ -107,7 +107,7 @@
 	padding: 10px;
 }
 
-#customize-schedule-box span{
+#customize-schedule-box div.preview-notice{
 	font-size: 13px;
 	line-height: 24px;
 }
@@ -121,4 +121,7 @@
 	left: -100%;
 	width: 100%;
 	position: relative;
+}
+#customize-schedule-box .snapshot-description{
+	font-style: normal;
 }
\ No newline at end of file
diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index d44ecaed..fb5384d2 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -188,21 +188,25 @@
 				}
 				component.data = _.extend( component.data, component.parseDateTime( component.data.snapshotPublishDate ) );
 				snapshotScheduleBox = $( $.trim( snapshotScheduleBoxTemplate( component.data ) ) );
+				snapshotScheduleBox.insertBefore( customizeInfo );
 				component.dateInputs = snapshotScheduleBox.find( '.date-input' );
+				component.scheduledCountdownContainer = snapshotScheduleBox.find( '.scheduled-countdown' );
+				component.scheduledCountdownTemplate = wp.template( 'snapshot-scheduled-countdown' );
+				component.snapshotScheduleBox = snapshotScheduleBox;
 				component.dateInputs.each( function() {
 					var input = $( this ), componentName;
 					componentName = input.data( 'component' );
 					component.dateComponentInputs[componentName] = input;
 				} );
-				snapshotScheduleBox.insertBefore( customizeInfo );
-				component.snapshotScheduleBox = snapshotScheduleBox;
 				component.snapshotEditLink = snapshotScheduleBox.find( 'a' );
 				component.dateInputs.on( 'input', function hydrateInputValues() {
 					component.populateSetting();
 				} );
 				component.dateInputs.on( 'blur', function hydrateInputValues() {
 					component.populateInputs();
+					component.populateSetting();
 				} );
+				component.updateScheduledCountdown();
 			} else {
 
 				// Todo need to update in case of dynamic section.
@@ -619,6 +623,7 @@
 					save.prop( 'disabled', ! component.data.isSnapshotHasUnsavedChanges );
 				}
 			}
+			component.updateScheduledCountdown();
 			date.setSeconds( 0 );
 		}
 	};
@@ -654,6 +659,27 @@
 		return component.formatDate( currentDate );
 	};
 
+	/**
+	 * Update the scheduled countdown.
+	 *
+	 * Hides countdown if post_status is not already future.
+	 * Toggles the countdown if there is no remaining time.
+	 *
+	 * @returns {void}
+	 */
+	component.updateScheduledCountdown = function updateScheduledCountdown() {
+		var remainingTime;
+		remainingTime = component.getDateFromInputs().valueOf();
+		remainingTime -= ( new Date( component.getCurrentTime() ) ).valueOf();
+		remainingTime = Math.ceil( remainingTime / 1000 );
+		if ( remainingTime > 0 ) {
+			component.scheduledCountdownContainer.text( component.scheduledCountdownTemplate( { remainingTime: remainingTime } ) );
+			component.scheduledCountdownContainer.show();
+		} else {
+			component.scheduledCountdownContainer.hide();
+		}
+	};
+
 	component.init();
 
 } )( wp.customize, jQuery );
diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index aaba0dbb..0500af30 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -1097,8 +1097,8 @@ public function render_templates() {
 		<script type="text/html" id="tmpl-snapshot-schedule-accordion">
 			<div id="customize-schedule-box" class="accordion-section">
 				<div class="accordion-section-title">
-					<span class="preview-notice"><strong class="panel-title site-title"><?php esc_html_e( 'Schedule Snapshot', 'customize-snapshots' ); ?></strong></span>
-					<span class="description customize-control-description">
+					<div class="preview-notice"><strong class="panel-title site-title"><?php esc_html_e( 'Schedule Snapshot', 'customize-snapshots' ); ?></strong></div>
+					<span class="description snapshot-description">
 						<?php
 						$tz_string = get_option( 'timezone_string' );
 						if ( $tz_string ) {
@@ -1115,6 +1115,7 @@ public function render_templates() {
 							$date_control_description = sprintf( __( 'Dates are in UTC%s.', 'customize-snapshots' ), $formatted_gmt_offset );
 						}
 						?>
+						<span class="scheduled-countdown"></span>
 						<span class="timezone-info"><?php echo esc_html( $date_control_description ); ?></span>
 					</span>
 					<a href={{ data.editLink }} class="dashicons dashicons-edit" aria-expanded="false"></a>
@@ -1144,6 +1145,27 @@ public function render_templates() {
 			</div>
 		</script>
 
+		<script id="tmpl-snapshot-scheduled-countdown" type="text/html">
+			<# if ( data.remainingTime < 2 * 60 ) { #>
+			<?php esc_html_e( 'This is scheduled for publishing in about a minute.', 'customize-snapshots' ); ?>
+			<# } else if ( data.remainingTime < 60 * 60 ) { #>
+			<?php
+			/* translators: %s is a placeholder for the Underscore template var */
+			echo sprintf( esc_html__( 'This snapshot is scheduled for publishing in about %s minutes.', 'customize-snapshots' ), '{{ Math.ceil( data.remainingTime / 60 ) }}' );
+			?>
+			<# } else if ( data.remainingTime < 24 * 60 * 60 ) { #>
+			<?php
+			/* translators: %s is a placeholder for the Underscore template var */
+			echo sprintf( esc_html__( 'This snapshot is scheduled for publishing in about %s hours.', 'customize-snapshots' ), '{{ Math.round( data.remainingTime / 60 / 60 * 10 ) / 10 }}' );
+			?>
+			<# } else { #>
+				<?php
+				/* translators: %s is a placeholder for the Underscore template var */
+				echo sprintf( esc_html__( 'This snapshot is scheduled for publishing in about %s days.', 'customize-snapshots' ), '{{ Math.round( data.remainingTime / 60 / 60 / 24 * 10 ) / 10 }}' );
+				?>
+				<# } #>
+		</script>
+
 		<script type="text/html" id="tmpl-snapshot-save">
 			<button id="snapshot-save" class="button button-secondary">
 				{{ data.buttonText }}

From 0eba8996e73df3a1cd38def6e72ad7bb55a5f8ac Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Thu, 4 Aug 2016 00:45:28 +0530
Subject: [PATCH 13/59] Add rest button

---
 css/customize-snapshots.css              |  7 ++++++-
 js/customize-snapshots.js                | 23 ++++++++++++++++++++---
 php/class-customize-snapshot-manager.php |  7 +++++--
 3 files changed, 31 insertions(+), 6 deletions(-)

diff --git a/css/customize-snapshots.css b/css/customize-snapshots.css
index 6a02926a..d06ecb62 100644
--- a/css/customize-snapshots.css
+++ b/css/customize-snapshots.css
@@ -91,7 +91,7 @@
 #customize-schedule-box .accordion-section-title:after{
 	z-index: -1;
 }
-#customize-schedule-box a{
+#customize-schedule-box a.snapshot-edit-link{
 	position: absolute;
 	top: 4px;
 	right: 1px;
@@ -124,4 +124,9 @@
 }
 #customize-schedule-box .snapshot-description{
 	font-style: normal;
+}
+
+#customize-schedule-box .wrap-reset-time{
+	font-weight: normal;
+	font-size: 75%;
 }
\ No newline at end of file
diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index fb5384d2..5feb6eab 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -191,6 +191,8 @@
 				snapshotScheduleBox.insertBefore( customizeInfo );
 				component.dateInputs = snapshotScheduleBox.find( '.date-input' );
 				component.scheduledCountdownContainer = snapshotScheduleBox.find( '.scheduled-countdown' );
+				component.resetTimeButton = snapshotScheduleBox.find( '.reset-time' );
+				component.resetTimeWrap = snapshotScheduleBox.find( '.wrap-reset-time' );
 				component.scheduledCountdownTemplate = wp.template( 'snapshot-scheduled-countdown' );
 				component.snapshotScheduleBox = snapshotScheduleBox;
 				component.dateInputs.each( function() {
@@ -207,6 +209,11 @@
 					component.populateSetting();
 				} );
 				component.updateScheduledCountdown();
+				component.resetTimeButton.on( 'click', function( e ) {
+					component.updateSnapshotScheduleBox();
+					component.resetTimeWrap.hide();
+					e.preventDefault();
+				} );
 			} else {
 
 				// Todo need to update in case of dynamic section.
@@ -251,7 +258,7 @@
 		// Update date controls.
 		component.snapshotEditLink.attr( 'href', component.data.editLink );
 		parsed = component.parseDateTime( component.data.snapshotPublishDate );
-		_.each( component.snapshotScheduleBox, function populateInput( node, component ) {
+		_.each( component.dateComponentInputs, function populateInput( node, component ) {
 			$( node ).val( parsed[component] );
 		} );
 	};
@@ -604,18 +611,23 @@
 	 * @returns {boolean} Whether the date inputs currently represent a valid date.
 	 */
 	component.populateSetting = function populateSetting() {
-		var date, save;
+		var date, save, isScheduleDateUpdated;
 		date = component.getDateFromInputs();
 		if ( ! date ) {
 			return false;
 		} else {
 			save = $( '#snapshot-save' );
+			isScheduleDateUpdated = component.formatDate( date ) !== component.data.snapshotPublishDate;
 			if ( component.isScheduleDateFuture() ) {
 
 				// Change update button to schedule.
 				if ( save.length ) {
 					save.html( component.data.i18n.scheduleButton );
-					save.prop( 'disabled', false );
+					if ( isScheduleDateUpdated || component.data.isSnapshotHasUnsavedChanges ) {
+						save.prop( 'disabled', false );
+					} else {
+						save.prop( 'disabled', true );
+					}
 				}
 			} else {
 				if ( save.length ) {
@@ -625,6 +637,11 @@
 			}
 			component.updateScheduledCountdown();
 			date.setSeconds( 0 );
+			if ( isScheduleDateUpdated ) {
+				component.resetTimeWrap.show();
+			} else {
+				component.resetTimeWrap.hide();
+			}
 		}
 	};
 
diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index 0500af30..7f1a0d58 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -1097,7 +1097,10 @@ public function render_templates() {
 		<script type="text/html" id="tmpl-snapshot-schedule-accordion">
 			<div id="customize-schedule-box" class="accordion-section">
 				<div class="accordion-section-title">
-					<div class="preview-notice"><strong class="panel-title site-title"><?php esc_html_e( 'Schedule Snapshot', 'customize-snapshots' ); ?></strong></div>
+					<div class="preview-notice">
+						<strong class="panel-title site-title"><?php esc_html_e( 'Schedule Snapshot', 'customize-snapshots' ); ?></strong>
+						<span class="wrap-reset-time">(<a href="#" class="reset-time"><?php esc_html_e( 'Reset', 'customize-posts' ) ?></a>)</span>
+					</div>
 					<span class="description snapshot-description">
 						<?php
 						$tz_string = get_option( 'timezone_string' );
@@ -1118,7 +1121,7 @@ public function render_templates() {
 						<span class="scheduled-countdown"></span>
 						<span class="timezone-info"><?php echo esc_html( $date_control_description ); ?></span>
 					</span>
-					<a href={{ data.editLink }} class="dashicons dashicons-edit" aria-expanded="false"></a>
+					<a href={{ data.editLink }} class="dashicons dashicons-edit snapshot-edit-link" aria-expanded="false"></a>
 				</div>
 				<div class="customize-snapshot-control">
 					<# _.defaults( data, <?php echo wp_json_encode( $data ) ?> );

From d1fa9d52f6defa68c315497dc77f6b22774e8ba2 Mon Sep 17 00:00:00 2001
From: Weston Ruter <weston@xwp.co>
Date: Wed, 3 Aug 2016 22:09:32 -0700
Subject: [PATCH 14/59] Fix text domain

---
 php/class-customize-snapshot-manager.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index 7f1a0d58..50dfc48a 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -1099,7 +1099,7 @@ public function render_templates() {
 				<div class="accordion-section-title">
 					<div class="preview-notice">
 						<strong class="panel-title site-title"><?php esc_html_e( 'Schedule Snapshot', 'customize-snapshots' ); ?></strong>
-						<span class="wrap-reset-time">(<a href="#" class="reset-time"><?php esc_html_e( 'Reset', 'customize-posts' ) ?></a>)</span>
+						<span class="wrap-reset-time">(<a href="#" class="reset-time"><?php esc_html_e( 'Reset', 'customize-snapshots' ) ?></a>)</span>
 					</div>
 					<span class="description snapshot-description">
 						<?php

From 26399a1b4b5149b58c97a800cdcb44fdb95cf7ba Mon Sep 17 00:00:00 2001
From: Weston Ruter <weston@xwp.co>
Date: Wed, 3 Aug 2016 22:21:49 -0700
Subject: [PATCH 15/59] Install eslint so checks can run

---
 package.json | 41 +++++++++++++++++++++--------------------
 1 file changed, 21 insertions(+), 20 deletions(-)

diff --git a/package.json b/package.json
index 3ad081a1..086a2aa9 100644
--- a/package.json
+++ b/package.json
@@ -1,22 +1,23 @@
 {
-	"name": "customize-snapshots",
-	"title": "Customize Snapshots",
-	"homepage": "https://github.com/xwp/wp-customize-snapshots",
-	"repository": {
-		"type": "git",
-		"url": "https://github.com/xwp/wp-customize-snapshots.git"
-	},
-	"author": "XWP",
-	"license": "GPL-2.0+",
-	"devDependencies": {
-		"grunt": "~0.4.5",
-		"grunt-checktextdomain": "~1.0.0",
-		"grunt-contrib-clean": "~1.0.0",
-		"grunt-contrib-copy": "~1.0.0",
-		"grunt-contrib-cssmin": "~1.0.1",
-		"grunt-contrib-jshint": "~1.0.0",
-		"grunt-contrib-uglify": "~1.0.1",
-		"grunt-shell": "~1.3.0",
-		"grunt-wp-deploy": "^1.1.0"
-	}
+  "name": "customize-snapshots",
+  "title": "Customize Snapshots",
+  "homepage": "https://github.com/xwp/wp-customize-snapshots",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/xwp/wp-customize-snapshots.git"
+  },
+  "author": "XWP",
+  "license": "GPL-2.0+",
+  "devDependencies": {
+    "eslint": "^3.2.2",
+    "grunt": "~0.4.5",
+    "grunt-checktextdomain": "~1.0.0",
+    "grunt-contrib-clean": "~1.0.0",
+    "grunt-contrib-copy": "~1.0.0",
+    "grunt-contrib-cssmin": "~1.0.1",
+    "grunt-contrib-jshint": "~1.0.0",
+    "grunt-contrib-uglify": "~1.0.1",
+    "grunt-shell": "~1.3.0",
+    "grunt-wp-deploy": "^1.1.0"
+  }
 }

From 94d234b8b3eaea80db336c94bc7b3f8334a5f506 Mon Sep 17 00:00:00 2001
From: Weston Ruter <weston@xwp.co>
Date: Wed, 3 Aug 2016 22:28:04 -0700
Subject: [PATCH 16/59] Bump node.js to 5.x on Travis for sake of ESLint

---
 .travis.yml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/.travis.yml b/.travis.yml
index 003dcbac..276d86e3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,6 +4,9 @@ language:
     - php
     - node_js
 
+node_js:
+    - 5
+
 php:
     - 5.3
     - 7.0

From 6460b48d07fb3fcb496c2b4d78aea848952f0aec Mon Sep 17 00:00:00 2001
From: Weston Ruter <weston@xwp.co>
Date: Wed, 3 Aug 2016 22:37:23 -0700
Subject: [PATCH 17/59] Add nvm use 4 to Travis

---
 .travis.yml | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 276d86e3..afd32eaf 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,9 +4,6 @@ language:
     - php
     - node_js
 
-node_js:
-    - 5
-
 php:
     - 5.3
     - 7.0
@@ -19,6 +16,7 @@ env:
     - WP_VERSION=latest WP_MULTISITE=1
 
 install:
+    - nvm use 4
     - export DEV_LIB_PATH=dev-lib
     - if [ ! -e "$DEV_LIB_PATH" ] && [ -L .travis.yml ]; then export DEV_LIB_PATH=$( dirname $( readlink .travis.yml ) ); fi
     - source $DEV_LIB_PATH/travis.install.sh

From 345294a25c388cea88ae25a18b9bb2c96e73a689 Mon Sep 17 00:00:00 2001
From: Weston Ruter <weston@xwp.co>
Date: Wed, 3 Aug 2016 22:43:21 -0700
Subject: [PATCH 18/59] Do nvm install before nvm use

---
 .travis.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index afd32eaf..1dbd3883 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,7 +16,7 @@ env:
     - WP_VERSION=latest WP_MULTISITE=1
 
 install:
-    - nvm use 4
+    - nvm install 4 && nvm use 4
     - export DEV_LIB_PATH=dev-lib
     - if [ ! -e "$DEV_LIB_PATH" ] && [ -L .travis.yml ]; then export DEV_LIB_PATH=$( dirname $( readlink .travis.yml ) ); fi
     - source $DEV_LIB_PATH/travis.install.sh

From 6311e36085d8e4951bd4888dd22644089d6bba38 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Thu, 4 Aug 2016 12:22:12 +0530
Subject: [PATCH 19/59] Fix eslint issues

---
 js/customize-snapshots.js | 74 ++++++++++++++++++++++-----------------
 1 file changed, 42 insertions(+), 32 deletions(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 5feb6eab..232227d8 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -1,5 +1,7 @@
 /* global jQuery, _customizeSnapshots */
 /* eslint-disable no-extra-parens */
+/* eslint no-magic-numbers: ["error", { "ignore": [-1,0,1,1000] }] */
+/* eslint complexity: ["error", 7] */
 
 ( function( api, $ ) {
 	'use strict';
@@ -52,7 +54,7 @@
 
 			component.extendPreviewerQuery();
 			component.addButtons();
-			component.addSlideDown();
+			component.addSnapshotScheduleBox();
 
 			$( '#snapshot-save' ).on( 'click', function( event ) {
 				var scheduleDate;
@@ -177,7 +179,12 @@
 
 	component.snapshotScheduleBox = {};
 
-	component.addSlideDown = function slideDown() {
+	/**
+	 * Renders Snapshot Schedule Box and handles it's events.
+	 *
+	 * @returns {void}
+	 */
+	component.addSnapshotScheduleBox = function addSnapshotScheduleBox() {
 		var snapshotScheduleBoxTemplate = wp.template( 'snapshot-schedule-accordion' ),
 			snapshotScheduleBox = component.snapshotScheduleBox;
 		component.snapshotSlideDownToggle.click( function( e ) {
@@ -209,10 +216,10 @@
 					component.populateSetting();
 				} );
 				component.updateScheduledCountdown();
-				component.resetTimeButton.on( 'click', function( e ) {
+				component.resetTimeButton.on( 'click', function( event ) {
 					component.updateSnapshotScheduleBox();
 					component.resetTimeWrap.hide();
-					e.preventDefault();
+					event.preventDefault();
 				} );
 			} else {
 
@@ -246,6 +253,11 @@
 
 	};
 
+	/**
+	 * Updates snapshot schedule box with component.data
+	 *
+	 * @returns {void}
+	 */
 	component.updateSnapshotScheduleBox = function updateSnapshotScheduleBox() {
 		var parsed;
 		if ( _.isEmpty( component.snapshotScheduleBox ) ) {
@@ -258,8 +270,8 @@
 		// Update date controls.
 		component.snapshotEditLink.attr( 'href', component.data.editLink );
 		parsed = component.parseDateTime( component.data.snapshotPublishDate );
-		_.each( component.dateComponentInputs, function populateInput( node, component ) {
-			$( node ).val( parsed[component] );
+		_.each( component.dateComponentInputs, function populateInput( node, fieldName ) {
+			$( node ).val( parsed[fieldName] );
 		} );
 	};
 
@@ -615,39 +627,37 @@
 		date = component.getDateFromInputs();
 		if ( ! date ) {
 			return false;
-		} else {
-			save = $( '#snapshot-save' );
-			isScheduleDateUpdated = component.formatDate( date ) !== component.data.snapshotPublishDate;
-			if ( component.isScheduleDateFuture() ) {
-
-				// Change update button to schedule.
-				if ( save.length ) {
-					save.html( component.data.i18n.scheduleButton );
-					if ( isScheduleDateUpdated || component.data.isSnapshotHasUnsavedChanges ) {
-						save.prop( 'disabled', false );
-					} else {
-						save.prop( 'disabled', true );
-					}
-				}
-			} else {
-				if ( save.length ) {
-					save.html( component.data.i18n.updateButton );
-					save.prop( 'disabled', ! component.data.isSnapshotHasUnsavedChanges );
-				}
-			}
-			component.updateScheduledCountdown();
-			date.setSeconds( 0 );
-			if ( isScheduleDateUpdated ) {
-				component.resetTimeWrap.show();
+		}
+		save = $( '#snapshot-save' );
+		isScheduleDateUpdated = component.formatDate( date ) !== component.data.snapshotPublishDate;
+		if ( component.isScheduleDateFuture() && save.length ) {
+
+			// Change update button to schedule.
+			save.html( component.data.i18n.scheduleButton );
+			if ( isScheduleDateUpdated || component.data.isSnapshotHasUnsavedChanges ) {
+				save.prop( 'disabled', false );
 			} else {
-				component.resetTimeWrap.hide();
+				save.prop( 'disabled', true );
 			}
+		} else if ( save.length ) {
+			save.html( component.data.i18n.updateButton );
+			save.prop( 'disabled', ! component.data.isSnapshotHasUnsavedChanges );
 		}
+		component.updateScheduledCountdown();
+		date.setSeconds( 0 );
+		if ( isScheduleDateUpdated ) {
+			component.resetTimeWrap.show();
+		} else {
+			component.resetTimeWrap.hide();
+		}
+		return true;
+
 	};
 
 	/**
 	 * Check if snapshot schedule date is in future date
-	 * @returns {boolean}
+	 *
+	 * @returns {boolean} if date in input controls is of future
 	 */
 	component.isScheduleDateFuture = function isScheduleDateFuture() {
 		var date, remainingTime;

From a6c381dde8acc23313a62ad4c3b8a818c1defbf8 Mon Sep 17 00:00:00 2001
From: Derek Herman <derek@valendesigns.com>
Date: Thu, 4 Aug 2016 00:21:54 -0700
Subject: [PATCH 20/59] Fix control visbility

---
 css/customize-snapshots.css              |  92 ++++++++--------
 js/customize-snapshots.js                | 131 +++++++++++++----------
 php/class-customize-snapshot-manager.php |  20 ++--
 3 files changed, 129 insertions(+), 114 deletions(-)

diff --git a/css/customize-snapshots.css b/css/customize-snapshots.css
index d06ecb62..8108f9cf 100644
--- a/css/customize-snapshots.css
+++ b/css/customize-snapshots.css
@@ -1,10 +1,12 @@
-#snapshot-preview-link, #snapshot-toggle-button {
+#snapshot-preview-link,
+#snapshot-schedule-button {
 	float: right;
 	margin-top: 13px;
 	margin-right: 4px;
 	color: #656a6f;
 }
-#snapshot-toggle-button{
+
+#snapshot-schedule-button {
 	display: block;
 }
 
@@ -55,43 +57,27 @@
 	}
 }
 
-.customize-snapshot-control select,
-.customize-snapshot-control input.date-input {
-	min-width: 10%;
-	width: auto;
-}
-.customize-snapshot-control input.date-input {
-	-moz-appearance: textfield;
-}
-.customize-snapshot-control input.date-input::-webkit-outer-spin-button,
-.customize-snapshot-control input.date-input::-webkit-inner-spin-button {
-	-webkit-appearance: none;
-	margin: 0;
-}
-
-#customize-snapshot-control{
-	padding: 5px;
-}
-
-#customize-schedule-box{
+#snapshot-schedule-section {
 	background: #fff !important;
-	margin-bottom: 10px;
-	padding-bottom: 10px;
+	border-bottom: 1px solid #ddd;
+	line-height: 1.5;
+	left: 0;
+	top: 46px;
+	position: absolute;
+	width: 100%;
+	box-shadow: 0 5px 0 0 rgba(0,0,0,0.05);
 }
 
-#customize-schedule-box .accordion-section-title{
-	background: #fff;
+#snapshot-schedule-section .snapshot-schedule-title {
 	color: #555;
-	border-left: none;
-	border-right: none;
-	border-bottom: none;
-	cursor: default;
+	padding: 10px 10px 0;
 }
 
-#customize-schedule-box .accordion-section-title:after{
-	z-index: -1;
+#snapshot-schedule-section .snapshot-schedule-title h3 {
+	margin: .2em 2em .75em 0;
 }
-#customize-schedule-box a.snapshot-edit-link{
+
+#snapshot-schedule-section a.snapshot-edit-link {
 	position: absolute;
 	top: 4px;
 	right: 1px;
@@ -107,26 +93,34 @@
 	padding: 10px;
 }
 
-#customize-schedule-box div.preview-notice{
-	font-size: 13px;
-	line-height: 24px;
-}
-#customize-schedule-box .customize-snapshot-control{
-	padding: 5px;
+#snapshot-schedule-section .snapshot-schedule-control {
+	padding: 10px;
 }
-#customize-schedule-box .accordion-section-title{
+
+#snapshot-schedule-section .accordion-section-title {
 	padding: 10px;
 }
-.in-sub-panel #customize-schedule-box {
-	left: -100%;
-	width: 100%;
-	position: relative;
+
+#snapshot-schedule-section .wrap-reset-time {
+	font-weight: normal;
+	font-size: 80%;
+	display: none;
 }
-#customize-schedule-box .snapshot-description{
-	font-style: normal;
+
+.snapshot-schedule-control select.date-input {
+	height: 28px;
 }
 
-#customize-schedule-box .wrap-reset-time{
-	font-weight: normal;
-	font-size: 75%;
-}
\ No newline at end of file
+.snapshot-schedule-control select,
+.snapshot-schedule-control input.date-input {
+	min-width: 10%;
+	width: auto;
+}
+.snapshot-schedule-control input.date-input {
+	-moz-appearance: textfield;
+}
+.snapshot-schedule-control input.date-input::-webkit-outer-spin-button,
+.snapshot-schedule-control input.date-input::-webkit-inner-spin-button {
+	-webkit-appearance: none;
+	margin: 0;
+}
diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 5feb6eab..12668647 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -52,12 +52,12 @@
 
 			component.extendPreviewerQuery();
 			component.addButtons();
-			component.addSlideDown();
+			component.addSnapshotScheduleSection();
 
 			$( '#snapshot-save' ).on( 'click', function( event ) {
 				var scheduleDate;
 				event.preventDefault();
-				if ( $( this ).html() === component.data.i18n.scheduleButton && ! _.isEmpty( component.snapshotScheduleBox ) && component.getDateFromInputs() && component.isScheduleDateFuture() ) {
+				if ( $( this ).html() === component.data.i18n.scheduleButton && ! _.isEmpty( component.snapshotScheduleSection ) && component.getDateFromInputs() && component.isScheduleDateFuture() ) {
 					scheduleDate = component.getDateFromInputs();
 					component.sendUpdateSnapshotRequest( {
 						status: 'future',
@@ -175,56 +175,73 @@
 
 	component.dateComponentInputs = {};
 
-	component.snapshotScheduleBox = {};
+	component.snapshotScheduleSection = {};
 
-	component.addSlideDown = function slideDown() {
-		var snapshotScheduleBoxTemplate = wp.template( 'snapshot-schedule-accordion' ),
-			snapshotScheduleBox = component.snapshotScheduleBox;
-		component.snapshotSlideDownToggle.click( function( e ) {
-			var customizeInfo = $( '#customize-info' );
-			if ( _.isEmpty( snapshotScheduleBox ) ) {
-				if ( '0000-00-00 00:00:00' === component.data.snapshotPublishDate ) {
-					component.data.snapshotPublishDate = component.getCurrentTime();
-				}
-				component.data = _.extend( component.data, component.parseDateTime( component.data.snapshotPublishDate ) );
-				snapshotScheduleBox = $( $.trim( snapshotScheduleBoxTemplate( component.data ) ) );
-				snapshotScheduleBox.insertBefore( customizeInfo );
-				component.dateInputs = snapshotScheduleBox.find( '.date-input' );
-				component.scheduledCountdownContainer = snapshotScheduleBox.find( '.scheduled-countdown' );
-				component.resetTimeButton = snapshotScheduleBox.find( '.reset-time' );
-				component.resetTimeWrap = snapshotScheduleBox.find( '.wrap-reset-time' );
-				component.scheduledCountdownTemplate = wp.template( 'snapshot-scheduled-countdown' );
-				component.snapshotScheduleBox = snapshotScheduleBox;
-				component.dateInputs.each( function() {
-					var input = $( this ), componentName;
-					componentName = input.data( 'component' );
-					component.dateComponentInputs[componentName] = input;
-				} );
-				component.snapshotEditLink = snapshotScheduleBox.find( 'a' );
-				component.dateInputs.on( 'input', function hydrateInputValues() {
-					component.populateSetting();
-				} );
-				component.dateInputs.on( 'blur', function hydrateInputValues() {
-					component.populateInputs();
-					component.populateSetting();
-				} );
-				component.updateScheduledCountdown();
-				component.resetTimeButton.on( 'click', function( e ) {
-					component.updateSnapshotScheduleBox();
-					component.resetTimeWrap.hide();
-					e.preventDefault();
-				} );
-			} else {
+	component.snapshotScheduleSlideToggle = function snapshotScheduleSlideToggle() {
+		component.snapshotScheduleSection.slideToggle( 'fast', function() {
+			$( this ).parent().toggleClass( 'schedule-section-open' );
+		} );
+	};
 
-				// Todo need to update in case of dynamic section.
-				snapshotScheduleBox.slideToggle();
+	/**
+	 * Renders Snapshot Schedule Section and handles it's events.
+	 *
+	 * @returns {void}
+	 */
+	component.addSnapshotScheduleSection = function addSnapshotScheduleSection() {
+		var snapshotScheduleSectionTemplate = wp.template( 'snapshot-schedule-accordion' );
+
+		// Inject the UI.
+		if ( _.isEmpty( component.snapshotScheduleSection ) ) {
+			if ( '0000-00-00 00:00:00' === component.data.snapshotPublishDate ) {
+				component.data.snapshotPublishDate = component.getCurrentTime();
 			}
-			e.preventDefault();
+
+			component.data = _.extend( component.data, component.parseDateTime( component.data.snapshotPublishDate ) );
+
+			component.snapshotScheduleSection = $( $.trim( snapshotScheduleSectionTemplate( component.data ) ) );
+			component.snapshotScheduleSection.hide().appendTo( $( '#customize-header-actions' ) );
+
+			component.dateInputs = component.snapshotScheduleSection.find( '.date-input' );
+			component.scheduledCountdownContainer = component.snapshotScheduleSection.find( '.snapshot-scheduled-countdown' );
+			component.resetTimeButton = component.snapshotScheduleSection.find( '.reset-time' );
+			component.resetTimeWrap = component.snapshotScheduleSection.find( '.wrap-reset-time' );
+			component.scheduledCountdownTemplate = wp.template( 'snapshot-scheduled-countdown' );
+
+			component.dateInputs.each( function() {
+				var input = $( this ), componentName;
+				componentName = input.data( 'component' );
+				component.dateComponentInputs[componentName] = input;
+			} );
+
+			component.snapshotEditLink = component.snapshotScheduleSection.find( 'a' );
+			component.dateInputs.on( 'input', function hydrateInputValues() {
+				component.populateSetting();
+			} );
+
+			component.dateInputs.on( 'blur', function hydrateInputValues() {
+				component.populateInputs();
+				component.populateSetting();
+			} );
+
+			component.updateScheduledCountdown();
+			component.resetTimeButton.on( 'click', function( event ) {
+				component.updateSnapshotScheduleSection();
+				component.resetTimeWrap.hide();
+				// @todo reset button back to save or update and set disabled prop and remove scheduled text.
+				event.preventDefault();
+			} );
+		}
+
+		// Listen for click events.
+		component.snapshotSlideDownToggle.on( 'click', function( event ) {
+			event.preventDefault();
+			component.snapshotScheduleSection.toggle();
 		} );
 
 		api.state( 'snapshot-saved' ).bind( function( saved ) {
 			if ( saved ) {
-				component.updateSnapshotScheduleBox();
+				component.updateSnapshotScheduleSection();
 			}
 		} );
 
@@ -233,22 +250,26 @@
 		} );
 
 		api.state( 'saved' ).bind( function( saved ) {
-			if ( saved && ! _.isEmpty( component.snapshotScheduleBox ) ) {
-				component.snapshotScheduleBox.hide();
+			if ( saved && ! _.isEmpty( component.snapshotScheduleSection ) ) {
+				component.snapshotScheduleSection.hide();
 			}
 		} );
 
 		api.state( 'snapshot-exists' ).bind( function( exists ) {
-			if ( exists && ! _.isEmpty( component.snapshotScheduleBox ) ) {
-				component.updateSnapshotScheduleBox();
+			if ( exists && ! _.isEmpty( component.snapshotScheduleSection ) ) {
+				component.updateSnapshotScheduleSection();
 			}
 		} );
-
 	};
 
-	component.updateSnapshotScheduleBox = function updateSnapshotScheduleBox() {
+	/**
+	 * Updates snapshot schedule section with `component.data`.
+	 *
+	 * @return {void}
+	 */
+	component.updateSnapshotScheduleSection = function updateSnapshotScheduleSection() {
 		var parsed;
-		if ( _.isEmpty( component.snapshotScheduleBox ) ) {
+		if ( _.isEmpty( component.snapshotScheduleSection ) ) {
 			return;
 		}
 		if ( '0000-00-00 00:00:00' === component.data.snapshotPublishDate ) {
@@ -258,8 +279,8 @@
 		// Update date controls.
 		component.snapshotEditLink.attr( 'href', component.data.editLink );
 		parsed = component.parseDateTime( component.data.snapshotPublishDate );
-		_.each( component.dateComponentInputs, function populateInput( node, component ) {
-			$( node ).val( parsed[component] );
+		_.each( component.dateComponentInputs, function populateInput( node, fieldName ) {
+			$( node ).val( parsed[fieldName] );
 		} );
 	};
 
@@ -271,7 +292,7 @@
 	component.addButtons = function() {
 		var header = $( '#customize-header-actions' ),
 			publishButton = header.find( '#save' ),
-			snapshotSlideDownToggleTemplate = wp.template( 'snapshot-toggle-button' ),
+			snapshotSlideDownToggleTemplate = wp.template( 'snapshot-schedule-button' ),
 			snapshotButton, submitButton, data, setPreviewLinkHref, snapshotSlideDownToggle, snapshotButtonText;
 
 		// Save/update button.
diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index 7f1a0d58..88c0396f 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -1090,18 +1090,18 @@ public function render_templates() {
 			</a>
 		</script>
 
-		<script type="text/html" id="tmpl-snapshot-toggle-button">
-			<a href="#" id="snapshot-toggle-button" class="dashicons dashicons-calendar-alt" title="<?php esc_attr_e( 'Schedule Snapshot','customize-snapshots' ); ?>"></a>
+		<script type="text/html" id="tmpl-snapshot-schedule-button">
+			<a href="#" id="snapshot-schedule-button" class="dashicons dashicons-calendar-alt" title="<?php esc_attr_e( 'Schedule Snapshot','customize-snapshots' ); ?>"></a>
 		</script>
 
 		<script type="text/html" id="tmpl-snapshot-schedule-accordion">
-			<div id="customize-schedule-box" class="accordion-section">
-				<div class="accordion-section-title">
-					<div class="preview-notice">
-						<strong class="panel-title site-title"><?php esc_html_e( 'Schedule Snapshot', 'customize-snapshots' ); ?></strong>
+			<div id="snapshot-schedule-section" class="accordion-section">
+				<div class="snapshot-schedule-title">
+					<h3>
+						<?php esc_html_e( 'Schedule Snapshot', 'customize-snapshots' ); ?>
 						<span class="wrap-reset-time">(<a href="#" class="reset-time"><?php esc_html_e( 'Reset', 'customize-posts' ) ?></a>)</span>
-					</div>
-					<span class="description snapshot-description">
+					</h3>
+					<span class="snapshot-schedule-description">
 						<?php
 						$tz_string = get_option( 'timezone_string' );
 						if ( $tz_string ) {
@@ -1118,12 +1118,12 @@ public function render_templates() {
 							$date_control_description = sprintf( __( 'Dates are in UTC%s.', 'customize-snapshots' ), $formatted_gmt_offset );
 						}
 						?>
-						<span class="scheduled-countdown"></span>
+						<span class="snapshot-scheduled-countdown"></span>
 						<span class="timezone-info"><?php echo esc_html( $date_control_description ); ?></span>
 					</span>
 					<a href={{ data.editLink }} class="dashicons dashicons-edit snapshot-edit-link" aria-expanded="false"></a>
 				</div>
-				<div class="customize-snapshot-control">
+				<div class="snapshot-schedule-control">
 					<# _.defaults( data, <?php echo wp_json_encode( $data ) ?> );
 						data.input_id_post_date = 'input-' + String( Math.random() );
 						data.input_id_post_date_gmt = 'input-' + String( Math.random() );

From d8a1edb8d7a85c636ae256e70ae450ab3f22aa45 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Thu, 4 Aug 2016 14:05:36 +0530
Subject: [PATCH 21/59] Fix error on updateScheduledCountdown when controls are
 not initialized

---
 js/customize-snapshots.js | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 4af70578..7533dbfd 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -697,11 +697,15 @@
 	 * Hides countdown if post_status is not already future.
 	 * Toggles the countdown if there is no remaining time.
 	 *
-	 * @returns {void}
+	 * @returns {boolean} if date input have valid time.
 	 */
 	component.updateScheduledCountdown = function updateScheduledCountdown() {
-		var remainingTime;
-		remainingTime = component.getDateFromInputs().valueOf();
+		var remainingTime, dateTimeFromInput;
+		dateTimeFromInput = component.getDateFromInputs();
+		if ( ! dateTimeFromInput ) {
+			return false;
+		}
+		remainingTime = dateTimeFromInput.valueOf();
 		remainingTime -= ( new Date( component.getCurrentTime() ) ).valueOf();
 		remainingTime = Math.ceil( remainingTime / 1000 );
 		if ( remainingTime > 0 ) {
@@ -710,6 +714,7 @@
 		} else {
 			component.scheduledCountdownContainer.hide();
 		}
+		return true;
 	};
 
 	component.init();

From 08a217116279a9421503b45a74dc3f879e785ad3 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Thu, 4 Aug 2016 14:33:41 +0530
Subject: [PATCH 22/59] On reset update UI settings

---
 js/customize-snapshots.js | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 7533dbfd..d413418f 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -223,7 +223,8 @@
 			component.updateScheduledCountdown();
 			component.resetTimeButton.on( 'click', function( event ) {
 				component.updateSnapshotScheduleSection();
-				component.resetTimeWrap.hide();
+				component.populateSetting();
+
 				// @todo reset button back to save or update and set disabled prop and remove scheduled text.
 				event.preventDefault();
 			} );
@@ -553,6 +554,7 @@
 		if ( isNaN( date.valueOf() ) ) {
 			return null;
 		}
+		date.setSeconds( 0 );
 		return date;
 	};
 
@@ -634,6 +636,7 @@
 			return false;
 		}
 		save = $( '#snapshot-save' );
+		date.setSeconds( 0 );
 		isScheduleDateUpdated = component.formatDate( date ) !== component.data.snapshotPublishDate;
 		if ( component.isScheduleDateFuture() && save.length ) {
 
@@ -649,7 +652,6 @@
 			save.prop( 'disabled', ! component.data.isSnapshotHasUnsavedChanges );
 		}
 		component.updateScheduledCountdown();
-		date.setSeconds( 0 );
 		if ( isScheduleDateUpdated ) {
 			component.resetTimeWrap.show();
 		} else {

From 4ef7d54c002d908d0ac5c96be04c7857d38c2dad Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Thu, 4 Aug 2016 15:23:34 +0530
Subject: [PATCH 23/59] On snapshot change schedule icon should be visible

---
 js/customize-snapshots.js | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index d413418f..f9ebfa26 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -319,9 +319,6 @@
 		if ( ! component.data.editLink ) {
 			snapshotSlideDownToggle.hide();
 		}
-		api.state.bind( 'change', function() {
-			snapshotSlideDownToggle.toggle( api.state( 'snapshot-saved' ).get() && api.state( 'snapshot-exists' ).get() );
-		} );
 
 		api.state( 'snapshot-saved' ).bind( function( saved ) {
 			snapshotButton.prop( 'disabled', saved );

From c2281d0eec70a292407c8ff1801e60cb727353b0 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Thu, 4 Aug 2016 16:15:11 +0530
Subject: [PATCH 24/59] Visibility of schedule toggle icon fix on publish and
 normalize date with sec as zeros

---
 js/customize-snapshots.js | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index f9ebfa26..f71cea00 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -1,6 +1,6 @@
 /* global jQuery, _customizeSnapshots */
 /* eslint-disable no-extra-parens */
-/* eslint no-magic-numbers: ["error", { "ignore": [-1,0,1,1000] }] */
+/* eslint no-magic-numbers: ["error", { "ignore": [-2,-1,0,1,1000] }] */
 /* eslint complexity: ["error", 7] */
 
 ( function( api, $ ) {
@@ -193,6 +193,9 @@
 				component.data.snapshotPublishDate = component.getCurrentTime();
 			}
 
+			// Normalize date with secs set as zeros removed.
+			component.data.snapshotPublishDate = component.data.snapshotPublishDate.slice( 0, -2 ) + '00';
+
 			component.data = _.extend( component.data, component.parseDateTime( component.data.snapshotPublishDate ) );
 
 			component.snapshotScheduleSection = $( $.trim( snapshotScheduleSectionTemplate( component.data ) ) );
@@ -255,6 +258,8 @@
 		api.state( 'snapshot-exists' ).bind( function( exists ) {
 			if ( exists && ! _.isEmpty( component.snapshotScheduleSection ) ) {
 				component.updateSnapshotScheduleSection();
+			} else {
+				component.snapshotScheduleSection.hide();
 			}
 		} );
 	};
@@ -273,6 +278,9 @@
 			component.data.snapshotPublishDate = component.getCurrentTime();
 		}
 
+		// Normalize date with secs removed.
+		component.data.snapshotPublishDate = component.data.snapshotPublishDate.slice( 0, -2 ) + '00';
+
 		// Update date controls.
 		component.snapshotEditLink.attr( 'href', component.data.editLink );
 		parsed = component.parseDateTime( component.data.snapshotPublishDate );
@@ -320,6 +328,14 @@
 			snapshotSlideDownToggle.hide();
 		}
 
+		api.state( 'change', function() {
+			snapshotSlideDownToggle.toggle( api.state( 'snapshot-saved' ).get() && api.state( 'snapshot-exists' ).get() );
+		} );
+
+		api.state( 'snapshot-exists' ).bind( function( exist ) {
+			snapshotSlideDownToggle.toggle( exist );
+		} );
+
 		api.state( 'snapshot-saved' ).bind( function( saved ) {
 			snapshotButton.prop( 'disabled', saved );
 		} );

From 9a58741e3d9e7331f851d55cdbf1dcaba6371867 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Thu, 4 Aug 2016 16:48:11 +0530
Subject: [PATCH 25/59] Fix edit link not visible on snapshot create

---
 js/customize-snapshots.js                | 2 +-
 php/class-customize-snapshot-manager.php | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index f71cea00..15fd6006 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -213,7 +213,7 @@
 				component.dateComponentInputs[componentName] = input;
 			} );
 
-			component.snapshotEditLink = component.snapshotScheduleSection.find( 'a' );
+			component.snapshotEditLink = component.snapshotScheduleSection.find( 'a.snapshot-edit-link' );
 			component.dateInputs.on( 'input', function hydrateInputValues() {
 				component.populateSetting();
 			} );
diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index 038bc614..2f5e6331 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -1121,7 +1121,7 @@ public function render_templates() {
 						<span class="snapshot-scheduled-countdown"></span>
 						<span class="timezone-info"><?php echo esc_html( $date_control_description ); ?></span>
 					</span>
-					<a href={{ data.editLink }} class="dashicons dashicons-edit snapshot-edit-link" aria-expanded="false"></a>
+					<a href="{{ data.editLink }}" class="dashicons dashicons-edit snapshot-edit-link" aria-expanded="false"></a>
 				</div>
 				<div class="snapshot-schedule-control">
 					<# _.defaults( data, <?php echo wp_json_encode( $data ) ?> );

From 73c18177898ce50cc0efce403e8c835bbeb0a960 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Thu, 4 Aug 2016 17:23:29 +0530
Subject: [PATCH 26/59] Fix unit test

---
 tests/php/test-class-ajax-customize-snapshot-manager.php | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tests/php/test-class-ajax-customize-snapshot-manager.php b/tests/php/test-class-ajax-customize-snapshot-manager.php
index 660486ba..99c085f5 100644
--- a/tests/php/test-class-ajax-customize-snapshot-manager.php
+++ b/tests/php/test-class-ajax-customize-snapshot-manager.php
@@ -334,6 +334,7 @@ function data_update_snapshot_cap_check() {
 					'setting_validities' => array(
 						'anyonecanedit' => true,
 					),
+					'snapshot_publish_date' => '0000-00-00 00:00:00',
 				),
 			),
 		);

From a8a614c5cfe430fc5477d32bcf0b272ebdbed37d Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Thu, 4 Aug 2016 17:34:41 +0530
Subject: [PATCH 27/59] Code reivew changes

---
 js/customize-snapshots.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 15fd6006..b8bab412 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -59,7 +59,7 @@
 			$( '#snapshot-save' ).on( 'click', function( event ) {
 				var scheduleDate;
 				event.preventDefault();
-				if ( $( this ).html() === component.data.i18n.scheduleButton && ! _.isEmpty( component.snapshotScheduleSection ) && component.getDateFromInputs() && component.isScheduleDateFuture() ) {
+				if ( ! _.isEmpty( component.snapshotScheduleSection ) && component.getDateFromInputs() && component.isScheduleDateFuture() ) {
 					scheduleDate = component.getDateFromInputs();
 					component.sendUpdateSnapshotRequest( {
 						status: 'future',

From 0d21f51f46459e266ff066982968cded9539a6f2 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Thu, 4 Aug 2016 18:17:53 +0530
Subject: [PATCH 28/59] Fix date issue on snapshot update after scheduling

---
 js/customize-snapshots.js                                | 6 +++++-
 php/class-customize-snapshot-manager.php                 | 7 +++++--
 php/class-post-type.php                                  | 3 ++-
 tests/php/test-class-ajax-customize-snapshot-manager.php | 3 ++-
 4 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index b8bab412..091c9f6d 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -662,7 +662,11 @@
 			}
 		} else if ( save.length ) {
 			save.html( component.data.i18n.updateButton );
-			save.prop( 'disabled', ! component.data.isSnapshotHasUnsavedChanges );
+			if ( component.data.isSnapshotHasUnsavedChanges || isScheduleDateUpdated ) {
+				save.prop( 'disabled', false );
+			} else {
+				save.prop( 'disabled', true );
+			}
 		}
 		component.updateScheduledCountdown();
 		if ( isScheduleDateUpdated ) {
diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index 2f5e6331..a8424329 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -951,17 +951,20 @@ function( $value ) {
 		$args = array(
 			'status' => $status,
 		);
+		$args['edit_date'] = current_time( 'mysql' );
 
 		if ( isset( $publish_date_obj ) && 'future' === $status ) {
 			$args['post_date'] = $publish_date_obj->format( 'Y-m-d H:i:s' );
-			$args['edit_date'] = current_time( 'mysql' );
+			$args['post_date_gmt'] = '0000-00-00 00:00:00';
+		} else {
+			$args['post_date_gmt'] = $args['post_date'] = '0000-00-00 00:00:00';
 		}
 		$r = $this->snapshot->save( $args );
 
 		$post = $this->snapshot->post();
 		if ( $post ) {
 			$data['edit_link'] = get_edit_post_link( $post, 'raw' );
-			$data['snapshot_publish_date'] = $post->post_date_gmt;
+			$data['snapshot_publish_date'] = $post->post_date;
 		}
 
 		if ( is_wp_error( $r ) ) {
diff --git a/php/class-post-type.php b/php/class-post-type.php
index 349f6b6a..b085465a 100644
--- a/php/class-post-type.php
+++ b/php/class-post-type.php
@@ -508,9 +508,10 @@ public function save( array $args ) {
 			),
 		);
 		if ( ! empty( $args['status'] ) ) {
-			if ( isset( $args['post_date'], $args['edit_date'] ) && 'future' === $args['status'] ) {
+			if ( isset( $args['post_date'], $args['edit_date'], $args['post_date_gmt'] ) ) {
 				$post_arr['post_date'] = $args['post_date'];
 				$post_arr['edit_date'] = $args['edit_date'];
+				$post_arr['post_date_gmt'] = $args['post_date_gmt'];
 			}
 			if ( ! get_post_status_object( $args['status'] ) ) {
 				return new \WP_Error( 'bad_status' );
diff --git a/tests/php/test-class-ajax-customize-snapshot-manager.php b/tests/php/test-class-ajax-customize-snapshot-manager.php
index 99c085f5..e263f42e 100644
--- a/tests/php/test-class-ajax-customize-snapshot-manager.php
+++ b/tests/php/test-class-ajax-customize-snapshot-manager.php
@@ -275,7 +275,9 @@ function test_ajax_update_snapshot_cap_check( $role, $expected_results ) {
 
 		if ( $response['success'] ) {
 			$this->assertNotEmpty( $response['data']['edit_link'] );
+			$this->assertNotEmpty( $response['data']['snapshot_publish_date'] );
 			unset( $response['data']['edit_link'] );
+			unset( $response['data']['snapshot_publish_date'] );
 		}
 		$this->assertSame( $expected_results, $response );
 	}
@@ -334,7 +336,6 @@ function data_update_snapshot_cap_check() {
 					'setting_validities' => array(
 						'anyonecanedit' => true,
 					),
-					'snapshot_publish_date' => '0000-00-00 00:00:00',
 				),
 			),
 		);

From 1ee150a33f1f210e829aaeabf06890dcd3409ef9 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Thu, 4 Aug 2016 18:27:58 +0530
Subject: [PATCH 29/59] Fix eslint complexity

---
 js/customize-snapshots.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 091c9f6d..81f3d884 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -1,7 +1,7 @@
 /* global jQuery, _customizeSnapshots */
 /* eslint-disable no-extra-parens */
 /* eslint no-magic-numbers: ["error", { "ignore": [-2,-1,0,1,1000] }] */
-/* eslint complexity: ["error", 7] */
+/* eslint complexity: ["error", 9] */
 
 ( function( api, $ ) {
 	'use strict';

From 256f7b6bc9f1bcc061b3b8069e3934a79470de62 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Thu, 4 Aug 2016 18:55:50 +0530
Subject: [PATCH 30/59] On snapshot schedule/update call populateSetting to
 update UI

---
 js/customize-snapshots.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 81f3d884..e716a3e6 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -287,6 +287,7 @@
 		_.each( component.dateComponentInputs, function populateInput( node, fieldName ) {
 			$( node ).val( parsed[fieldName] );
 		} );
+		component.populateSetting();
 	};
 
 	/**
@@ -458,6 +459,8 @@
 			if ( response.snapshot_publish_date ) {
 				component.data.snapshotPublishDate = response.snapshot_publish_date;
 			}
+			component.updateSnapshotScheduleSection();
+			component.data.isSnapshotHasUnsavedChanges = false;
 
 			// @todo Remove privateness from _handleSettingValidities in Core.
 			if ( api._handleSettingValidities && response.setting_validities ) {

From 96cba79ca7f35948d511c3dedc390a3e1a460e66 Mon Sep 17 00:00:00 2001
From: Derek Herman <derek@valendesigns.com>
Date: Fri, 5 Aug 2016 02:08:44 -0700
Subject: [PATCH 31/59] Refactor

---
 css/customize-snapshots.css              |  38 +-
 js/customize-snapshots.js                | 466 +++++++++++++----------
 php/class-customize-snapshot-manager.php |  84 ++--
 3 files changed, 333 insertions(+), 255 deletions(-)

diff --git a/css/customize-snapshots.css b/css/customize-snapshots.css
index 8108f9cf..5a872a12 100644
--- a/css/customize-snapshots.css
+++ b/css/customize-snapshots.css
@@ -57,7 +57,7 @@
 	}
 }
 
-#snapshot-schedule-section {
+#snapshot-schedule {
 	background: #fff !important;
 	border-bottom: 1px solid #ddd;
 	line-height: 1.5;
@@ -68,16 +68,16 @@
 	box-shadow: 0 5px 0 0 rgba(0,0,0,0.05);
 }
 
-#snapshot-schedule-section .snapshot-schedule-title {
+#snapshot-schedule .snapshot-schedule-title {
 	color: #555;
 	padding: 10px 10px 0;
 }
 
-#snapshot-schedule-section .snapshot-schedule-title h3 {
+#snapshot-schedule .snapshot-schedule-title h3 {
 	margin: .2em 2em .75em 0;
 }
 
-#snapshot-schedule-section a.snapshot-edit-link {
+#snapshot-schedule a.snapshot-edit-link {
 	position: absolute;
 	top: 4px;
 	right: 1px;
@@ -93,15 +93,39 @@
 	padding: 10px;
 }
 
-#snapshot-schedule-section .snapshot-schedule-control {
+#snapshot-schedule a.snapshot-edit-link:focus,
+#snapshot-schedule a.snapshot-edit-link:hover {
+	color: #0073aa;
+	
+}
+			
+#snapshot-schedule a.snapshot-edit-link:before {
+	padding: 4px;
+	position: absolute;
+	top: 5px;
+	left: 6px;
+	-webkit-border-radius: 100%;
+	border-radius: 100%;
+}
+
+#snapshot-schedule a.snapshot-edit-link:focus:before {
+	-webkit-box-shadow:
+		0 0 0 1px #5b9dd9,
+		0 0 2px 1px rgba(30, 140, 190, .8);
+	box-shadow:
+		0 0 0 1px #5b9dd9,
+		0 0 2px 1px rgba(30, 140, 190, .8);
+}
+
+#snapshot-schedule .snapshot-schedule-control {
 	padding: 10px;
 }
 
-#snapshot-schedule-section .accordion-section-title {
+#snapshot-schedule .accordion-section-title {
 	padding: 10px;
 }
 
-#snapshot-schedule-section .wrap-reset-time {
+#snapshot-schedule .reset-time {
 	font-weight: normal;
 	font-size: 80%;
 	display: none;
diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index e716a3e6..24c6865f 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -1,7 +1,4 @@
 /* global jQuery, _customizeSnapshots */
-/* eslint-disable no-extra-parens */
-/* eslint no-magic-numbers: ["error", { "ignore": [-2,-1,0,1,1000] }] */
-/* eslint complexity: ["error", 9] */
 
 ( function( api, $ ) {
 	'use strict';
@@ -14,18 +11,20 @@
 
 	component = api.Snapshots;
 
+	component.schedule = {};
+
 	component.data = {
 		action: '',
 		uuid: '',
 		editLink: '',
-		snapshotPublishDate: '',
-		snapshotStatus: '',
+		publishDate: '',
+		postStatus: '',
 		currentUserCanPublish: '',
 		initialServerDate: '',
 		initialServerTimestamp: 0,
-		initialClientTimestamp: ( new Date() ).valueOf(),
+		initialClientTimestamp: 0,
 		i18n: {},
-		isSnapshotHasUnsavedChanges: false
+		dirty: false
 	};
 
 	if ( 'undefined' !== typeof _customizeSnapshots ) {
@@ -41,6 +40,9 @@
 		window._wpCustomizeControlsL10n.save = component.data.i18n.publish;
 		window._wpCustomizeControlsL10n.saved = component.data.i18n.published;
 
+		// Set the initial client timestamp.
+		component.data.initialClientTimestamp = component.dateValueOf();
+
 		api.bind( 'ready', function() {
 			api.state.create( 'snapshot-exists', component.data.snapshotExists );
 			api.state.create( 'snapshot-saved', true );
@@ -54,12 +56,12 @@
 
 			component.extendPreviewerQuery();
 			component.addButtons();
-			component.addSnapshotScheduleSection();
+			component.addSchedule();
 
 			$( '#snapshot-save' ).on( 'click', function( event ) {
 				var scheduleDate;
 				event.preventDefault();
-				if ( ! _.isEmpty( component.snapshotScheduleSection ) && component.getDateFromInputs() && component.isScheduleDateFuture() ) {
+				if ( ! _.isEmpty( component.schedule.template ) && component.isFutureDate() ) {
 					scheduleDate = component.getDateFromInputs();
 					component.sendUpdateSnapshotRequest( {
 						status: 'future',
@@ -175,121 +177,6 @@
 		return a.href;
 	};
 
-	component.dateComponentInputs = {};
-
-	component.snapshotScheduleSection = {};
-
-	/**
-	 * Renders Snapshot Schedule Section and handles it's events.
-	 *
-	 * @returns {void}
-	 */
-	component.addSnapshotScheduleSection = function addSnapshotScheduleSection() {
-		var snapshotScheduleSectionTemplate = wp.template( 'snapshot-schedule-accordion' );
-
-		// Inject the UI.
-		if ( _.isEmpty( component.snapshotScheduleSection ) ) {
-			if ( '0000-00-00 00:00:00' === component.data.snapshotPublishDate ) {
-				component.data.snapshotPublishDate = component.getCurrentTime();
-			}
-
-			// Normalize date with secs set as zeros removed.
-			component.data.snapshotPublishDate = component.data.snapshotPublishDate.slice( 0, -2 ) + '00';
-
-			component.data = _.extend( component.data, component.parseDateTime( component.data.snapshotPublishDate ) );
-
-			component.snapshotScheduleSection = $( $.trim( snapshotScheduleSectionTemplate( component.data ) ) );
-			component.snapshotScheduleSection.hide().appendTo( $( '#customize-header-actions' ) );
-
-			component.dateInputs = component.snapshotScheduleSection.find( '.date-input' );
-			component.scheduledCountdownContainer = component.snapshotScheduleSection.find( '.snapshot-scheduled-countdown' );
-			component.resetTimeButton = component.snapshotScheduleSection.find( '.reset-time' );
-			component.resetTimeWrap = component.snapshotScheduleSection.find( '.wrap-reset-time' );
-			component.scheduledCountdownTemplate = wp.template( 'snapshot-scheduled-countdown' );
-
-			component.dateInputs.each( function() {
-				var input = $( this ), componentName;
-				componentName = input.data( 'component' );
-				component.dateComponentInputs[componentName] = input;
-			} );
-
-			component.snapshotEditLink = component.snapshotScheduleSection.find( 'a.snapshot-edit-link' );
-			component.dateInputs.on( 'input', function hydrateInputValues() {
-				component.populateSetting();
-			} );
-
-			component.dateInputs.on( 'blur', function hydrateInputValues() {
-				component.populateInputs();
-				component.populateSetting();
-			} );
-
-			component.updateScheduledCountdown();
-			component.resetTimeButton.on( 'click', function( event ) {
-				component.updateSnapshotScheduleSection();
-				component.populateSetting();
-
-				// @todo reset button back to save or update and set disabled prop and remove scheduled text.
-				event.preventDefault();
-			} );
-		}
-
-		// Listen for click events.
-		component.snapshotSlideDownToggle.on( 'click', function( event ) {
-			event.preventDefault();
-			component.snapshotScheduleSection.toggle();
-		} );
-
-		api.state( 'snapshot-saved' ).bind( function( saved ) {
-			if ( saved ) {
-				component.updateSnapshotScheduleSection();
-			}
-		} );
-
-		api.bind( 'change', function() {
-			component.data.isSnapshotHasUnsavedChanges = true;
-		} );
-
-		api.state( 'saved' ).bind( function( saved ) {
-			if ( saved && ! _.isEmpty( component.snapshotScheduleSection ) ) {
-				component.snapshotScheduleSection.hide();
-			}
-		} );
-
-		api.state( 'snapshot-exists' ).bind( function( exists ) {
-			if ( exists && ! _.isEmpty( component.snapshotScheduleSection ) ) {
-				component.updateSnapshotScheduleSection();
-			} else {
-				component.snapshotScheduleSection.hide();
-			}
-		} );
-	};
-
-	/**
-	 * Updates snapshot schedule section with `component.data`.
-	 *
-	 * @return {void}
-	 */
-	component.updateSnapshotScheduleSection = function updateSnapshotScheduleSection() {
-		var parsed;
-		if ( _.isEmpty( component.snapshotScheduleSection ) ) {
-			return;
-		}
-		if ( '0000-00-00 00:00:00' === component.data.snapshotPublishDate ) {
-			component.data.snapshotPublishDate = component.getCurrentTime();
-		}
-
-		// Normalize date with secs removed.
-		component.data.snapshotPublishDate = component.data.snapshotPublishDate.slice( 0, -2 ) + '00';
-
-		// Update date controls.
-		component.snapshotEditLink.attr( 'href', component.data.editLink );
-		parsed = component.parseDateTime( component.data.snapshotPublishDate );
-		_.each( component.dateComponentInputs, function populateInput( node, fieldName ) {
-			$( node ).val( parsed[fieldName] );
-		} );
-		component.populateSetting();
-	};
-
 	/**
 	 * Create the snapshot buttons.
 	 *
@@ -298,13 +185,12 @@
 	component.addButtons = function() {
 		var header = $( '#customize-header-actions' ),
 			publishButton = header.find( '#save' ),
-			snapshotSlideDownToggleTemplate = wp.template( 'snapshot-schedule-button' ),
-			snapshotButton, submitButton, data, setPreviewLinkHref, snapshotSlideDownToggle, snapshotButtonText;
+			snapshotButton, scheduleButton, submitButton, data, setPreviewLinkHref, snapshotButtonText;
 
 		// Save/update button.
 		snapshotButton = wp.template( 'snapshot-save' );
 		if ( api.state( 'snapshot-exists' ).get() ) {
-			if ( 'future' === component.data.snapshotStatus ) {
+			if ( 'future' === component.data.postStatus ) {
 				snapshotButtonText = component.data.i18n.scheduleButton;
 			} else {
 				snapshotButtonText = component.data.i18n.updateButton;
@@ -322,19 +208,21 @@
 		snapshotButton.prop( 'disabled', true );
 		snapshotButton.insertAfter( publishButton );
 
-		snapshotSlideDownToggle = $( $.trim( snapshotSlideDownToggleTemplate( {} ) ) );
-		snapshotSlideDownToggle.insertAfter( snapshotButton );
-		component.snapshotSlideDownToggle = snapshotSlideDownToggle;
+		// Schedule button.
+		scheduleButton = wp.template( 'snapshot-schedule-button' );
+		scheduleButton = $( $.trim( scheduleButton( {} ) ) );
+		scheduleButton.insertAfter( snapshotButton );
+
 		if ( ! component.data.editLink ) {
-			snapshotSlideDownToggle.hide();
+			scheduleButton.hide();
 		}
 
 		api.state( 'change', function() {
-			snapshotSlideDownToggle.toggle( api.state( 'snapshot-saved' ).get() && api.state( 'snapshot-exists' ).get() );
+			scheduleButton.toggle( api.state( 'snapshot-saved' ).get() && api.state( 'snapshot-exists' ).get() );
 		} );
 
 		api.state( 'snapshot-exists' ).bind( function( exist ) {
-			snapshotSlideDownToggle.toggle( exist );
+			scheduleButton.toggle( exist );
 		} );
 
 		api.state( 'snapshot-saved' ).bind( function( saved ) {
@@ -403,6 +291,154 @@
 		header.addClass( 'button-added' );
 	};
 
+	/**
+	 * Renders snapshot schedule and handles it's events.
+	 *
+	 * @returns {void}
+	 */
+	component.addSchedule = function addSchedule() {
+		var sliceBegin = 0,
+			sliceEnd = -2;
+
+		// Inject the UI.
+		if ( _.isEmpty( component.schedule.template ) ) {
+			if ( '0000-00-00 00:00:00' === component.data.publishDate ) {
+				component.data.publishDate = component.getCurrentTime();
+			}
+
+			// Normalize date with secs set as zeros removed.
+			component.data.publishDate = component.data.publishDate.slice( sliceBegin, sliceEnd ) + '00';
+
+			// Extend the components data object and add the parsed datetime strings.
+			component.data = _.extend( component.data, component.parseDateTime( component.data.publishDate ) );
+
+			// Add the template to the DOM.
+			component.schedule.template = $( $.trim( wp.template( 'snapshot-schedule' )( component.data ) ) );
+			component.schedule.template.hide().appendTo( $( '#customize-header-actions' ) );
+
+			// Store the date inputs.
+			component.schedule.inputs = component.schedule.template.find( '.date-input' );
+
+			component.schedule.inputs.on( 'input', function() {
+				component.populateSetting();
+			} );
+
+			component.schedule.inputs.on( 'blur', function() {
+				component.populateInputs();
+				component.populateSetting();
+			} );
+
+			component.updateCountdown();
+
+			component.schedule.template.find( '.reset-time a' ).on( 'click', function( event ) {
+				event.preventDefault();
+
+				component.updateSchedule();
+				component.populateSetting();
+			} );
+		}
+
+		// Listen for click events.
+		$( '#snapshot-schedule-button' ).on( 'click', function( event ) {
+			event.preventDefault();
+			component.schedule.template.toggle();
+		} );
+
+		api.state( 'snapshot-saved' ).bind( function( saved ) {
+			if ( saved ) {
+				component.updateSchedule();
+			}
+		} );
+
+		api.bind( 'change', function() {
+			component.data.dirty = true;
+		} );
+
+		api.state( 'saved' ).bind( function( saved ) {
+			if ( saved && ! _.isEmpty( component.schedule.template ) ) {
+				component.schedule.template.hide();
+			}
+		} );
+
+		api.state( 'snapshot-exists' ).bind( function( exists ) {
+			if ( exists && ! _.isEmpty( component.schedule.template ) ) {
+				component.updateSchedule();
+			} else {
+				component.schedule.template.hide();
+			}
+		} );
+	};
+
+	/**
+	 * Updates snapshot schedule with `component.data`.
+	 *
+	 * @return {void}
+	 */
+	component.updateSchedule = function updateSchedule() {
+		var parsed,
+			sliceBegin = 0,
+			sliceEnd = -2;
+
+		if ( _.isEmpty( component.schedule.template ) ) {
+			return;
+		}
+
+		if ( '0000-00-00 00:00:00' === component.data.publishDate ) {
+			component.data.publishDate = component.getCurrentTime();
+		}
+
+		// Normalize date with seconds removed.
+		component.data.publishDate = component.data.publishDate.slice( sliceBegin, sliceEnd ) + '00';
+
+		// Update date controls.
+		component.schedule.template.find( 'a.snapshot-edit-link' ).attr( 'href', component.data.editLink );
+		parsed = component.parseDateTime( component.data.publishDate );
+
+		component.schedule.inputs.each( function() {
+			var input = $( this ),
+				fieldName = input.data( 'date-input' );
+
+			$( this ).val( parsed[fieldName] );
+		} );
+
+		component.populateSetting();
+	};
+
+	/**
+	 * Update the scheduled countdown text.
+	 *
+	 * Hides countdown if post_status is not already future.
+	 * Toggles the countdown if there is no remaining time.
+	 *
+	 * @returns {boolean} True if date inputs are valid.
+	 */
+	component.updateCountdown = function updateCountdown() {
+		var countdown = component.schedule.template.find( '.snapshot-scheduled-countdown' ),
+			countdownTemplate = wp.template( 'snapshot-scheduled-countdown' ),
+			dateTimeFromInput = component.getDateFromInputs(),
+			millisecondsDivider = 1000,
+			remainingTime;
+
+		if ( ! dateTimeFromInput ) {
+			return false;
+		}
+
+		remainingTime = dateTimeFromInput.valueOf();
+		remainingTime -= component.dateValueOf( component.getCurrentTime() );
+		remainingTime = Math.ceil( remainingTime / millisecondsDivider );
+
+		if ( 0 < remainingTime ) {
+			countdown.text( countdownTemplate( {
+				remainingTime: remainingTime
+			} ) );
+			countdown.show();
+		} else {
+			countdown.hide();
+		}
+
+		return true;
+	};
+
 	/**
 	 * Silently update the saved state to be true without triggering the
 	 * changed event so that the AYS beforeunload dialog won't appear
@@ -457,10 +493,10 @@
 				component.data.editLink = response.edit_link;
 			}
 			if ( response.snapshot_publish_date ) {
-				component.data.snapshotPublishDate = response.snapshot_publish_date;
+				component.data.publishDate = response.snapshot_publish_date;
 			}
-			component.updateSnapshotScheduleSection();
-			component.data.isSnapshotHasUnsavedChanges = false;
+			component.updateSchedule();
+			component.data.dirty = false;
 
 			// @todo Remove privateness from _handleSettingValidities in Core.
 			if ( api._handleSettingValidities && response.setting_validities ) {
@@ -474,9 +510,10 @@
 		request.done( function() {
 			var url = api.previewer.previewUrl(),
 				regex = new RegExp( '([?&])customize_snapshot_uuid=.*?(&|$)', 'i' ),
-				separator = url.indexOf( '?' ) !== -1 ? '&' : '?',
+				notFound = -1,
+				separator = url.indexOf( '?' ) !== notFound ? '&' : '?',
 				customizeUrl = window.location.href,
-				customizeSeparator = customizeUrl.indexOf( '?' ) !== -1 ? '&' : '?';
+				customizeSeparator = customizeUrl.indexOf( '?' ) !== notFound ? '&' : '?';
 
 			if ( url.match( regex ) ) {
 				url = url.replace( regex, '$1customize_snapshot_uuid=' + encodeURIComponent( component.data.uuid ) + '$2' );
@@ -559,18 +596,24 @@
 	 * @returns {Date|null} Date created from inputs or null if invalid date.
 	 */
 	component.getDateFromInputs = function getDateFromInputs() {
-		var control = component, date;
+		var template = component.schedule.template,
+			monthOffset = 1,
+			date;
+
 		date = new Date(
-			parseInt( control.dateComponentInputs.year.val(), 10 ),
-			parseInt( control.dateComponentInputs.month.val(), 10 ) - 1,
-			parseInt( control.dateComponentInputs.day.val(), 10 ),
-			parseInt( control.dateComponentInputs.hour.val(), 10 ),
-			parseInt( control.dateComponentInputs.minute.val(), 10 )
+			parseInt( template.find( '[data-date-input="year"]' ).val(), 10 ),
+			parseInt( template.find( '[data-date-input="month"]' ).val(), 10 ) - monthOffset,
+			parseInt( template.find( '[data-date-input="day"]' ).val(), 10 ),
+			parseInt( template.find( '[data-date-input="hour"]' ).val(), 10 ),
+			parseInt( template.find( '[data-date-input="minute"]' ).val(), 10 )
 		);
+
 		if ( isNaN( date.valueOf() ) ) {
 			return null;
 		}
+
 		date.setSeconds( 0 );
+
 		return date;
 	};
 
@@ -582,10 +625,13 @@
 	 */
 	component.parseDateTime = function parseDateTime( datetime ) {
 		var matches = datetime.match( /^(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)$/ );
+
 		if ( ! matches ) {
 			return null;
 		}
+
 		matches.shift();
+
 		return {
 			year: matches.shift(),
 			month: matches.shift(),
@@ -599,15 +645,19 @@
 	/**
 	 * Format a Date Object. Returns 'Y-m-d H:i:s' format.
 	 *
+	 * @props http://stackoverflow.com/questions/10073699/pad-a-number-with-leading-zeros-in-javascript#comment33639551_10073699
+	 *
 	 * @param {Date} date A Date object.
 	 * @returns {string} A formatted date String.
 	 */
 	component.formatDate = function formatDate( date ) {
-		var formattedDate, yearLength = 4, nonYearLength = 2;
+		var formattedDate,
+			yearLength = 4,
+			nonYearLength = 2,
+			monthOffset = 1;
 
-		// Props: http://stackoverflow.com/questions/10073699/pad-a-number-with-leading-zeros-in-javascript#comment33639551_10073699
 		formattedDate = ( '0000' + date.getFullYear() ).substr( -yearLength, yearLength );
-		formattedDate += '-' + ( '00' + ( date.getMonth() + 1 ) ).substr( -nonYearLength, nonYearLength );
+		formattedDate += '-' + ( '00' + ( date.getMonth() + monthOffset ) ).substr( -nonYearLength, nonYearLength );
 		formattedDate += '-' + ( '00' + date.getDate() ).substr( -nonYearLength, nonYearLength );
 		formattedDate += ' ' + ( '00' + date.getHours() ).substr( -nonYearLength, nonYearLength );
 		formattedDate += ':' + ( '00' + date.getMinutes() ).substr( -nonYearLength, nonYearLength );
@@ -622,21 +672,25 @@
 	 * @returns {boolean} Whether the inputs were populated.
 	 */
 	component.populateInputs = function populateInputs() {
-		var parsed, setComponentInputValue;
-		if ( component.dateInputs.is( ':focus' ) || '0000-00-00 00:00:00' === component.data.snapshotPublishDate ) {
+		var parsed;
+
+		if ( component.schedule.inputs.is( ':focus' ) || '0000-00-00 00:00:00' === component.data.publishDate ) {
 			return false;
 		}
-		parsed = component.parseDateTime( component.data.snapshotPublishDate );
+
+		parsed = component.parseDateTime( component.data.publishDate );
 		if ( ! parsed ) {
 			return false;
 		}
-		setComponentInputValue = function( value, inputName ) {
-			var input = component.dateComponentInputs[inputName];
-			if ( input && ! input.is( 'select' ) && '' === input.val() ) {
-				input.val( value );
+
+		component.schedule.inputs.each( function() {
+			var input = $( this ),
+				fieldName = input.data( 'date-input' );
+
+			if ( ! $( this ).is( 'select' ) && '' === $( this ).val() ) {
+				$( this ).val( parsed[fieldName] );
 			}
-		};
-		_.each( parsed, setComponentInputValue );
+		} );
 		return true;
 	};
 
@@ -646,97 +700,95 @@
 	 * @returns {boolean} Whether the date inputs currently represent a valid date.
 	 */
 	component.populateSetting = function populateSetting() {
-		var date, save, isScheduleDateUpdated;
-		date = component.getDateFromInputs();
+		var date = component.getDateFromInputs(),
+			save = $( '#snapshot-save' ),
+			scheduled;
+
 		if ( ! date ) {
 			return false;
 		}
-		save = $( '#snapshot-save' );
+
 		date.setSeconds( 0 );
-		isScheduleDateUpdated = component.formatDate( date ) !== component.data.snapshotPublishDate;
-		if ( component.isScheduleDateFuture() && save.length ) {
+		scheduled = component.formatDate( date ) !== component.data.publishDate;
+
+		if ( save.length ) {
 
 			// Change update button to schedule.
-			save.html( component.data.i18n.scheduleButton );
-			if ( isScheduleDateUpdated || component.data.isSnapshotHasUnsavedChanges ) {
-				save.prop( 'disabled', false );
+			if ( component.isFutureDate() ) {
+				save.html( component.data.i18n.scheduleButton );
 			} else {
-				save.prop( 'disabled', true );
+				save.html( component.data.i18n.updateButton );
 			}
-		} else if ( save.length ) {
-			save.html( component.data.i18n.updateButton );
-			if ( component.data.isSnapshotHasUnsavedChanges || isScheduleDateUpdated ) {
+
+			if ( scheduled || component.data.dirty ) {
 				save.prop( 'disabled', false );
 			} else {
 				save.prop( 'disabled', true );
 			}
 		}
-		component.updateScheduledCountdown();
-		if ( isScheduleDateUpdated ) {
-			component.resetTimeWrap.show();
-		} else {
-			component.resetTimeWrap.hide();
-		}
-		return true;
 
+		component.updateCountdown();
+		component.schedule.template.find( '.reset-time' ).toggle( scheduled );
+
+		return true;
 	};
 
 	/**
-	 * Check if snapshot schedule date is in future date
+	 * Check if the schedule date is in the future.
 	 *
-	 * @returns {boolean} if date in input controls is of future
+	 * @returns {boolean} True if future date.
 	 */
-	component.isScheduleDateFuture = function isScheduleDateFuture() {
-		var date, remainingTime;
-		date = component.getDateFromInputs();
+	component.isFutureDate = function isFutureDate() {
+		var date = component.getDateFromInputs(),
+			millisecondsDivider = 1000,
+			remainingTime;
+
 		if ( ! date ) {
 			return false;
 		}
-		remainingTime = ( new Date( date ) ).valueOf();
-		remainingTime -= ( new Date( component.getCurrentTime() ) ).valueOf();
-		remainingTime = Math.ceil( remainingTime / 1000 );
-		return remainingTime > 0;
+
+		remainingTime = component.dateValueOf( date );
+		remainingTime -= component.dateValueOf( component.getCurrentTime() );
+		remainingTime = Math.ceil( remainingTime / millisecondsDivider );
+
+		return ( 0 < remainingTime );
 	};
 
 	/**
-	 * Get current date/time in the site's timezone, as does the current_time( 'mysql', false ) function in PHP.
+	 * Get current date/time in the site's timezone.
+	 *
+	 * Same functionality as the `current_time( 'mysql', false )` function in PHP.
 	 *
 	 * @returns {string} Current datetime string.
 	 */
 	component.getCurrentTime = function getCurrentTime() {
-		var currentDate, currentTimestamp, timestampDifferential;
-		currentTimestamp = ( new Date() ).valueOf();
-		currentDate = new Date( component.data.initialServerDate );
+		var currentDate = new Date( component.data.initialServerDate ),
+			currentTimestamp = component.dateValueOf(),
+			timestampDifferential;
+
 		timestampDifferential = currentTimestamp - component.data.initialClientTimestamp;
 		timestampDifferential += component.data.initialClientTimestamp - component.data.initialServerTimestamp;
 		currentDate.setTime( currentDate.getTime() + timestampDifferential );
+
 		return component.formatDate( currentDate );
 	};
 
 	/**
-	 * Update the scheduled countdown.
-	 *
-	 * Hides countdown if post_status is not already future.
-	 * Toggles the countdown if there is no remaining time.
+	 * Get the primitive value of a Date object.
 	 *
-	 * @returns {boolean} if date input have valid time.
+	 * @param {string} dateString The post status for the snapshot.
+	 * @return {string}
 	 */
-	component.updateScheduledCountdown = function updateScheduledCountdown() {
-		var remainingTime, dateTimeFromInput;
-		dateTimeFromInput = component.getDateFromInputs();
-		if ( ! dateTimeFromInput ) {
-			return false;
-		}
-		remainingTime = dateTimeFromInput.valueOf();
-		remainingTime -= ( new Date( component.getCurrentTime() ) ).valueOf();
-		remainingTime = Math.ceil( remainingTime / 1000 );
-		if ( remainingTime > 0 ) {
-			component.scheduledCountdownContainer.text( component.scheduledCountdownTemplate( { remainingTime: remainingTime } ) );
-			component.scheduledCountdownContainer.show();
+	component.dateValueOf = function( dateString ) {
+		var date;
+
+		if ( _.isUndefined( dateString ) ) {
+			date = new Date();
 		} else {
-			component.scheduledCountdownContainer.hide();
+			date = new Date( dateString );
 		}
-		return true;
+
+		return date.valueOf();
 	};
 
 	component.init();
diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index a8424329..20db2d7e 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -537,8 +537,8 @@ public function enqueue_scripts() {
 			'action' => self::AJAX_ACTION,
 			'uuid' => $this->snapshot ? $this->snapshot->uuid() : self::generate_uuid(),
 			'editLink' => $this->snapshot ? get_edit_post_link( $post, 'raw' ) : '',
-			'snapshotPublishDate' => $this->snapshot ? $post->post_date : '',
-			'snapshotStatus' => $this->snapshot ? $post->post_status : '',
+			'publishDate' => $this->snapshot ? $post->post_date : '',
+			'postStatus' => $this->snapshot ? $post->post_status : '',
 			'currentUserCanPublish' => current_user_can( 'customize_publish' ),
 			'initialServerDate' => current_time( 'mysql', false ),
 			'initialServerTimestamp' => floor( microtime( true ) * 1000 ),
@@ -1086,6 +1086,21 @@ public function replace_customize_link( $wp_admin_bar ) {
 	 */
 	public function render_templates() {
 		$data = $this->get_month_choices();
+
+		$tz_string = get_option( 'timezone_string' );
+		if ( $tz_string ) {
+			$tz = new \DateTimezone( $tz_string );
+			$formatted_gmt_offset = $this->format_gmt_offset( $tz->getOffset( new \DateTime() ) / 3600 );
+			$tz_name = str_replace( '_', ' ', $tz->getName() );
+
+			/* translators: 1: timezone name, 2: gmt offset  */
+			$date_control_description = sprintf( __( 'This site\'s dates are in the %1$s timezone (currently UTC%2$s).', 'customize-snapshots' ), $tz_name, $formatted_gmt_offset );
+		} else {
+			$formatted_gmt_offset = $this->format_gmt_offset( get_option( 'gmt_offset' ) );
+
+			/* translators: %s: gmt offset  */
+			$date_control_description = sprintf( __( 'Dates are in UTC%s.', 'customize-snapshots' ), $formatted_gmt_offset );
+		}
 		?>
 		<script type="text/html" id="tmpl-snapshot-preview-link">
 			<a href="#" target="frontend-preview" id="snapshot-preview-link" class="dashicons dashicons-welcome-view-site" title="<?php esc_attr_e( 'View on frontend', 'customize-snapshots' ) ?>">
@@ -1097,56 +1112,43 @@ public function render_templates() {
 			<a href="#" id="snapshot-schedule-button" class="dashicons dashicons-calendar-alt" title="<?php esc_attr_e( 'Schedule Snapshot','customize-snapshots' ); ?>"></a>
 		</script>
 
-		<script type="text/html" id="tmpl-snapshot-schedule-accordion">
-			<div id="snapshot-schedule-section" class="accordion-section">
+		<script type="text/html" id="tmpl-snapshot-schedule">
+			<div id="snapshot-schedule">
 				<div class="snapshot-schedule-title">
 					<h3>
 						<?php esc_html_e( 'Schedule Snapshot', 'customize-snapshots' ); ?>
-						<span class="wrap-reset-time">(<a href="#" class="reset-time"><?php esc_html_e( 'Reset', 'customize-snapshots' ) ?></a>)</span>
+						<span class="reset-time">(<a href="#"><?php esc_html_e( 'Reset', 'customize-snapshots' ) ?></a>)</span>
 					</h3>
 					<span class="snapshot-schedule-description">
-						<?php
-						$tz_string = get_option( 'timezone_string' );
-						if ( $tz_string ) {
-							$tz = new \DateTimezone( $tz_string );
-							$formatted_gmt_offset = $this->format_gmt_offset( $tz->getOffset( new \DateTime() ) / 3600 );
-							$tz_name = str_replace( '_', ' ', $tz->getName() );
-
-							/* translators: 1: timezone name, 2: gmt offset  */
-							$date_control_description = sprintf( __( 'This site\'s dates are in the %1$s timezone (currently UTC%2$s).', 'customize-snapshots' ), $tz_name, $formatted_gmt_offset );
-						} else {
-							$formatted_gmt_offset = $this->format_gmt_offset( get_option( 'gmt_offset' ) );
-
-							/* translators: %s: gmt offset  */
-							$date_control_description = sprintf( __( 'Dates are in UTC%s.', 'customize-snapshots' ), $formatted_gmt_offset );
-						}
-						?>
 						<span class="snapshot-scheduled-countdown"></span>
 						<span class="timezone-info"><?php echo esc_html( $date_control_description ); ?></span>
 					</span>
 					<a href="{{ data.editLink }}" class="dashicons dashicons-edit snapshot-edit-link" aria-expanded="false"></a>
 				</div>
 				<div class="snapshot-schedule-control">
-					<# _.defaults( data, <?php echo wp_json_encode( $data ) ?> );
-						data.input_id_post_date = 'input-' + String( Math.random() );
-						data.input_id_post_date_gmt = 'input-' + String( Math.random() );
-						#>
-						<select id="{{ data.input_id }}" class="date-input month" data-component="month">
-							<# _.each( data.month_choices, function( choice ) { #>
-								<# if ( _.isObject( choice ) && ! _.isUndefined( choice.text ) && ! _.isUndefined( choice.value ) ) {
-									text = choice.text;
-									value = choice.value;
-									}
-									#>
-								<option value="{{ value }}" <# if (choice.value == data.month) { #>
-										selected="selected"
-										<# } #>>{{ text }}</option>
-							<# } ); #>
-						</select>
-						<input type="number" size="2" maxlength="2" autocomplete="off" class="date-input day" data-component="day" min="1" max="31" value="{{ data.day }}" />,
-						<input type="number" size="4" maxlength="4" autocomplete="off" class="date-input year" data-component="year" min="<?php echo esc_attr( date( 'Y' ) ); ?>" value="{{ data.year }}" max="9999" />
-						@ <input type="number" size="2" maxlength="2" autocomplete="off" class="date-input hour" data-component="hour" min="0" max="23" value="{{ data.hour }}" />:<?php
-						?><input type="number" size="2" maxlength="2" autocomplete="off" class="date-input minute" data-component="minute" min="0" max="59" value="{{ data.minute }}" />
+					<#
+					_.defaults( data, <?php echo wp_json_encode( $data ) ?> );
+					data.input_id_post_date = 'input-' + String( Math.random() );
+					data.input_id_post_date_gmt = 'input-' + String( Math.random() );
+					#>
+					<select id="{{ data.input_id }}" class="date-input month" data-date-input="month">
+					<# _.each( data.month_choices, function( choice ) { #>
+						<# if ( _.isObject( choice ) && ! _.isUndefined( choice.text ) && ! _.isUndefined( choice.value ) ) {
+							text = choice.text;
+							value = choice.value;
+						} #>
+						<option value="{{ value }}" 
+							<# if (choice.value == data.month) { #>
+								selected="selected"
+							<# } #>>
+							{{ text }}
+						</option>
+					<# } ); #>
+					</select>
+					<input type="number" size="2" maxlength="2" autocomplete="off" class="date-input day" data-date-input="day" min="1" max="31" value="{{ data.day }}" />,
+					<input type="number" size="4" maxlength="4" autocomplete="off" class="date-input year" data-date-input="year" min="<?php echo esc_attr( date( 'Y' ) ); ?>" value="{{ data.year }}" max="9999" /> @
+					<input type="number" size="2" maxlength="2" autocomplete="off" class="date-input hour" data-date-input="hour" min="0" max="23" value="{{ data.hour }}" />:<?php
+					?><input type="number" size="2" maxlength="2" autocomplete="off" class="date-input minute" data-date-input="minute" min="0" max="59" value="{{ data.minute }}" />
 				</div>
 			</div>
 		</script>

From d7848cf09ba19c5e1dc0da0d84d34a88cf376ccc Mon Sep 17 00:00:00 2001
From: Derek Herman <derek@valendesigns.com>
Date: Fri, 5 Aug 2016 02:16:13 -0700
Subject: [PATCH 32/59] Fix eslint issues

---
 js/customize-snapshots.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 24c6865f..04bc255c 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -751,7 +751,7 @@
 		remainingTime -= component.dateValueOf( component.getCurrentTime() );
 		remainingTime = Math.ceil( remainingTime / millisecondsDivider );
 
-		return ( 0 < remainingTime );
+		return 0 < remainingTime;
 	};
 
 	/**
@@ -777,7 +777,7 @@
 	 * Get the primitive value of a Date object.
 	 *
 	 * @param {string} dateString The post status for the snapshot.
-	 * @return {string}
+	 * @return {object|string} The primitive value or date object.
 	 */
 	component.dateValueOf = function( dateString ) {
 		var date;

From 0f5634de9009fa32dd4c1d98c4f017d8f2cc645b Mon Sep 17 00:00:00 2001
From: Derek Herman <derek@valendesigns.com>
Date: Fri, 5 Aug 2016 02:16:53 -0700
Subject: [PATCH 33/59] Fix typo

---
 js/customize-snapshots.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 04bc255c..81036ae6 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -777,7 +777,7 @@
 	 * Get the primitive value of a Date object.
 	 *
 	 * @param {string} dateString The post status for the snapshot.
-	 * @return {object|string} The primitive value or date object.
+	 * @returns {object|string} The primitive value or date object.
 	 */
 	component.dateValueOf = function( dateString ) {
 		var date;

From 2772725d5f93a1b189dd01b5869620167b66172f Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Fri, 5 Aug 2016 16:43:03 +0530
Subject: [PATCH 34/59] Reset schedule variables when snapshot publish

---
 js/customize-snapshots.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 81036ae6..d33ef980 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -357,6 +357,8 @@
 		api.state( 'saved' ).bind( function( saved ) {
 			if ( saved && ! _.isEmpty( component.schedule.template ) ) {
 				component.schedule.template.hide();
+				component.data.publishDate = '0000-00-00 00:00:00';
+				component.data.dirty = false;
 			}
 		} );
 

From ccf7a869644bbcb0b208e5843ec2b716c43b0516 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Fri, 5 Aug 2016 19:17:17 +0530
Subject: [PATCH 35/59] Add unit-test for schedule snapshot

---
 ...-class-ajax-customize-snapshot-manager.php | 40 +++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/tests/php/test-class-ajax-customize-snapshot-manager.php b/tests/php/test-class-ajax-customize-snapshot-manager.php
index e263f42e..096482ad 100644
--- a/tests/php/test-class-ajax-customize-snapshot-manager.php
+++ b/tests/php/test-class-ajax-customize-snapshot-manager.php
@@ -418,4 +418,44 @@ function make_save_snapshot_ajax_call() {
 			unset( $e );
 		}
 	}
+
+	/**
+	 * Testing schedule Snapshot
+	 */
+	function test_ajax_update_snapshot_schedule() {
+		unset( $GLOBALS['wp_customize'] );
+		remove_all_actions( 'wp_ajax_' . Customize_Snapshot_Manager::AJAX_ACTION );
+
+		$setting_key = 'anyonecanedit';
+		$tomorrow = date( 'Y-m-d H:i:s', time() + 86400 );
+		$this->set_current_user( 'administrator' );
+		$this->set_input_vars( array(
+			'action' => Customize_Snapshot_Manager::AJAX_ACTION,
+			'nonce' => wp_create_nonce( Customize_Snapshot_Manager::AJAX_ACTION ),
+			'customize_snapshot_uuid' => self::UUID,
+			'customized' => wp_json_encode( array( $setting_key => 'Hello' ) ),
+			'status' => 'future',
+			'publish_date' => $tomorrow, // Tomorrow.
+		) );
+
+		$this->plugin = new Plugin();
+		$this->plugin->init();
+		$this->add_setting();
+
+		$this->make_ajax_call( Customize_Snapshot_Manager::AJAX_ACTION );
+		$post_id = get_plugin_instance()->customize_snapshot_manager->post_type->find_post( self::UUID );
+		$expected_results = array(
+			'success' => true,
+			'data' => array(
+				'errors' => null,
+				'setting_validities' => array( $setting_key => true ),
+				'edit_link' => get_edit_post_link( $post_id, 'raw' ),
+				'snapshot_publish_date' => $tomorrow,
+			),
+		);
+		// Get the results.
+		$response = json_decode( $this->_last_response, true );
+		$this->assertSame( $expected_results, $response );
+		$this->assertEquals( 'future', get_post_status( $post_id ) );
+	}
 }

From ee64c74db859bdba99f7b05969e536bbfcb43d7a Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Fri, 5 Aug 2016 20:03:37 +0530
Subject: [PATCH 36/59] Fix unit test for multisite

---
 tests/php/test-class-ajax-customize-snapshot-manager.php | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/tests/php/test-class-ajax-customize-snapshot-manager.php b/tests/php/test-class-ajax-customize-snapshot-manager.php
index 096482ad..d21ca720 100644
--- a/tests/php/test-class-ajax-customize-snapshot-manager.php
+++ b/tests/php/test-class-ajax-customize-snapshot-manager.php
@@ -453,6 +453,10 @@ function test_ajax_update_snapshot_schedule() {
 				'snapshot_publish_date' => $tomorrow,
 			),
 		);
+		require_once ABSPATH . WPINC . '/class-wp-customize-manager.php';
+		if ( ! method_exists( 'WP_Customize_Manager', 'prepare_setting_validity_for_js' ) ) {
+			unset( $expected_results['data']['errors'], $expected_results['data']['setting_validities'] );
+		}
 		// Get the results.
 		$response = json_decode( $this->_last_response, true );
 		$this->assertSame( $expected_results, $response );

From 5640162f8ad77f916ced078e0cefaab030c7ef8b Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Fri, 5 Aug 2016 20:11:30 +0530
Subject: [PATCH 37/59] Fix unit test for multisite for real

---
 tests/php/test-class-ajax-customize-snapshot-manager.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/php/test-class-ajax-customize-snapshot-manager.php b/tests/php/test-class-ajax-customize-snapshot-manager.php
index d21ca720..5e8b6d0a 100644
--- a/tests/php/test-class-ajax-customize-snapshot-manager.php
+++ b/tests/php/test-class-ajax-customize-snapshot-manager.php
@@ -455,7 +455,7 @@ function test_ajax_update_snapshot_schedule() {
 		);
 		require_once ABSPATH . WPINC . '/class-wp-customize-manager.php';
 		if ( ! method_exists( 'WP_Customize_Manager', 'prepare_setting_validity_for_js' ) ) {
-			unset( $expected_results['data']['errors'], $expected_results['data']['setting_validities'] );
+			unset( $expected_results['data']['setting_validities'] );
 		}
 		// Get the results.
 		$response = json_decode( $this->_last_response, true );

From 976c740cc262f71f6b58d6a5cd2145d25858df18 Mon Sep 17 00:00:00 2001
From: Derek Herman <derek@valendesigns.com>
Date: Fri, 5 Aug 2016 17:36:49 -0700
Subject: [PATCH 38/59] Ensure a scheduled Snapshot is published after the
 Customizer is saved

---
 js/customize-snapshots.js                |  5 ++---
 php/class-customize-snapshot-manager.php | 13 +++++++++++--
 2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index d33ef980..cd030633 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -332,9 +332,7 @@
 
 			component.schedule.template.find( '.reset-time a' ).on( 'click', function( event ) {
 				event.preventDefault();
-
 				component.updateSchedule();
-				component.populateSetting();
 			} );
 		}
 
@@ -356,8 +354,9 @@
 
 		api.state( 'saved' ).bind( function( saved ) {
 			if ( saved && ! _.isEmpty( component.schedule.template ) ) {
+				component.data.publishDate = component.getCurrentTime();
+				component.updateSchedule();
 				component.schedule.template.hide();
-				component.data.publishDate = '0000-00-00 00:00:00';
 				component.data.dirty = false;
 			}
 		} );
diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index 20db2d7e..32be9564 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -626,9 +626,18 @@ function( $value ) {
 		}
 
 		if ( ! $this->snapshot->post() || 'publish' !== $this->snapshot->post()->post_status ) {
-			$r = $this->snapshot->save( array(
+			$args = array(
 				'status' => 'publish',
-			) );
+			);
+
+			// Ensure a scheduled Snapshot is published.
+			if ( 'future' === $this->snapshot->post()->post_status ) {
+				$args['edit_date'] = true;
+				$args['post_date'] = current_time( 'mysql', false );
+				$args['post_date_gmt'] = current_time( 'mysql', true );
+			}
+
+			$r = $this->snapshot->save( $args );
 			if ( is_wp_error( $r ) ) {
 				add_filter( 'customize_save_response', function( $response ) use ( $r, $that ) {
 					$response['snapshot_errors'] = $that->prepare_errors_for_response( $r );

From 7d1a8ccbb6fda0eb15df26d306563e6e8a3e8c7d Mon Sep 17 00:00:00 2001
From: Derek Herman <derek@valendesigns.com>
Date: Fri, 5 Aug 2016 17:44:21 -0700
Subject: [PATCH 39/59] Fix tests

---
 php/class-customize-snapshot-manager.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index 32be9564..0312eeb4 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -631,7 +631,7 @@ function( $value ) {
 			);
 
 			// Ensure a scheduled Snapshot is published.
-			if ( 'future' === $this->snapshot->post()->post_status ) {
+			if ( $this->snapshot->post() && 'future' === $this->snapshot->post()->post_status ) {
 				$args['edit_date'] = true;
 				$args['post_date'] = current_time( 'mysql', false );
 				$args['post_date_gmt'] = current_time( 'mysql', true );

From a5294bd839025ac19edcae9be342b465d7018558 Mon Sep 17 00:00:00 2001
From: Derek Herman <derek@valendesigns.com>
Date: Fri, 5 Aug 2016 18:24:52 -0700
Subject: [PATCH 40/59] Format template output

---
 php/class-customize-snapshot-manager.php | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index 0312eeb4..bf854973 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -1164,23 +1164,27 @@ public function render_templates() {
 
 		<script id="tmpl-snapshot-scheduled-countdown" type="text/html">
 			<# if ( data.remainingTime < 2 * 60 ) { #>
-			<?php esc_html_e( 'This is scheduled for publishing in about a minute.', 'customize-snapshots' ); ?>
+				<?php esc_html_e( 'This is scheduled for publishing in about a minute.', 'customize-snapshots' ); ?>
+
 			<# } else if ( data.remainingTime < 60 * 60 ) { #>
-			<?php
-			/* translators: %s is a placeholder for the Underscore template var */
-			echo sprintf( esc_html__( 'This snapshot is scheduled for publishing in about %s minutes.', 'customize-snapshots' ), '{{ Math.ceil( data.remainingTime / 60 ) }}' );
-			?>
+				<?php
+				/* translators: %s is a placeholder for the Underscore template var */
+				echo sprintf( esc_html__( 'This snapshot is scheduled for publishing in about %s minutes.', 'customize-snapshots' ), '{{ Math.ceil( data.remainingTime / 60 ) }}' );
+				?>
+
 			<# } else if ( data.remainingTime < 24 * 60 * 60 ) { #>
-			<?php
-			/* translators: %s is a placeholder for the Underscore template var */
-			echo sprintf( esc_html__( 'This snapshot is scheduled for publishing in about %s hours.', 'customize-snapshots' ), '{{ Math.round( data.remainingTime / 60 / 60 * 10 ) / 10 }}' );
-			?>
+				<?php
+				/* translators: %s is a placeholder for the Underscore template var */
+				echo sprintf( esc_html__( 'This snapshot is scheduled for publishing in about %s hours.', 'customize-snapshots' ), '{{ Math.round( data.remainingTime / 60 / 60 * 10 ) / 10 }}' );
+				?>
+
 			<# } else { #>
 				<?php
 				/* translators: %s is a placeholder for the Underscore template var */
 				echo sprintf( esc_html__( 'This snapshot is scheduled for publishing in about %s days.', 'customize-snapshots' ), '{{ Math.round( data.remainingTime / 60 / 60 / 24 * 10 ) / 10 }}' );
 				?>
-				<# } #>
+
+			<# } #>
 		</script>
 
 		<script type="text/html" id="tmpl-snapshot-save">

From f0391dd5655b3a464a12b2543765f615d8044375 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Mon, 8 Aug 2016 12:06:12 +0530
Subject: [PATCH 41/59] Fix edit link visiblity issue on dirty snapshot state

---
 js/customize-snapshots.js | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index cd030633..7b3132db 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -350,6 +350,7 @@
 
 		api.bind( 'change', function() {
 			component.data.dirty = true;
+			component.schedule.template.find( 'a.snapshot-edit-link' ).hide();
 		} );
 
 		api.state( 'saved' ).bind( function( saved ) {
@@ -392,7 +393,9 @@
 		component.data.publishDate = component.data.publishDate.slice( sliceBegin, sliceEnd ) + '00';
 
 		// Update date controls.
-		component.schedule.template.find( 'a.snapshot-edit-link' ).attr( 'href', component.data.editLink );
+		component.schedule.template.find( 'a.snapshot-edit-link' )
+			.attr( 'href', component.data.editLink )
+			.show();
 		parsed = component.parseDateTime( component.data.publishDate );
 
 		component.schedule.inputs.each( function() {

From 00a674cab2ddd981ed5bf9721fb4b67c54f4aa1b Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Mon, 8 Aug 2016 23:30:55 +0530
Subject: [PATCH 42/59] Optimize extend in js

---
 js/customize-snapshots.js | 20 +++++++-------------
 1 file changed, 7 insertions(+), 13 deletions(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 7b3132db..636ca6b7 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -469,24 +469,18 @@
 	 */
 	component.sendUpdateSnapshotRequest = function( options ) {
 		var spinner = $( '#customize-header-actions .spinner' ),
-			request, data, args;
+			request, data;
 
-		args = _.extend(
+		data = _.extend(
 			{
 				status: 'draft'
 			},
-			options
-		);
-
-		data = _.extend( args, {
-			nonce: api.settings.nonce.snapshot,
-			customize_snapshot_uuid: component.data.uuid
-		} );
-
-		data = _.extend(
-			{},
 			api.previewer.query(),
-			data
+			options,
+			{
+				nonce: api.settings.nonce.snapshot,
+				customize_snapshot_uuid: component.data.uuid
+			}
 		);
 		request = wp.ajax.post( 'customize_update_snapshot', data );
 

From 1daff6cbeadc75ba08526c1db1472f03cb33135b Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Mon, 8 Aug 2016 23:54:11 +0530
Subject: [PATCH 43/59] Fix error args not undefine

---
 js/customize-snapshots.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 636ca6b7..40cf91e2 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -529,7 +529,7 @@
 			}
 
 			api.state( 'snapshot-saved' ).set( true );
-			if ( 'pending' === args.status ) {
+			if ( 'pending' === data.status ) {
 				api.state( 'snapshot-submitted' ).set( true );
 			}
 			component.resetSavedStateQuietly();

From 2f3fa54fc0df22164e2c0701b74f9e311d2383d7 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Tue, 9 Aug 2016 11:25:41 +0530
Subject: [PATCH 44/59] Fix UI issue

---
 css/customize-snapshots.css | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/css/customize-snapshots.css b/css/customize-snapshots.css
index 5a872a12..208f56a6 100644
--- a/css/customize-snapshots.css
+++ b/css/customize-snapshots.css
@@ -148,3 +148,7 @@
 	-webkit-appearance: none;
 	margin: 0;
 }
+.wp-full-overlay-sidebar .wp-full-overlay-header{
+	padding-left: 15px !important;
+	z-index: 1000001; !important;
+}
\ No newline at end of file

From 11b5acdee5dbead2d8f7e50a112683fa229e99aa Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Tue, 9 Aug 2016 11:39:36 +0530
Subject: [PATCH 45/59] Remove extra semicolon

---
 css/customize-snapshots.css | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/css/customize-snapshots.css b/css/customize-snapshots.css
index 208f56a6..ffabe742 100644
--- a/css/customize-snapshots.css
+++ b/css/customize-snapshots.css
@@ -150,5 +150,5 @@
 }
 .wp-full-overlay-sidebar .wp-full-overlay-header{
 	padding-left: 15px !important;
-	z-index: 1000001; !important;
+	z-index: 1000001 !important;
 }
\ No newline at end of file

From e98c6f1f04488c5adba427b64b58d3dd11f76049 Mon Sep 17 00:00:00 2001
From: Derek Herman <derek@valendesigns.com>
Date: Tue, 9 Aug 2016 21:24:23 -0700
Subject: [PATCH 46/59] Simplify the z-index issue, only slightly

---
 css/customize-snapshots.css | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/css/customize-snapshots.css b/css/customize-snapshots.css
index ffabe742..ddb771d6 100644
--- a/css/customize-snapshots.css
+++ b/css/customize-snapshots.css
@@ -15,11 +15,13 @@
 #snapshot-preview-link:active {
 	color: #191e23;
 }
+
 #snapshot-save {
 	float: right;
 	margin-top: 9px;
 	margin-right: 9px;
 }
+
 #customize-header-actions:not(.button-added) .button#save {
 	visibility: hidden;
 }
@@ -140,15 +142,22 @@
 	min-width: 10%;
 	width: auto;
 }
+
 .snapshot-schedule-control input.date-input {
 	-moz-appearance: textfield;
 }
+
 .snapshot-schedule-control input.date-input::-webkit-outer-spin-button,
 .snapshot-schedule-control input.date-input::-webkit-inner-spin-button {
 	-webkit-appearance: none;
 	margin: 0;
 }
-.wp-full-overlay-sidebar .wp-full-overlay-header{
-	padding-left: 15px !important;
-	z-index: 1000001 !important;
-}
\ No newline at end of file
+
+.wp-full-overlay .wp-full-overlay-sidebar .wp-full-overlay-header {
+	padding-left: 15px;
+	z-index: 500101;
+}
+
+.select2-container {
+	z-index: 500100 !important;
+}

From 096d44cf6323021531336ca48f793c5ad7eb849c Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Wed, 10 Aug 2016 12:56:07 +0530
Subject: [PATCH 47/59] Add unit-test for get_month_choices and
 override_post_date_default_data

---
 .../test-class-customize-snapshot-manager.php | 27 +++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/tests/php/test-class-customize-snapshot-manager.php b/tests/php/test-class-customize-snapshot-manager.php
index 24854795..93abb875 100644
--- a/tests/php/test-class-customize-snapshot-manager.php
+++ b/tests/php/test-class-customize-snapshot-manager.php
@@ -931,4 +931,31 @@ public function test_render_templates() {
 		$this->assertContains( 'tmpl-snapshot-save', $templates );
 		$this->assertContains( 'tmpl-snapshot-dialog-error', $templates );
 	}
+
+	/**
+	 * Test month choices
+	 *
+	 * @covers Customize_Snapshot_Manager::get_month_choices()
+	 */
+	public function test_get_month_choices() {
+		$data = $this->manager->get_month_choices();
+		$this->assertArrayHasKey( 'month_choices', $data );
+		$this->assertCount( 12, $data['month_choices'] );
+	}
+
+	/**
+	 * Test override post date if empty.
+	 *
+	 * @covers Customize_Snapshot_Manager::override_post_date_default_data()
+	 */
+	public function test_override_post_date_default_data() {
+		$post_id = $this->factory()->post->create();
+		$post = get_post( $post_id );
+		$post->post_date = $post->post_date_gmt = $post->post_modified = $post->post_modified_gmt = '0000-00-00 00:00:00';
+		$this->manager->override_post_date_default_data( $post );
+		$this->assertNotEquals( $post->post_date, '0000-00-00 00:00:00' );
+		$this->assertNotEquals( $post->post_date_gmt, '0000-00-00 00:00:00' );
+		$this->assertNotEquals( $post->post_modified, '0000-00-00 00:00:00' );
+		$this->assertNotEquals( $post->post_modified_gmt, '0000-00-00 00:00:00' );
+	}
 }

From add9311ab1dbbb8f624812eab32ad1d0e860d4d9 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Wed, 10 Aug 2016 13:10:49 +0530
Subject: [PATCH 48/59] Title and screen reader text add

---
 php/class-customize-snapshot-manager.php | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index 80640577..a11e01cb 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -1390,13 +1390,14 @@ public function render_templates() {
 				<div class="snapshot-schedule-title">
 					<h3>
 						<?php esc_html_e( 'Schedule Snapshot', 'customize-snapshots' ); ?>
-						<span class="reset-time">(<a href="#"><?php esc_html_e( 'Reset', 'customize-snapshots' ) ?></a>)</span>
+						<span class="reset-time">(<a href="#" title="<?php esc_attr_e( 'Reset schedule date to original or current date', 'customize-snapshots' ); ?>"><?php esc_html_e( 'Reset', 'customize-snapshots' ) ?></a>)</span>
 					</h3>
 					<span class="snapshot-schedule-description">
 						<span class="snapshot-scheduled-countdown"></span>
 						<span class="timezone-info"><?php echo esc_html( $date_control_description ); ?></span>
 					</span>
-					<a href="{{ data.editLink }}" class="dashicons dashicons-edit snapshot-edit-link" aria-expanded="false"></a>
+					<?php $edit_snapshot_text = __( 'Edit Snapshot', 'customize-snapshots' ); ?>
+					<a href="{{ data.editLink }}" class="dashicons dashicons-edit snapshot-edit-link" title="<?php echo esc_attr( $edit_snapshot_text ); ?>" aria-expanded="false"><span class="screen-reader-text"><?php echo esc_html( $edit_snapshot_text ); ?></span></a>
 				</div>
 				<div class="snapshot-schedule-control">
 					<#

From 00857ba7089d585f8ffcab775147787c00d778a8 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Wed, 10 Aug 2016 13:19:58 +0530
Subject: [PATCH 49/59] Check post exists check more readable

---
 php/class-customize-snapshot-manager.php | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index a11e01cb..441e3d9f 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -643,9 +643,9 @@ public function enqueue_controls_scripts() {
 		$exports = apply_filters( 'customize_snapshots_export_data', array(
 			'action' => self::AJAX_ACTION,
 			'uuid' => $this->snapshot ? $this->snapshot->uuid() : self::generate_uuid(),
-			'editLink' => $this->snapshot ? get_edit_post_link( $post, 'raw' ) : '',
-			'publishDate' => $this->snapshot ? $post->post_date : '',
-			'postStatus' => $this->snapshot ? $post->post_status : '',
+			'editLink' => isset( $post ) ? get_edit_post_link( $post, 'raw' ) : '',
+			'publishDate' => isset( $post->post_date ) ? $post->post_date : '',
+			'postStatus' => isset( $post->post_status ) ? $post->post_status : '',
 			'currentUserCanPublish' => current_user_can( 'customize_publish' ),
 			'initialServerDate' => current_time( 'mysql', false ),
 			'initialServerTimestamp' => floor( microtime( true ) * 1000 ),

From ae7189dbce89e3c49f9f4756b1aab290dded9e47 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Wed, 10 Aug 2016 13:37:37 +0530
Subject: [PATCH 50/59] Fix js dateValueOf function

---
 js/customize-snapshots.js | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/js/customize-snapshots.js b/js/customize-snapshots.js
index 40cf91e2..3934d407 100644
--- a/js/customize-snapshots.js
+++ b/js/customize-snapshots.js
@@ -774,16 +774,18 @@
 	/**
 	 * Get the primitive value of a Date object.
 	 *
-	 * @param {string} dateString The post status for the snapshot.
+	 * @param {string|Date} dateString The post status for the snapshot.
 	 * @returns {object|string} The primitive value or date object.
 	 */
 	component.dateValueOf = function( dateString ) {
 		var date;
 
-		if ( _.isUndefined( dateString ) ) {
-			date = new Date();
-		} else {
+		if ( 'string' === typeof dateString ) {
 			date = new Date( dateString );
+		} else if ( dateString instanceof Date ) {
+			date = dateString;
+		} else {
+			date = new Date();
 		}
 
 		return date.valueOf();

From 59a74848369fc61745862430db45a72a66fa305a Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Wed, 10 Aug 2016 14:40:03 +0530
Subject: [PATCH 51/59] Update unit-test

---
 .../test-class-customize-snapshot-manager.php   | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/tests/php/test-class-customize-snapshot-manager.php b/tests/php/test-class-customize-snapshot-manager.php
index 93abb875..c2fd4d16 100644
--- a/tests/php/test-class-customize-snapshot-manager.php
+++ b/tests/php/test-class-customize-snapshot-manager.php
@@ -921,7 +921,7 @@ public function test_add_post_edit_and_exit_links() {
 	/**
 	 * Test render templates.
 	 *
-	 * @see Customize_Snapshot_Manager::render_templates()
+	 * @covers Customize_Snapshot_Manager::render_templates()
 	 */
 	public function test_render_templates() {
 		ob_start();
@@ -930,6 +930,21 @@ public function test_render_templates() {
 		ob_end_clean();
 		$this->assertContains( 'tmpl-snapshot-save', $templates );
 		$this->assertContains( 'tmpl-snapshot-dialog-error', $templates );
+		$this->assertContains( 'tmpl-snapshot-preview-link', $templates );
+		$this->assertContains( 'tmpl-snapshot-schedule-button', $templates );
+		$this->assertContains( 'tmpl-snapshot-schedule', $templates );
+		$this->assertContains( 'tmpl-snapshot-scheduled-countdown', $templates );
+		$this->assertContains( 'tmpl-snapshot-submit', $templates );
+	}
+
+	/**
+	 * Test format_gmt_offset
+	 *
+	 * @covers Customize_Snapshot_Manager::format_gmt_offset()
+	 */
+	public function test_format_gmt_offset() {
+		$offset = $this->manager->format_gmt_offset( 7.0 );
+		$this->assertEquals( '+7', $offset );
 	}
 
 	/**

From f825bea8a543c4d13cd01163cac01303b11f814e Mon Sep 17 00:00:00 2001
From: Weston Ruter <weston@xwp.co>
Date: Wed, 10 Aug 2016 17:50:21 -0700
Subject: [PATCH 52/59] Add test for
 Customize_Snapshot_Manager::preview_snapshot_settings(); fix cover phpdoc tag

---
 .../test-class-customize-snapshot-manager.php | 104 +++++++++++-------
 tests/php/test-class-post-type.php            |  12 +-
 tests/test-customize-snapshots.php            |   4 +-
 3 files changed, 72 insertions(+), 48 deletions(-)

diff --git a/tests/php/test-class-customize-snapshot-manager.php b/tests/php/test-class-customize-snapshot-manager.php
index c2fd4d16..1513e3e1 100644
--- a/tests/php/test-class-customize-snapshot-manager.php
+++ b/tests/php/test-class-customize-snapshot-manager.php
@@ -190,7 +190,7 @@ function test_construct_with_customize_bootstrapped() {
 	/**
 	 * Tests init hooks.
 	 *
-	 * @covers Customize_Snapshot_Manager::init()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::init()
 	 */
 	public function test_init_hooks() {
 		$manager = new Customize_Snapshot_Manager( $this->plugin );
@@ -219,8 +219,8 @@ public function test_init_hooks() {
 	/**
 	 * Tests init hooks.
 	 *
-	 * @covers Customize_Snapshot_Manager::init()
-	 * @covers Customize_Snapshot_Manager::read_current_snapshot_uuid()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::init()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::read_current_snapshot_uuid()
 	 */
 	public function test_read_current_snapshot_uuid() {
 		$manager = new Customize_Snapshot_Manager( $this->plugin );
@@ -244,8 +244,8 @@ public function test_read_current_snapshot_uuid() {
 	/**
 	 * Tests load_snapshot.
 	 *
-	 * @covers Customize_Snapshot_Manager::init()
-	 * @covers Customize_Snapshot_Manager::load_snapshot()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::init()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::load_snapshot()
 	 */
 	public function test_load_snapshot() {
 		global $wp_actions;
@@ -272,8 +272,8 @@ public function test_load_snapshot() {
 	/**
 	 * Tests setup_preview_ajax_requests.
 	 *
-	 * @covers Customize_Snapshot_Manager::init()
-	 * @covers Customize_Snapshot_Manager::setup_preview_ajax_requests()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::init()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::setup_preview_ajax_requests()
 	 */
 	public function test_setup_preview_ajax_requests() {
 		wp_set_current_user( $this->user_id );
@@ -293,7 +293,7 @@ public function test_setup_preview_ajax_requests() {
 	/**
 	 * Tests override_request_method.
 	 *
-	 * @covers Customize_Snapshot_Manager::override_request_method()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::override_request_method()
 	 */
 	public function test_override_request_method() {
 		global $wp;
@@ -328,7 +328,7 @@ public function test_override_request_method() {
 	/**
 	 * Tests doing_customize_save_ajax.
 	 *
-	 * @covers Customize_Snapshot_Manager::doing_customize_save_ajax()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::doing_customize_save_ajax()
 	 */
 	public function test_doing_customize_save_ajax() {
 		$manager = new Customize_Snapshot_Manager( $this->plugin );
@@ -344,7 +344,7 @@ public function test_doing_customize_save_ajax() {
 	/**
 	 * Tests ensure_customize_manager.
 	 *
-	 * @covers Customize_Snapshot_Manager::ensure_customize_manager()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::ensure_customize_manager()
 	 */
 	public function test_ensure_customize_manager() {
 		global $wp_customize;
@@ -359,7 +359,7 @@ public function test_ensure_customize_manager() {
 	/**
 	 * Tests is_theme_active.
 	 *
-	 * @covers Customize_Snapshot_Manager::is_theme_active()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::is_theme_active()
 	 */
 	public function test_is_theme_active() {
 		global $wp_customize;
@@ -374,7 +374,7 @@ public function test_is_theme_active() {
 	/**
 	 * Tests should_import_and_preview_snapshot.
 	 *
-	 * @covers Customize_Snapshot_Manager::should_import_and_preview_snapshot()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::should_import_and_preview_snapshot()
 	 */
 	public function test_should_import_and_preview_snapshot() {
 		global $pagenow, $wp_customize;
@@ -431,7 +431,7 @@ public function test_should_import_and_preview_snapshot() {
 	/**
 	 * Tests is_previewing_settings.
 	 *
-	 * @covers Customize_Snapshot_Manager::is_previewing_settings()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::is_previewing_settings()
 	 */
 	public function test_is_previewing_settings() {
 		$_REQUEST['customize_snapshot_uuid'] = self::UUID;
@@ -448,7 +448,7 @@ public function test_is_previewing_settings() {
 	/**
 	 * Tests is_previewing_settings.
 	 *
-	 * @covers Customize_Snapshot_Manager::is_previewing_settings()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::is_previewing_settings()
 	 */
 	public function test_is_previewing_settings_via_preview_init() {
 		$manager = new Customize_Snapshot_Manager( $this->plugin );
@@ -460,16 +460,40 @@ public function test_is_previewing_settings_via_preview_init() {
 	/**
 	 * Tests preview_snapshot_settings.
 	 *
-	 * @covers Customize_Snapshot_Manager::preview_snapshot_settings()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::init()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::preview_snapshot_settings()
 	 */
 	public function test_preview_snapshot_settings() {
-		$this->markTestIncomplete();
+		global $wp_actions;
+		$_REQUEST['customize_snapshot_uuid'] = self::UUID;
+		$this->manager->post_type->save( array(
+			'uuid' => self::UUID,
+			'data' => array(
+				'blogname' => array( 'value' => 'Hello' ),
+			),
+			'status' => 'draft',
+		) );
+
+		// Prevent init from calling preview_snapshot_settings straight away.
+		unset( $wp_actions['wp_loaded'] );
+
+		$manager = new Customize_Snapshot_Manager( $this->plugin );
+		$manager->init();
+		$manager->ensure_customize_manager();
+		do_action( 'customize_register', $manager->customize_manager );
+		$manager->customize_manager->set_post_value( 'blogname', 'Hello' );
+		$this->assertFalse( $manager->is_previewing_settings() );
+		$this->assertFalse( $manager->customize_manager->get_setting( 'blogname' )->dirty );
+		$this->assertNotEquals( 'Hello', get_option( 'blogname' ) );
+		$manager->preview_snapshot_settings();
+		$this->assertEquals( 'Hello', get_option( 'blogname' ) );
+		$this->assertTrue( $manager->customize_manager->get_setting( 'blogname' )->dirty );
 	}
 
 	/**
 	 * Tests import_snapshot_data.
 	 *
-	 * @covers Customize_Snapshot_Manager::import_snapshot_data()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::import_snapshot_data()
 	 */
 	public function test_import_snapshot_data() {
 		$this->markTestIncomplete();
@@ -478,7 +502,7 @@ public function test_import_snapshot_data() {
 	/**
 	 * Tests add_widget_setting_preview_filters.
 	 *
-	 * @covers Customize_Snapshot_Manager::add_widget_setting_preview_filters()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::add_widget_setting_preview_filters()
 	 */
 	public function test_add_widget_setting_preview_filters() {
 		$this->markTestIncomplete();
@@ -487,7 +511,7 @@ public function test_add_widget_setting_preview_filters() {
 	/**
 	 * Tests add_nav_menu_setting_preview_filters.
 	 *
-	 * @covers Customize_Snapshot_Manager::add_nav_menu_setting_preview_filters()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::add_nav_menu_setting_preview_filters()
 	 */
 	public function test_add_nav_menu_setting_preview_filters() {
 		$this->markTestIncomplete();
@@ -496,7 +520,7 @@ public function test_add_nav_menu_setting_preview_filters() {
 	/**
 	 * Tests preview_early_nav_menus_in_customizer.
 	 *
-	 * @covers Customize_Snapshot_Manager::preview_early_nav_menus_in_customizer()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::preview_early_nav_menus_in_customizer()
 	 */
 	public function test_preview_early_nav_menus_in_customizer() {
 		$this->markTestIncomplete();
@@ -523,7 +547,7 @@ public function test_add_snapshot_uuid_to_return_url() {
 	/**
 	 * Tests show_theme_switch_error.
 	 *
-	 * @covers Customize_Snapshot_Manager::show_theme_switch_error()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::show_theme_switch_error()
 	 */
 	function test_show_theme_switch_error() {
 		$this->markTestIncomplete();
@@ -532,7 +556,7 @@ function test_show_theme_switch_error() {
 	/**
 	 * Tests get_theme_switch_error.
 	 *
-	 * @covers Customize_Snapshot_Manager::get_theme_switch_error()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::get_theme_switch_error()
 	 */
 	function test_get_theme_switch_error() {
 		$this->markTestIncomplete();
@@ -541,7 +565,7 @@ function test_get_theme_switch_error() {
 	/**
 	 * Tests check_customize_publish_authorization.
 	 *
-	 * @covers Customize_Snapshot_Manager::check_customize_publish_authorization()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::check_customize_publish_authorization()
 	 */
 	function test_check_customize_publish_authorization() {
 		$this->markTestIncomplete();
@@ -600,7 +624,7 @@ function test_enqueue_frontend_scripts() {
 	/**
 	 * Test filter_customize_refresh_nonces.
 	 *
-	 * @covers Customize_Snapshot_Manager::filter_customize_refresh_nonces()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::filter_customize_refresh_nonces()
 	 */
 	function test_filter_customize_refresh_nonces() {
 		$this->markTestIncomplete();
@@ -621,7 +645,7 @@ function test_snapshot() {
 	/**
 	 * Test publish snapshot with customize_save_after.
 	 *
-	 * @covers Customize_Snapshot_Manager::publish_snapshot_with_customize_save_after()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::publish_snapshot_with_customize_save_after()
 	 */
 	function test_publish_snapshot_with_customize_save_after() {
 		wp_set_current_user( $this->user_id );
@@ -645,7 +669,7 @@ function test_publish_snapshot_with_customize_save_after() {
 	/**
 	 * Test prepare_snapshot_post_content_for_publish.
 	 *
-	 * @covers Customize_Snapshot_Manager::prepare_snapshot_post_content_for_publish()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::prepare_snapshot_post_content_for_publish()
 	 */
 	public function test_prepare_snapshot_post_content_for_publish() {
 		$snapshot_manager = get_plugin_instance()->customize_snapshot_manager;
@@ -676,7 +700,7 @@ public function test_prepare_snapshot_post_content_for_publish() {
 	/**
 	 * Test save_settings_with_publish_snapshot.
 	 *
-	 * @covers Customize_Snapshot_Manager::save_settings_with_publish_snapshot()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::save_settings_with_publish_snapshot()
 	 */
 	public function test_save_settings_with_publish_snapshot() {
 		$post_type = $this->manager->post_type;
@@ -740,7 +764,7 @@ public function test_save_settings_with_publish_snapshot() {
 	/**
 	 * Test prepare_errors_for_response.
 	 *
-	 * @covers Customize_Snapshot_Manager::prepare_errors_for_response()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::prepare_errors_for_response()
 	 */
 	public function test_prepare_errors_for_response() {
 		$this->markTestIncomplete();
@@ -749,7 +773,7 @@ public function test_prepare_errors_for_response() {
 	/**
 	 * Tests generate_uuid.
 	 *
-	 * @covers Customize_Snapshot_Manager::generate_uuid()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::generate_uuid()
 	 */
 	public function test_generate_uuid() {
 		$this->markTestIncomplete();
@@ -758,7 +782,7 @@ public function test_generate_uuid() {
 	/**
 	 * Tests is_valid_uuid.
 	 *
-	 * @covers Customize_Snapshot_Manager::is_valid_uuid()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::is_valid_uuid()
 	 */
 	public function test_is_valid_uuid() {
 		$this->markTestIncomplete();
@@ -814,7 +838,7 @@ public function test_customize_menu_return() {
 	/**
 	 * Tests print_admin_bar_styles.
 	 *
-	 * @covers Customize_Snapshot_Manager::print_admin_bar_styles()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::print_admin_bar_styles()
 	 */
 	public function test_print_admin_bar_styles() {
 		$manager = new Customize_Snapshot_Manager( $this->plugin );
@@ -828,7 +852,7 @@ public function test_print_admin_bar_styles() {
 	/**
 	 * Test replace_customize_link.
 	 *
-	 * @covers Customize_Snapshot_Manager::replace_customize_link()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::replace_customize_link()
 	 */
 	public function test_replace_customize_link() {
 		global $wp_admin_bar;
@@ -871,10 +895,10 @@ public function test_replace_customize_link() {
 	/**
 	 * Test misc admin bar extensions.
 	 *
-	 * @covers Customize_Snapshot_Manager::add_post_edit_screen_link()
-	 * @covers Customize_Snapshot_Manager::add_snapshot_exit_link()
-	 * @covers Customize_Snapshot_Manager::add_resume_snapshot_link()
-	 * @covers Customize_Snapshot_Manager::remove_all_non_snapshot_admin_bar_links()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::add_post_edit_screen_link()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::add_snapshot_exit_link()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::add_resume_snapshot_link()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::remove_all_non_snapshot_admin_bar_links()
 	 */
 	public function test_add_post_edit_and_exit_links() {
 		global $wp_admin_bar;
@@ -921,7 +945,7 @@ public function test_add_post_edit_and_exit_links() {
 	/**
 	 * Test render templates.
 	 *
-	 * @covers Customize_Snapshot_Manager::render_templates()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::render_templates()
 	 */
 	public function test_render_templates() {
 		ob_start();
@@ -940,7 +964,7 @@ public function test_render_templates() {
 	/**
 	 * Test format_gmt_offset
 	 *
-	 * @covers Customize_Snapshot_Manager::format_gmt_offset()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::format_gmt_offset()
 	 */
 	public function test_format_gmt_offset() {
 		$offset = $this->manager->format_gmt_offset( 7.0 );
@@ -950,7 +974,7 @@ public function test_format_gmt_offset() {
 	/**
 	 * Test month choices
 	 *
-	 * @covers Customize_Snapshot_Manager::get_month_choices()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::get_month_choices()
 	 */
 	public function test_get_month_choices() {
 		$data = $this->manager->get_month_choices();
@@ -961,7 +985,7 @@ public function test_get_month_choices() {
 	/**
 	 * Test override post date if empty.
 	 *
-	 * @covers Customize_Snapshot_Manager::override_post_date_default_data()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::override_post_date_default_data()
 	 */
 	public function test_override_post_date_default_data() {
 		$post_id = $this->factory()->post->create();
diff --git a/tests/php/test-class-post-type.php b/tests/php/test-class-post-type.php
index b2027887..773c6209 100644
--- a/tests/php/test-class-post-type.php
+++ b/tests/php/test-class-post-type.php
@@ -63,7 +63,7 @@ public function test_register() {
 	/**
 	 * Test filter_post_type_link.
 	 *
-	 * @covers Post_Type::filter_post_type_link()
+	 * @covers CustomizeSnapshots\Post_Type::filter_post_type_link()
 	 */
 	function test_filter_post_type_link() {
 		$post_type = new Post_Type( $this->plugin->customize_snapshot_manager );
@@ -382,7 +382,7 @@ public function test_find_post() {
 	/**
 	 * Test getting the snapshot array out of the post_content.
 	 *
-	 * @covers Post_Type::get_post_content()
+	 * @covers CustomizeSnapshots\Post_Type::get_post_content()
 	 * @expectedException \PHPUnit_Framework_Error_Warning
 	 */
 	public function test_get_post_content() {
@@ -626,7 +626,7 @@ function test_filter_user_has_cap() {
 	/**
 	 * Tests display_post_states.
 	 *
-	 * @covers Post_Type::display_post_states()
+	 * @covers CustomizeSnapshots\Post_Type::display_post_states()
 	 */
 	public function test_display_post_states() {
 		$post_type = new Post_Type( $this->plugin->customize_snapshot_manager );
@@ -646,7 +646,7 @@ public function test_display_post_states() {
 	/**
 	 * Tests show_publish_error_admin_notice.
 	 *
-	 * @covers Post_Type::show_publish_error_admin_notice()
+	 * @covers CustomizeSnapshots\Post_Type::show_publish_error_admin_notice()
 	 */
 	public function test_show_publish_error_admin_notice() {
 		global $current_screen, $post;
@@ -687,7 +687,7 @@ public function test_show_publish_error_admin_notice() {
 	/**
 	 * Tests disable_revision_ui_for_published_posts.
 	 *
-	 * @covers Post_Type::disable_revision_ui_for_published_posts()
+	 * @covers CustomizeSnapshots\Post_Type::disable_revision_ui_for_published_posts()
 	 */
 	public function test_disable_revision_ui_for_published_posts() {
 		$post_type = new Post_Type( $this->plugin->customize_snapshot_manager );
@@ -716,7 +716,7 @@ public function test_disable_revision_ui_for_published_posts() {
 	/**
 	 * Tests hide_disabled_publishing_actions.
 	 *
-	 * @covers Post_Type::hide_disabled_publishing_actions()
+	 * @covers CustomizeSnapshots\Post_Type::hide_disabled_publishing_actions()
 	 */
 	public function test_hide_disabled_publishing_actions() {
 		$post_type = new Post_Type( $this->plugin->customize_snapshot_manager );
diff --git a/tests/test-customize-snapshots.php b/tests/test-customize-snapshots.php
index bf391a53..0b326566 100644
--- a/tests/test-customize-snapshots.php
+++ b/tests/test-customize-snapshots.php
@@ -47,7 +47,7 @@ function test_customize_snapshots_php_version_text() {
 	/**
 	 * Tests is_previewing_settings().
 	 *
-	 * @covers is_previewing_settings()
+	 * @see is_previewing_settings()
 	 */
 	public function test_is_previewing_settings() {
 		$this->assertFalse( is_previewing_settings() );
@@ -58,7 +58,7 @@ public function test_is_previewing_settings() {
 	/**
 	 * Tests current_snapshot_uuid().
 	 *
-	 * @covers current_snapshot_uuid()
+	 * @see current_snapshot_uuid()
 	 */
 	public function test_current_snapshot_uuid() {
 		global $customize_snapshots_plugin;

From 4ea308b203bcc1a8da715932d68b5c499fac99cd Mon Sep 17 00:00:00 2001
From: Weston Ruter <weston@xwp.co>
Date: Wed, 10 Aug 2016 17:57:20 -0700
Subject: [PATCH 53/59] Add test for
 Customize_Snapshot_Manager::import_snapshot_data()

---
 .../test-class-customize-snapshot-manager.php | 31 +++++++++++++++++--
 1 file changed, 28 insertions(+), 3 deletions(-)

diff --git a/tests/php/test-class-customize-snapshot-manager.php b/tests/php/test-class-customize-snapshot-manager.php
index 1513e3e1..cddacdc8 100644
--- a/tests/php/test-class-customize-snapshot-manager.php
+++ b/tests/php/test-class-customize-snapshot-manager.php
@@ -460,7 +460,6 @@ public function test_is_previewing_settings_via_preview_init() {
 	/**
 	 * Tests preview_snapshot_settings.
 	 *
-	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::init()
 	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::preview_snapshot_settings()
 	 */
 	public function test_preview_snapshot_settings() {
@@ -481,7 +480,6 @@ public function test_preview_snapshot_settings() {
 		$manager->init();
 		$manager->ensure_customize_manager();
 		do_action( 'customize_register', $manager->customize_manager );
-		$manager->customize_manager->set_post_value( 'blogname', 'Hello' );
 		$this->assertFalse( $manager->is_previewing_settings() );
 		$this->assertFalse( $manager->customize_manager->get_setting( 'blogname' )->dirty );
 		$this->assertNotEquals( 'Hello', get_option( 'blogname' ) );
@@ -496,7 +494,34 @@ public function test_preview_snapshot_settings() {
 	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::import_snapshot_data()
 	 */
 	public function test_import_snapshot_data() {
-		$this->markTestIncomplete();
+		global $wp_actions;
+		$_REQUEST['customize_snapshot_uuid'] = self::UUID;
+		$this->manager->post_type->save( array(
+			'uuid' => self::UUID,
+			'data' => array(
+				'blogname' => array( 'value' => 'Hello' ),
+				'blogdescription' => array( 'value' => null ),
+			),
+			'status' => 'draft',
+		) );
+
+		// Prevent init from calling import_snapshot_data straight away.
+		unset( $wp_actions['setup_theme'] );
+
+		$manager = new Customize_Snapshot_Manager( $this->plugin );
+		$manager->init();
+		$manager->ensure_customize_manager();
+		do_action( 'customize_register', $manager->customize_manager );
+
+		$this->assertArrayNotHasKey( 'customized', $_POST );
+		$this->assertArrayNotHasKey( 'customized', $_REQUEST );
+		$this->assertArrayNotHasKey( 'blogname', $manager->customize_manager->unsanitized_post_values() );
+		$this->assertArrayNotHasKey( 'blogdescription', $manager->customize_manager->unsanitized_post_values() );
+		$manager->import_snapshot_data();
+		$this->assertArrayHasKey( 'customized', $_POST );
+		$this->assertArrayHasKey( 'customized', $_REQUEST );
+		$this->assertArrayHasKey( 'blogname', $manager->customize_manager->unsanitized_post_values() );
+		$this->assertArrayNotHasKey( 'blogdescription', $manager->customize_manager->unsanitized_post_values() );
 	}
 
 	/**

From 85e590c9c454a8e3a4385400e5a114c621062a48 Mon Sep 17 00:00:00 2001
From: Utkarsh Patel <iamutkarsh@live.com>
Date: Thu, 11 Aug 2016 12:18:35 +0530
Subject: [PATCH 54/59] Add target blank on snapshot-edit link

---
 php/class-customize-snapshot-manager.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index 441e3d9f..7464791f 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -1397,7 +1397,7 @@ public function render_templates() {
 						<span class="timezone-info"><?php echo esc_html( $date_control_description ); ?></span>
 					</span>
 					<?php $edit_snapshot_text = __( 'Edit Snapshot', 'customize-snapshots' ); ?>
-					<a href="{{ data.editLink }}" class="dashicons dashicons-edit snapshot-edit-link" title="<?php echo esc_attr( $edit_snapshot_text ); ?>" aria-expanded="false"><span class="screen-reader-text"><?php echo esc_html( $edit_snapshot_text ); ?></span></a>
+					<a href="{{ data.editLink }}" class="dashicons dashicons-edit snapshot-edit-link" target="_blank" title="<?php echo esc_attr( $edit_snapshot_text ); ?>" aria-expanded="false"><span class="screen-reader-text"><?php echo esc_html( $edit_snapshot_text ); ?></span></a>
 				</div>
 				<div class="snapshot-schedule-control">
 					<#

From 14572fefe826fa9f4fe5101a83d08250d571d04a Mon Sep 17 00:00:00 2001
From: Weston Ruter <weston@xwp.co>
Date: Wed, 10 Aug 2016 23:27:59 -0700
Subject: [PATCH 55/59] Add test for setup_preview_ajax_requests in admin-ajax

---
 .../test-class-customize-snapshot-manager.php | 30 +++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/tests/php/test-class-customize-snapshot-manager.php b/tests/php/test-class-customize-snapshot-manager.php
index cddacdc8..02aa143f 100644
--- a/tests/php/test-class-customize-snapshot-manager.php
+++ b/tests/php/test-class-customize-snapshot-manager.php
@@ -290,6 +290,36 @@ public function test_setup_preview_ajax_requests() {
 		$this->assertEquals( 5, has_action( 'parse_request', array( $manager, 'override_request_method' ) ) );
 	}
 
+
+	/**
+	 * Tests setup_preview_ajax_requests for admin_ajax.
+	 *
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::init()
+	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::setup_preview_ajax_requests()
+	 */
+	public function test_setup_preview_ajax_requests_for_admin_ajax() {
+		global $pagenow;
+		wp_set_current_user( $this->user_id );
+
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+		$_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] = 'GET';
+		$pagenow = 'admin-ajax.php'; // WPCS: Global override ok.
+		set_current_screen( 'admin-ajax' );
+		$this->assertTrue( is_admin() );
+
+		$_REQUEST['wp_customize_preview_ajax'] = 'true';
+		$_POST['customized'] = wp_slash( wp_json_encode( array( 'blogname' => 'Foo' ) ) );
+		$manager = new Customize_Snapshot_Manager( $this->plugin );
+		$manager->init();
+		do_action( 'admin_init' );
+		$this->do_customize_boot_actions( true );
+		$this->assertTrue( is_customize_preview() );
+		$this->assertFalse( has_action( 'shutdown', array( $this->wp_customize, 'customize_preview_signature' ) ) );
+		$this->assertFalse( has_action( 'parse_request', array( $manager, 'override_request_method' ) ) );
+		$this->assertEquals( 'GET', $_SERVER['REQUEST_METHOD'] );
+		$this->assertEquals( 'Foo', get_option( 'blogname' ) );
+	}
+
 	/**
 	 * Tests override_request_method.
 	 *

From 4589448fbd447d7ee0b45232925f1690d2acb1b5 Mon Sep 17 00:00:00 2001
From: Weston Ruter <weston@xwp.co>
Date: Wed, 10 Aug 2016 23:28:36 -0700
Subject: [PATCH 56/59] Test last line of override_request_method

---
 tests/php/test-class-customize-snapshot-manager.php | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/tests/php/test-class-customize-snapshot-manager.php b/tests/php/test-class-customize-snapshot-manager.php
index 02aa143f..554a5907 100644
--- a/tests/php/test-class-customize-snapshot-manager.php
+++ b/tests/php/test-class-customize-snapshot-manager.php
@@ -353,6 +353,10 @@ public function test_override_request_method() {
 		$this->assertEquals( 'foo=1&bar=2', $_SERVER['QUERY_STRING'] );
 		$this->assertArrayHasKey( 'foo', $_GET );
 		$this->assertArrayHasKey( 'bar', $_GET );
+
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+		$_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] = 'PUT';
+		$this->assertFalse( $manager->override_request_method() );
 	}
 
 	/**

From e416cedf76c9a8f446c1fadb703ee064f2f75df1 Mon Sep 17 00:00:00 2001
From: Weston Ruter <weston@xwp.co>
Date: Wed, 10 Aug 2016 23:29:44 -0700
Subject: [PATCH 57/59] Add test for
 Customize_Snapshot_Manager::preview_early_nav_menus_in_customizer()

---
 php/class-customize-snapshot-manager.php      | 11 +++++++
 .../test-class-customize-snapshot-manager.php | 31 ++++++++++++++++++-
 2 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/php/class-customize-snapshot-manager.php b/php/class-customize-snapshot-manager.php
index 7464791f..d02b4369 100644
--- a/php/class-customize-snapshot-manager.php
+++ b/php/class-customize-snapshot-manager.php
@@ -525,6 +525,17 @@ public function preview_early_nav_menus_in_customizer() {
 			);
 			if ( $is_nav_menu_setting ) {
 				$setting->preview();
+
+				/*
+				 * The following is redundant because it will be done later in
+				 * Customize_Snapshot_Manager::preview_snapshot_settings().
+				 * Also note that the $setting instance here will likely be
+				 * blown away inside of WP_Customize_Nav_Menus::customize_register(),
+				 * when add_setting is called there. What matters here is that
+				 * preview() is called on the setting _before_ the logic inside
+				 * WP_Customize_Nav_Menus::customize_register() runs, so that
+				 * the nav menu sections will be created.
+				 */
 				$setting->dirty = true;
 			}
 		}
diff --git a/tests/php/test-class-customize-snapshot-manager.php b/tests/php/test-class-customize-snapshot-manager.php
index 554a5907..1ba34d8a 100644
--- a/tests/php/test-class-customize-snapshot-manager.php
+++ b/tests/php/test-class-customize-snapshot-manager.php
@@ -582,7 +582,36 @@ public function test_add_nav_menu_setting_preview_filters() {
 	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::preview_early_nav_menus_in_customizer()
 	 */
 	public function test_preview_early_nav_menus_in_customizer() {
-		$this->markTestIncomplete();
+		global $pagenow;
+		$pagenow = 'customize.php'; // WPCS: Global override ok.
+		set_current_screen( 'customize' );
+
+		$menu_id = -123;
+		$setting_id = sprintf( 'nav_menu[%d]', $menu_id );
+
+		$_REQUEST['customize_snapshot_uuid'] = self::UUID;
+		$this->manager->post_type->save( array(
+			'uuid' => self::UUID,
+			'data' => array(
+				$setting_id => array(
+					'value' => array(
+						'name' => 'Bar',
+					),
+				),
+			),
+			'status' => 'draft',
+		) );
+
+		$manager = new Customize_Snapshot_Manager( $this->plugin );
+		$manager->init();
+		do_action( 'customize_register', $manager->customize_manager );
+
+		$setting = $manager->customize_manager->get_setting( $setting_id );
+		$this->assertInstanceOf( 'WP_Customize_Nav_Menu_Setting', $setting );
+		$nav_menu = wp_get_nav_menu_object( $menu_id );
+		$this->assertEquals( 'Bar', $nav_menu->name );
+
+		$this->assertInstanceOf( 'WP_Customize_Nav_Menu_Section', $manager->customize_manager->get_section( $setting_id ) );
 	}
 
 	/**

From 1ed08b64ab3126e83c123887a7e4a767f36154fe Mon Sep 17 00:00:00 2001
From: Weston Ruter <weston@xwp.co>
Date: Wed, 10 Aug 2016 23:42:11 -0700
Subject: [PATCH 58/59] Add tests for customize_preview_init and
 enqueue_preview_scripts

---
 .../test-class-customize-snapshot-manager.php | 33 +++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/tests/php/test-class-customize-snapshot-manager.php b/tests/php/test-class-customize-snapshot-manager.php
index 1ba34d8a..71875131 100644
--- a/tests/php/test-class-customize-snapshot-manager.php
+++ b/tests/php/test-class-customize-snapshot-manager.php
@@ -687,6 +687,39 @@ function test_enqueue_controls_scripts() {
 		$this->assertTrue( wp_style_is( 'customize-snapshots', 'enqueued' ) );
 	}
 
+	/**
+	 * Test customize preview init.
+	 *
+	 * @see Customize_Snapshot_Manager::customize_preview_init()
+	 */
+	function test_customize_preview_init() {
+		$manager = new Customize_Snapshot_Manager( $this->plugin );
+		$this->assertFalse( has_action( 'wp_enqueue_scripts', array( $manager, 'enqueue_preview_scripts' ) ) );
+		$manager->customize_preview_init();
+		$this->assertEquals( 10, has_action( 'wp_enqueue_scripts', array( $manager, 'enqueue_preview_scripts' ) ) );
+	}
+
+	/**
+	 * Test enqueue preview scripts.
+	 *
+	 * @see Customize_Snapshot_Manager::enqueue_preview_scripts()
+	 */
+	function test_enqueue_preview_scripts() {
+		$manager = new Customize_Snapshot_Manager( $this->plugin );
+		$manager->ensure_customize_manager();
+		$manager->init();
+		$handle = 'customize-snapshots-preview';
+		$this->assertFalse( wp_scripts()->query( $handle, 'enqueued' ) );
+		$this->assertFalse( wp_styles()->query( $handle, 'enqueued' ) );
+		$manager->enqueue_preview_scripts();
+		$this->assertTrue( wp_scripts()->query( $handle, 'enqueued' ) );
+		$this->assertTrue( wp_styles()->query( $handle, 'enqueued' ) );
+
+		$after = wp_scripts()->get_data( $handle, 'after' );
+		$this->assertNotEmpty( $after );
+		$this->assertContains( 'CustomizeSnapshotsPreview', join( '', $after ) );
+	}
+
 	/**
 	 * Test enqueue frontend scripts.
 	 *

From 535cbd57184d8bdfbc9a3f65aabdb96f62cb0082 Mon Sep 17 00:00:00 2001
From: Weston Ruter <weston@xwp.co>
Date: Thu, 11 Aug 2016 00:12:44 -0700
Subject: [PATCH 59/59] Add test for filter_customize_refresh_nonces

---
 tests/php/test-class-customize-snapshot-manager.php | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/tests/php/test-class-customize-snapshot-manager.php b/tests/php/test-class-customize-snapshot-manager.php
index 71875131..62d60b93 100644
--- a/tests/php/test-class-customize-snapshot-manager.php
+++ b/tests/php/test-class-customize-snapshot-manager.php
@@ -748,7 +748,8 @@ function test_enqueue_frontend_scripts() {
 	 * @covers CustomizeSnapshots\Customize_Snapshot_Manager::filter_customize_refresh_nonces()
 	 */
 	function test_filter_customize_refresh_nonces() {
-		$this->markTestIncomplete();
+		$manager = new Customize_Snapshot_Manager( $this->plugin );
+		$this->assertArrayHasKey( 'snapshot', $manager->filter_customize_refresh_nonces( array() ) );
 	}
 
 	/**