1 /* global ajaxurl, wpAjax */
4 * @param {jQuery} $ jQuery object.
26 * URL for Ajax requests.
33 * The HTTP method to use for Ajax requests.
40 * ID of the element the parsed Ajax response will be stored in.
44 response: 'ajax-response',
54 * CSS class name for alternate styling.
61 * Offset to start alternate styling from.
68 * Color used in animation when adding an element.
70 * Can be 'none' to disable the animation.
77 * Color used in animation when deleting an element.
79 * Can be 'none' to disable the animation.
86 * Color used in dim add animation.
88 * Can be 'none' to disable the animation.
92 dimAddColor: '#ffff33',
95 * Color used in dim delete animation.
97 * Can be 'none' to disable the animation.
101 dimDelColor: '#ff3333',
104 * Callback that's run before a request is made.
106 * @callback wpList~confirm
107 * @param {object} this
108 * @param {HTMLElement} list The list DOM element.
109 * @param {object} settings Settings for the current list.
110 * @param {string} action The type of action to perform: 'add', 'delete', or 'dim'.
111 * @param {string} backgroundColor Background color of the list's DOM element.
112 * @returns {boolean} Whether to proceed with the action or not.
117 * Callback that's run before an item gets added to the list.
119 * Allows to cancel the request.
121 * @callback wpList~addBefore
122 * @param {object} settings Settings for the Ajax request.
123 * @returns {object|boolean} Settings for the Ajax request or false to abort.
128 * Callback that's run after an item got added to the list.
130 * @callback wpList~addAfter
131 * @param {XML} returnedResponse Raw response returned from the server.
132 * @param {object} settings Settings for the Ajax request.
133 * @param {jqXHR} settings.xml jQuery XMLHttpRequest object.
134 * @param {string} settings.status Status of the request: 'success', 'notmodified', 'nocontent', 'error',
135 * 'timeout', 'abort', or 'parsererror'.
136 * @param {object} settings.parsed Parsed response object.
141 * Callback that's run before an item gets deleted from the list.
143 * Allows to cancel the request.
145 * @callback wpList~delBefore
146 * @param {object} settings Settings for the Ajax request.
147 * @param {HTMLElement} list The list DOM element.
148 * @returns {object|boolean} Settings for the Ajax request or false to abort.
153 * Callback that's run after an item got deleted from the list.
155 * @callback wpList~delAfter
156 * @param {XML} returnedResponse Raw response returned from the server.
157 * @param {object} settings Settings for the Ajax request.
158 * @param {jqXHR} settings.xml jQuery XMLHttpRequest object.
159 * @param {string} settings.status Status of the request: 'success', 'notmodified', 'nocontent', 'error',
160 * 'timeout', 'abort', or 'parsererror'.
161 * @param {object} settings.parsed Parsed response object.
166 * Callback that's run before an item gets dim'd.
168 * Allows to cancel the request.
170 * @callback wpList~dimBefore
171 * @param {object} settings Settings for the Ajax request.
172 * @returns {object|boolean} Settings for the Ajax request or false to abort.
177 * Callback that's run after an item got dim'd.
179 * @callback wpList~dimAfter
180 * @param {XML} returnedResponse Raw response returned from the server.
181 * @param {object} settings Settings for the Ajax request.
182 * @param {jqXHR} settings.xml jQuery XMLHttpRequest object.
183 * @param {string} settings.status Status of the request: 'success', 'notmodified', 'nocontent', 'error',
184 * 'timeout', 'abort', or 'parsererror'.
185 * @param {object} settings.parsed Parsed response object.
193 * 1. Nonce in settings.
194 * 2. `_ajax_nonce` value in element's href attribute.
195 * 3. `_ajax_nonce` input field that is a descendant of element.
196 * 4. `_wpnonce` value in element's href attribute.
197 * 5. `_wpnonce` input field that is a descendant of element.
198 * 6. 0 if none can be found.
200 * @param {jQuery} element Element that triggered the request.
201 * @param {object} settings Settings for the Ajax request.
202 * @returns {string|number} Nonce
204 nonce: function( element, settings ) {
205 var url = wpAjax.unserialize( element.attr( 'href' ) ),
206 $element = $( '#' + settings.element );
208 return settings.nonce || url._ajax_nonce || $element.find( 'input[name="_ajax_nonce"]' ).val() || url._wpnonce || $element.find( 'input[name="_wpnonce"]' ).val() || 0;
212 * Extract list item data from a DOM element.
214 * Example 1: data-wp-lists="delete:the-comment-list:comment-{comment_ID}:66cc66:unspam=1"
215 * Example 2: data-wp-lists="dim:the-comment-list:comment-{comment_ID}:unapproved:e7e7d3:e7e7d3:new=approved"
217 * Returns an unassociated array with the following data:
218 * data[0] - Data identifier: 'list', 'add', 'delete', or 'dim'.
219 * data[1] - ID of the corresponding list. If data[0] is 'list', the type of list ('comment', 'category', etc).
220 * data[2] - ID of the parent element of all inputs necessary for the request.
221 * data[3] - Hex color to be used in this request. If data[0] is 'dim', dim class.
222 * data[4] - Additional arguments in query syntax that are added to the request. Example: 'post_id=1234'.
223 * If data[0] is 'dim', dim add color.
224 * data[5] - Only available if data[0] is 'dim', dim delete color.
225 * data[6] - Only available if data[0] is 'dim', additional arguments in query syntax that are added to the request.
227 * Result for Example 1:
229 * data[1] - the-comment-list
230 * data[2] - comment-{comment_ID}
234 * @param {HTMLElement} element The DOM element.
235 * @param {string} type The type of data to look for: 'list', 'add', 'delete', or 'dim'.
236 * @returns {Array} Extracted list item data.
238 parseData: function( element, type ) {
239 var data = [], wpListsData;
242 wpListsData = $( element ).data( 'wp-lists' ) || '';
243 wpListsData = wpListsData.match( new RegExp( type + ':[\\S]+' ) );
246 data = wpListsData[0].split( ':' );
254 * Calls a confirm callback to verify the action that is about to be performed.
256 * @param {HTMLElement} list The DOM element.
257 * @param {object} settings Settings for this list.
258 * @param {string} action The type of action to perform: 'add', 'delete', or 'dim'.
259 * @returns {object|boolean} Settings if confirmed, false if not.
261 pre: function( list, settings, action ) {
262 var $element, backgroundColor, confirmed;
264 settings = $.extend( {}, this.wpList.settings, {
267 target: list.get( 0 )
270 if ( $.isFunction( settings.confirm ) ) {
271 $element = $( '#' + settings.element );
273 if ( 'add' !== action ) {
274 backgroundColor = $element.css( 'backgroundColor' );
275 $element.css( 'backgroundColor', '#ff9966' );
278 confirmed = settings.confirm.call( this, list, settings, action, backgroundColor );
280 if ( 'add' !== action ) {
281 $element.css( 'backgroundColor', backgroundColor );
293 * Adds an item to the list via AJAX.
295 * @param {HTMLElement} element The DOM element.
296 * @param {object} settings Settings for this list.
297 * @returns {boolean} Whether the item was added.
299 ajaxAdd: function( element, settings ) {
301 $element = $( element ),
302 data = wpList.parseData( $element, 'add' ),
303 formValues, formData, parsedResponse, returnedResponse;
305 settings = settings || {};
306 settings = wpList.pre.call( list, $element, settings, 'add' );
308 settings.element = data[2] || $element.prop( 'id' ) || settings.element || null;
309 settings.addColor = data[3] ? '#' + data[3] : settings.addColor;
315 if ( ! $element.is( '[id="' + settings.element + '-submit"]' ) ) {
316 return ! wpList.add.call( list, $element, settings );
319 if ( ! settings.element ) {
323 settings.action = 'add-' + settings.what;
324 settings.nonce = wpList.nonce( $element, settings );
326 if ( ! wpAjax.validateForm( '#' + settings.element ) ) {
330 settings.data = $.param( $.extend( {
331 _ajax_nonce: settings.nonce,
332 action: settings.action
333 }, wpAjax.unserialize( data[4] || '' ) ) );
335 formValues = $( '#' + settings.element + ' :input' ).not( '[name="_ajax_nonce"], [name="_wpnonce"], [name="action"]' );
336 formData = $.isFunction( formValues.fieldSerialize ) ? formValues.fieldSerialize() : formValues.serialize();
339 settings.data += '&' + formData;
342 if ( $.isFunction( settings.addBefore ) ) {
343 settings = settings.addBefore( settings );
350 if ( ! settings.data.match( /_ajax_nonce=[a-f0-9]+/ ) ) {
354 settings.success = function( response ) {
355 parsedResponse = wpAjax.parseAjaxResponse( response, settings.response, settings.element );
356 returnedResponse = response;
358 if ( ! parsedResponse || parsedResponse.errors ) {
362 if ( true === parsedResponse ) {
366 $.each( parsedResponse.responses, function() {
367 wpList.add.call( list, this.data, $.extend( {}, settings, { // this.firstChild.nodevalue
368 position: this.position || 0,
370 oldId: this.oldId || null
374 list.wpList.recolor();
375 $( list ).trigger( 'wpListAddEnd', [ settings, list.wpList ] );
376 wpList.clear.call( list, '#' + settings.element );
379 settings.complete = function( jqXHR, status ) {
380 if ( $.isFunction( settings.addAfter ) ) {
381 settings.addAfter( returnedResponse, $.extend( {
384 parsed: parsedResponse
395 * Delete an item in the list via AJAX.
397 * @param {HTMLElement} element A DOM element containing item data.
398 * @param {object} settings Settings for this list.
399 * @returns {boolean} Whether the item was deleted.
401 ajaxDel: function( element, settings ) {
403 $element = $( element ),
404 data = wpList.parseData( $element, 'delete' ),
405 $eventTarget, parsedResponse, returnedResponse;
407 settings = settings || {};
408 settings = wpList.pre.call( list, $element, settings, 'delete' );
410 settings.element = data[2] || settings.element || null;
411 settings.delColor = data[3] ? '#' + data[3] : settings.delColor;
413 if ( ! settings || ! settings.element ) {
417 settings.action = 'delete-' + settings.what;
418 settings.nonce = wpList.nonce( $element, settings );
420 settings.data = $.extend( {
421 _ajax_nonce: settings.nonce,
422 action: settings.action,
423 id: settings.element.split( '-' ).pop()
424 }, wpAjax.unserialize( data[4] || '' ) );
426 if ( $.isFunction( settings.delBefore ) ) {
427 settings = settings.delBefore( settings, list );
434 if ( ! settings.data._ajax_nonce ) {
438 $eventTarget = $( '#' + settings.element );
440 if ( 'none' !== settings.delColor ) {
441 $eventTarget.css( 'backgroundColor', settings.delColor ).fadeOut( 350, function() {
442 list.wpList.recolor();
443 $( list ).trigger( 'wpListDelEnd', [ settings, list.wpList ] );
446 list.wpList.recolor();
447 $( list ).trigger( 'wpListDelEnd', [ settings, list.wpList ] );
450 settings.success = function( response ) {
451 parsedResponse = wpAjax.parseAjaxResponse( response, settings.response, settings.element );
452 returnedResponse = response;
454 if ( ! parsedResponse || parsedResponse.errors ) {
455 $eventTarget.stop().stop().css( 'backgroundColor', '#faa' ).show().queue( function() {
456 list.wpList.recolor();
464 settings.complete = function( jqXHR, status ) {
465 if ( $.isFunction( settings.delAfter ) ) {
466 $eventTarget.queue( function() {
467 settings.delAfter( returnedResponse, $.extend( {
470 parsed: parsedResponse
482 * Dim an item in the list via AJAX.
484 * @param {HTMLElement} element A DOM element containing item data.
485 * @param {object} settings Settings for this list.
486 * @returns {boolean} Whether the item was dim'ed.
488 ajaxDim: function( element, settings ) {
490 $element = $( element ),
491 data = wpList.parseData( $element, 'dim' ),
492 $eventTarget, isClass, color, dimColor, parsedResponse, returnedResponse;
494 // Prevent hidden links from being clicked by hotkeys.
495 if ( 'none' === $element.parent().css( 'display' ) ) {
499 settings = settings || {};
500 settings = wpList.pre.call( list, $element, settings, 'dim' );
502 settings.element = data[2] || settings.element || null;
503 settings.dimClass = data[3] || settings.dimClass || null;
504 settings.dimAddColor = data[4] ? '#' + data[4] : settings.dimAddColor;
505 settings.dimDelColor = data[5] ? '#' + data[5] : settings.dimDelColor;
507 if ( ! settings || ! settings.element || ! settings.dimClass ) {
511 settings.action = 'dim-' + settings.what;
512 settings.nonce = wpList.nonce( $element, settings );
514 settings.data = $.extend( {
515 _ajax_nonce: settings.nonce,
516 action: settings.action,
517 id: settings.element.split( '-' ).pop(),
518 dimClass: settings.dimClass
519 }, wpAjax.unserialize( data[6] || '' ) );
521 if ( $.isFunction( settings.dimBefore ) ) {
522 settings = settings.dimBefore( settings );
529 $eventTarget = $( '#' + settings.element );
530 isClass = $eventTarget.toggleClass( settings.dimClass ).is( '.' + settings.dimClass );
531 color = wpList.getColor( $eventTarget );
532 dimColor = isClass ? settings.dimAddColor : settings.dimDelColor;
533 $eventTarget.toggleClass( settings.dimClass );
535 if ( 'none' !== dimColor ) {
537 .animate( { backgroundColor: dimColor }, 'fast' )
539 $eventTarget.toggleClass( settings.dimClass );
542 .animate( { backgroundColor: color }, {
543 complete: function() {
544 $( this ).css( 'backgroundColor', '' );
545 $( list ).trigger( 'wpListDimEnd', [ settings, list.wpList ] );
549 $( list ).trigger( 'wpListDimEnd', [ settings, list.wpList ] );
552 if ( ! settings.data._ajax_nonce ) {
556 settings.success = function( response ) {
557 parsedResponse = wpAjax.parseAjaxResponse( response, settings.response, settings.element );
558 returnedResponse = response;
560 if ( true === parsedResponse ) {
564 if ( ! parsedResponse || parsedResponse.errors ) {
565 $eventTarget.stop().stop().css( 'backgroundColor', '#ff3333' )[isClass ? 'removeClass' : 'addClass']( settings.dimClass ).show().queue( function() {
566 list.wpList.recolor();
573 /** @property {string} comment_link Link of the comment to be dimmed. */
574 if ( 'undefined' !== typeof parsedResponse.responses[0].supplemental.comment_link ) {
575 var $submittedOn = $element.find( '.submitted-on' ),
576 $commentLink = $submittedOn.find( 'a' );
578 // Comment is approved; link the date field.
579 if ( '' !== parsedResponse.responses[0].supplemental.comment_link ) {
580 $submittedOn.html( $('<a></a>').text( $submittedOn.text() ).prop( 'href', parsedResponse.responses[0].supplemental.comment_link ) );
582 // Comment is not approved; unlink the date field.
583 } else if ( $commentLink.length ) {
584 $submittedOn.text( $commentLink.text() );
589 settings.complete = function( jqXHR, status ) {
590 if ( $.isFunction( settings.dimAfter ) ) {
591 $eventTarget.queue( function() {
592 settings.dimAfter( returnedResponse, $.extend( {
595 parsed: parsedResponse
607 * Returns the background color of the passed element.
609 * @param {jQuery|string} element Element to check.
610 * @returns {string} Background color value in HEX. Default: '#ffffff'.
612 getColor: function( element ) {
613 return $( element ).css( 'backgroundColor' ) || '#ffffff';
619 * @param {HTMLElement} element A DOM element containing item data.
620 * @param {object} settings Settings for this list.
621 * @returns {boolean} Whether the item was added.
623 add: function( element, settings ) {
624 var $list = $( this ),
625 $element = $( element ),
629 if ( 'string' === typeof settings ) {
630 settings = { what: settings };
633 settings = $.extend( { position: 0, id: 0, oldId: null }, this.wpList.settings, settings );
635 if ( ! $element.length || ! settings.what ) {
639 if ( settings.oldId ) {
640 old = $( '#' + settings.what + '-' + settings.oldId );
643 if ( settings.id && ( settings.id !== settings.oldId || ! old || ! old.length ) ) {
644 $( '#' + settings.what + '-' + settings.id ).remove();
647 if ( old && old.length ) {
648 old.before( $element );
651 } else if ( isNaN( settings.position ) ) {
654 if ( '-' === settings.position.substr( 0, 1 ) ) {
655 settings.position = settings.position.substr( 1 );
659 reference = $list.find( '#' + settings.position );
661 if ( 1 === reference.length ) {
662 reference[position]( $element );
664 $list.append( $element );
667 } else if ( 'comment' !== settings.what || 0 === $( '#' + settings.element ).length ) {
668 if ( settings.position < 0 ) {
669 $list.prepend( $element );
671 $list.append( $element );
675 if ( settings.alt ) {
676 $element.toggleClass( settings.alt, ( $list.children( ':visible' ).index( $element[0] ) + settings.altOffset ) % 2 );
679 if ( 'none' !== settings.addColor ) {
680 $element.css( 'backgroundColor', settings.addColor ).animate( { backgroundColor: wpList.getColor( $element ) }, {
681 complete: function() {
682 $( this ).css( 'backgroundColor', '' );
687 // Add event handlers.
688 $list.each( function( index, list ) {
689 list.wpList.process( $element );
696 * Clears all input fields within the element passed.
698 * @param {string} elementId ID of the element to check, including leading #.
700 clear: function( elementId ) {
702 $element = $( elementId ),
705 // Bail if we're within the list.
706 if ( list.wpList && $element.parents( '#' + list.id ).length ) {
710 // Check each input field.
711 $element.find( ':input' ).each( function( index, input ) {
713 // Bail if the form was marked to not to be cleared.
714 if ( $( input ).parents( '.form-no-clear' ).length ) {
718 type = input.type.toLowerCase();
719 tagName = input.tagName.toLowerCase();
721 if ( 'text' === type || 'password' === type || 'textarea' === tagName ) {
724 } else if ( 'checkbox' === type || 'radio' === type ) {
725 input.checked = false;
727 } else if ( 'select' === tagName ) {
728 input.selectedIndex = null;
734 * Registers event handlers to add, delete, and dim items.
736 * @param {string} elementId
738 process: function( elementId ) {
740 $element = $( elementId || document );
742 $element.on( 'submit', 'form[data-wp-lists^="add:' + list.id + ':"]', function() {
743 return list.wpList.add( this );
746 $element.on( 'click', 'a[data-wp-lists^="add:' + list.id + ':"], input[data-wp-lists^="add:' + list.id + ':"]', function() {
747 return list.wpList.add( this );
750 $element.on( 'click', '[data-wp-lists^="delete:' + list.id + ':"]', function() {
751 return list.wpList.del( this );
754 $element.on( 'click', '[data-wp-lists^="dim:' + list.id + ':"]', function() {
755 return list.wpList.dim( this );
760 * Updates list item background colors.
762 recolor: function() {
764 evenOdd = [':even', ':odd'],
767 // Bail if there is no alternate class name specified.
768 if ( ! list.wpList.settings.alt ) {
772 items = $( '.list-item:visible', list );
774 if ( ! items.length ) {
775 items = $( list ).children( ':visible' );
778 if ( list.wpList.settings.altOffset % 2 ) {
782 items.filter( evenOdd[0] ).addClass( list.wpList.settings.alt ).end();
783 items.filter( evenOdd[1] ).removeClass( list.wpList.settings.alt );
787 * Sets up `process()` and `recolor()` functions.
792 $list.wpList.process = function( element ) {
793 $list.each( function() {
794 this.wpList.process( element );
798 $list.wpList.recolor = function() {
799 $list.each( function() {
800 this.wpList.recolor();
807 * Initializes wpList object.
809 * @param {Object} settings
810 * @param {string} settings.url URL for ajax calls. Default: ajaxurl.
811 * @param {string} settings.type The HTTP method to use for Ajax requests. Default: 'POST'.
812 * @param {string} settings.response ID of the element the parsed ajax response will be stored in.
813 * Default: 'ajax-response'.
815 * @param {string} settings.what Default: ''.
816 * @param {string} settings.alt CSS class name for alternate styling. Default: 'alternate'.
817 * @param {number} settings.altOffset Offset to start alternate styling from. Default: 0.
818 * @param {string} settings.addColor Hex code or 'none' to disable animation. Default: '#ffff33'.
819 * @param {string} settings.delColor Hex code or 'none' to disable animation. Default: '#faafaa'.
820 * @param {string} settings.dimAddColor Hex code or 'none' to disable animation. Default: '#ffff33'.
821 * @param {string} settings.dimDelColor Hex code or 'none' to disable animation. Default: '#ff3333'.
823 * @param {wpList~confirm} settings.confirm Callback that's run before a request is made. Default: null.
824 * @param {wpList~addBefore} settings.addBefore Callback that's run before an item gets added to the list.
826 * @param {wpList~addAfter} settings.addAfter Callback that's run after an item got added to the list.
828 * @param {wpList~delBefore} settings.delBefore Callback that's run before an item gets deleted from the list.
830 * @param {wpList~delAfter} settings.delAfter Callback that's run after an item got deleted from the list.
832 * @param {wpList~dimBefore} settings.dimBefore Callback that's run before an item gets dim'd. Default: null.
833 * @param {wpList~dimAfter} settings.dimAfter Callback that's run after an item got dim'd. Default: null.
834 * @returns {$.fn} wpList API function.
836 $.fn.wpList = function( settings ) {
837 this.each( function( index, list ) {
839 settings: $.extend( {}, wpList.settings, { what: wpList.parseData( list, 'list' )[1] || '' }, settings )
842 $.each( functions, function( func, callback ) {
843 list.wpList[func] = function( element, setting ) {
844 return wpList[callback].call( list, element, setting );
849 wpList.init.call( this );
850 this.wpList.process();