PluginProbe
Edit Flow / 0.11.0
Edit Flow v0.11.0
0.11.1 0.11.0 0.7.2 0.7.3 0.7.4 0.7.5 0.7.6 0.8 0.8.1 0.8.2 0.9 0.9.1 0.9.2 0.9.3 0.9.4 0.9.5 0.9.6 0.9.7 0.9.8 0.9.9 trunk 0.1.5 0.10.0 0.10.1 0.10.2 All 44 releases
edit-flow / modules / calendar / lib / calendar.js

calendar.js in Edit Flow 0.11.0, at modules/calendar/lib/calendar.js

420 lines 12.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 const dispatch = wp.data.dispatch;
2
3 /**
4 * Safely dispatch an action to the edit-flow/calendar store.
5 * Returns a no-op object if the store isn't registered yet.
6 */
7 function getCalendarDispatch() {
8 const calendarStore = dispatch( 'edit-flow/calendar' );
9 if ( calendarStore ) {
10 return calendarStore;
11 }
12 // Return no-op functions if store isn't available
13 return {
14 setCalendarIsLoading: () => {},
15 setPostSaved: () => {},
16 clearCalendarSnackbarMessage: () => {},
17 };
18 }
19
20 jQuery( document ).ready( function ( $ ) {
21 $( 'a.show-more' ).on( 'click', function () {
22 const parent = $( this ).closest( 'td.day-unit' );
23 $( 'ul li', parent ).removeClass( 'hidden' );
24 $( this ).hide();
25 return false;
26 } );
27
28 /**
29 * Listen for click event and subsitute correct type of replacement
30 * html given the input type
31 */
32 $( '.day-unit' ).on( 'click', '.editable-value', function ( event ) {
33 // Reset anything that was currently being edited.
34 reset_editorial_metadata();
35 const t = this;
36 const $editable_el = $( this ).addClass( 'hidden' ).next( '.editable-html' );
37
38 if ( $editable_el.children().first().is( 'select' ) ) {
39 $editable_el.find( 'option' ).each( function () {
40 if ( $( this ).text() == $( t ).text() ) {
41 $( this ).attr( 'selected', 'selected' );
42 }
43 } );
44 }
45
46 $editable_el
47 .removeClass( 'hidden' )
48 .addClass( 'editing' )
49 .closest( '.day-item' )
50 .find( '.item-actions .save' )
51 .removeClass( 'hidden' );
52 } );
53
54 // Save the editorial metadata we've changed
55 $( '.day-unit' ).on( 'click', 'a#save-editorial-metadata', function () {
56 const post_id = $( this ).attr( 'class' ).replace( 'post-', '' );
57 save_editorial_metadata( post_id );
58 return false;
59 } );
60
61 function reset_editorial_metadata() {
62 $( '.editing' ).removeClass( 'editing' ).addClass( 'hidden' ).prev().removeClass( 'hidden' );
63
64 $( '.item-actions .save' ).addClass( 'hidden' );
65 }
66
67 /**
68 * save_editorial_metadata
69 * Save the editorial metadata that's been edited (whatever is marked '#actively-editing').
70 * @param post_id Id of post we're editing
71 */
72 function save_editorial_metadata( post_id ) {
73 const metadata_info = {
74 action: 'ef_calendar_update_metadata',
75 nonce: $( '#ef-calendar-modify' ).val(),
76 metadata_type: $( '.editing' ).attr( 'data-type' ),
77 metadata_value: $( '.editing' ).children().first().val(),
78 metadata_term: $( '.editing' ).attr( 'data-metadataterm' ),
79 post_id,
80 };
81
82 $( '.editing' )
83 .addClass( 'hidden' )
84 .after( $( '<div class="spinner spinner-calendar"></div>' ).show() );
85
86 // Send the request
87 jQuery.ajax( {
88 type: 'POST',
89 url: ajaxurl ? ajaxurl : wpListL10n.url,
90 data: metadata_info,
91 success( x ) {
92 let val = $( '.editing' ).children().first();
93 if ( val.is( 'select' ) ) {
94 val = val.find( 'option:selected' ).text();
95 } else {
96 val = val.val();
97 }
98
99 $( '.editing' ).next().remove();
100
101 $( '.editing' )
102 .addClass( 'hidden' )
103 .removeClass( 'editing' )
104 .prev()
105 .removeClass( 'hidden' )
106 .text( val );
107
108 reset_editorial_metadata();
109 },
110 error( r ) {
111 $( '.editing' )
112 .next( '.spinner' )
113 .replaceWith( '<span class="edit-flow-error-message">Error saving metadata.</span>' );
114 },
115 } );
116 }
117
118 // Hide a message. Used by setTimeout()
119 function edit_flow_calendar_hide_message() {
120 $( '.edit-flow-message' ).fadeOut( function () {
121 $( this ).remove();
122 } );
123 }
124
125 // Close out all of the overlays with your escape key,
126 // or by clicking anywhere other than inside an existing overlay
127 $( document ).on( 'keydown', function ( event ) {
128 if ( event.keyCode == '27' ) {
129 edit_flow_calendar_close_overlays();
130 }
131 } );
132
133 /**
134 * Somewhat hackish way to close overlays automagically when you click outside an overlay
135 */
136 $( document ).on( 'click', function ( event ) {
137 // Did we click on a list item? How do we figure that out?
138 // First let's see if we directly clicked on a .day-item
139 let target = $( event.target );
140 // Case where we've clicked on the list item directly
141 if ( target.hasClass( 'day-item' ) ) {
142 if ( target.hasClass( 'active' ) ) {
143 return;
144 } else if ( target.hasClass( 'post-insert-overlay' ) ) {
145 return;
146 }
147
148 edit_flow_calendar_close_overlays();
149
150 target
151 .addClass( 'active' )
152 .find( '.item-static' )
153 .removeClass( 'item-static' )
154 .addClass( 'item-overlay' );
155
156 return;
157 }
158
159 // Case where we've clicked in the list item
160 target = target.closest( '.day-item' );
161 if ( target.length ) {
162 if ( target.hasClass( 'day-item' ) ) {
163 if ( target.hasClass( 'active' ) ) {
164 return;
165 } else if ( target.hasClass( 'post-insert-overlay' ) ) {
166 return;
167 }
168
169 edit_flow_calendar_close_overlays();
170
171 target
172 .addClass( 'active' )
173 .find( '.item-static' )
174 .removeClass( 'item-static' )
175 .addClass( 'item-overlay' );
176
177 return;
178 }
179 }
180
181 target = $( event.target ).closest( '#ui-datepicker-div' );
182 if ( target.length ) {
183 return;
184 }
185
186 target = $( event.target ).closest( '.post-insert-dialog' );
187 if ( target.length ) {
188 return;
189 }
190
191 edit_flow_calendar_close_overlays();
192 } );
193
194 function edit_flow_calendar_close_overlays() {
195 reset_editorial_metadata();
196 $( '.day-item.active' )
197 .removeClass( 'active' )
198 .find( '.item-overlay' )
199 .removeClass( 'item-overlay' )
200 .addClass( 'item-static' );
201
202 $( '.post-insert-overlay' ).remove();
203 }
204
205 /**
206 * Instantiates drag and drop sorting for posts on the calendar
207 */
208 let snackbarMessageTimeout = null;
209 $( 'td.day-unit ul' ).sortable( {
210 items: 'li.day-item.sortable',
211 connectWith: 'td.day-unit ul',
212 placeholder: 'ui-state-highlight',
213 cancel: '.item-overlay, .item-overlay *, .post-insert-overlay, .post-insert-overlay *',
214 start( event, ui ) {
215 $( this ).disableSelection();
216 edit_flow_calendar_close_overlays();
217 $( 'td.day-unit ul li' ).unbind( 'click.ef-calendar-show-overlay' );
218 $( this ).css( 'cursor', 'move' );
219 },
220 sort( event, ui ) {
221 $( 'td.day-unit' ).removeClass( 'ui-wrapper-highlight' );
222 $( '.ui-state-highlight' ).closest( 'td.day-unit' ).addClass( 'ui-wrapper-highlight' );
223 },
224 stop( event, ui ) {
225 $( this ).css( 'cursor', 'auto' );
226 $( 'td.day-unit' ).removeClass( 'ui-wrapper-highlight' );
227 // Only do a POST request if we moved the post off today
228 if (
229 $( this ).closest( '.day-unit' ).attr( 'id' ) !=
230 $( ui.item ).closest( '.day-unit' ).attr( 'id' )
231 ) {
232 let post_id = $( ui.item ).attr( 'id' ).split( '-' );
233 post_id = post_id[ post_id.length - 1 ];
234 const prev_date_id = $( this ).closest( '.day-unit' ).attr( 'id' );
235 const prev_date = prev_date_id.substr( 'date-'.length );
236 const next_date_id = $( ui.item ).closest( '.day-unit' ).attr( 'id' );
237 const next_date = next_date_id.substr( 'date-'.length );
238 const nonce = $( document ).find( '#ef-calendar-modify' ).val();
239 $( '.edit-flow-message' ).remove();
240 getCalendarDispatch().setCalendarIsLoading( true );
241 // $('li.ajax-actions .waiting').show();
242 // make ajax request
243 const params = {
244 action: 'ef_calendar_drag_and_drop',
245 post_id,
246 prev_date,
247 next_date,
248 nonce,
249 };
250 jQuery.post( ajaxurl, params, function ( response ) {
251 if ( snackbarMessageTimeout ) {
252 clearTimeout( snackbarMessageTimeout );
253 }
254
255 getCalendarDispatch().setPostSaved( response.message );
256
257 snackbarMessageTimeout = setTimeout( () => {
258 getCalendarDispatch().clearCalendarSnackbarMessage();
259 }, 2500 );
260
261 setTimeout( edit_flow_calendar_hide_message, 10000 );
262 } );
263 }
264 $( this ).enableSelection();
265 },
266 } );
267
268 // Enables quick creation/edit of drafts on a particular date from the calendar
269 var EFQuickPublish = {
270 /**
271 * When user clicks the '+' on an individual calendar date or
272 * double clicks on a calendar square pop up a form that allows
273 * them to create a post for that date
274 */
275 init() {
276 const $day_units = $( 'td.day-unit' );
277
278 // Bind the form display to the '+' button
279 // or to a double click on the calendar square
280 $day_units
281 .find( '.schedule-new-post-button' )
282 .on( 'click.editFlow.quickPublish', EFQuickPublish.open_quickpost_dialogue );
283 $day_units.on( 'dblclick.editFlow.quickPublish', EFQuickPublish.open_quickpost_dialogue );
284 $day_units.on( 'mouseenter', function () {
285 $( this ).find( '.schedule-new-post-button' ).stop().delay( 500 ).fadeIn( 100 );
286 } );
287 $day_units.on( 'mouseleave', function () {
288 $( this ).find( '.schedule-new-post-button' ).stop().hide();
289 } );
290 }, // init
291
292 /**
293 * Callback for click and double click events that open the
294 * quickpost dialogue
295 * @param Event e The user interaction event
296 */
297 open_quickpost_dialogue( e ) {
298 e.preventDefault();
299
300 // Close other overlays
301 edit_flow_calendar_close_overlays();
302
303 $this = $( this );
304
305 // Get the current calendar square
306 if ( $this.is( 'td.day-unit' ) ) {
307 EFQuickPublish.$current_date_square = $this;
308 } else if ( $this.is( '.schedule-new-post-button' ) ) {
309 EFQuickPublish.$current_date_square = $this.parent();
310 }
311
312 // Get our form content
313 const $new_post_form_content =
314 EFQuickPublish.$current_date_square.find( '.post-insert-dialog' );
315
316 // Inject the form (it will automatically be removed on click-away because of its 'item-overlay' class)
317 EFQuickPublish.$new_post_form = $new_post_form_content
318 .clone()
319 .addClass( 'item-overlay post-insert-overlay' )
320 .appendTo( EFQuickPublish.$current_date_square );
321
322 // Get the inputs and controls for this injected form and focus the cursor on the post title box
323 const $edit_post_link = EFQuickPublish.$new_post_form.find(
324 '.post-insert-dialog-edit-post-link'
325 );
326 EFQuickPublish.$post_title_input = EFQuickPublish.$new_post_form
327 .find( '.post-insert-dialog-post-title' )
328 .focus();
329
330 // Setup the ajax mechanism for form submit
331 EFQuickPublish.$new_post_form.on( 'submit', function ( e ) {
332 e.preventDefault();
333 EFQuickPublish.ajax_ef_create_post( false );
334 } );
335
336 // Setup direct link to new draft
337 $edit_post_link.on( 'click', function ( e ) {
338 e.preventDefault();
339 EFQuickPublish.ajax_ef_create_post( true );
340 } );
341
342 return false; // prevent bubbling up
343 },
344
345 /**
346 * Sends an ajax request to create a new post
347 * @param bool redirect_to_draft Whether or not we should be redirected to the post's edit screen on success
348 */
349 ajax_ef_create_post( redirect_to_draft ) {
350 // Get some of the form elements for later use
351 const $submit_controls = EFQuickPublish.$new_post_form.find( '.post-insert-dialog-controls' );
352 const $spinner = EFQuickPublish.$new_post_form.find( '.spinner' );
353
354 // Set loading animation
355 $submit_controls.hide();
356 $spinner.show();
357
358 // Delay submit to prevent spinner flashing
359 setTimeout( function () {
360 jQuery.ajax( {
361 type: 'POST',
362 url: ajaxurl,
363 dataType: 'json',
364 data: {
365 action: 'ef_insert_post',
366 ef_insert_date: EFQuickPublish.$new_post_form
367 .find( 'input.post-insert-dialog-post-date' )
368 .val(),
369 ef_insert_title: EFQuickPublish.$post_title_input.val(),
370 nonce: $( document ).find( '#ef-calendar-modify' ).val(),
371 },
372 success( response, textStatus, XMLHttpRequest ) {
373 if ( response.status == 'success' ) {
374 // The response message on success is the html for the a post list item
375 const $new_post = $( response.message );
376
377 if ( redirect_to_draft ) {
378 // If user clicked on the 'edit post' link, let's send them to the new post
379 const edit_url = $new_post.find( '.item-actions .edit a' ).attr( 'href' );
380 window.location = edit_url;
381 } else {
382 // Otherwise, inject the new post and bind the appropriate click event
383 $new_post.appendTo( EFQuickPublish.$current_date_square.find( 'ul.post-list' ) );
384 edit_flow_calendar_close_overlays();
385 }
386 } else {
387 EFQuickPublish.display_errors( EFQuickPublish.$new_post_form, response.message );
388 }
389 },
390 error( XMLHttpRequest, textStatus, errorThrown ) {
391 EFQuickPublish.display_errors( EFQuickPublish.$new_post_form, errorThrown );
392 },
393 } ); // .ajax
394
395 return false; // prevent bubbling up
396 }, 200 ); // setTimout
397 }, // ajax_ef_create_post
398
399 /**
400 * Displays form errors and resets the UI
401 * @param jQueryObj $form The form to display the errors in
402 * @param str error_msg Error message
403 */
404 display_errors( $form, error_msg ) {
405 $form.find( '.error' ).remove(); // clear out old errors
406 $form.find( '.spinner' ).hide(); // stop the loading animation
407
408 // show submit controls and the error
409 $form
410 .find( '.post-insert-dialog-controls' )
411 .show()
412 .before( jQuery( '<div class="error"></div>' ).text( 'Error: ' + error_msg ) );
413 }, // display_errors
414 };
415
416 if ( ef_calendar_params.can_add_posts === 'true' ) {
417 EFQuickPublish.init();
418 }
419 } );
420