( function( window, $, undefined ) {
var document = window.document;
var AdCodeManager = function() {
/**
* A reference to the AdCodeManager object so we avoid confusion with `this` later on
*
* @type {*}
*/
var SELF = this;
/**
* Container for cached UI elements
*
* @type {Object}
*/
var UI = {};
/**
* Used for storing the currently edited ACM ID
* @type {Boolean}
*/
var EDIT_ID = false;
/**
* Initializes the AdCodeManager when the object is instantiated. This script must always run from the footer
* after the DOM elements are rendered
*
* @private
*/
var _init = function() {
_cacheElements();
_bindEvents();
};
/**
* Caches useful DOM elements so we can easily reference them later without the extra lookup
*
* @private
*/
var _cacheElements = function() {
UI.addMoreButton = document.getElementById( 'conditional-tpl' ).querySelector( '.add-more-conditionals' );
UI.theList = document.getElementById( 'the-list' );
UI.theNew = document.getElementById( 'add-adcode' );
};
/**
* Handles binding our events for the page to work
*
* @private
*/
var _bindEvents = function() {
_addEvent( 'click', UI.addMoreButton, _addConditional );
_addEvent( 'click', UI.theList, _delegateListClicks );
_addEvent( 'click', UI.theNew, _delegateNewAdClicks );
_addEvent( 'keydown', UI.theList, _delegateListKeyEvents );
};
/**
* Registers a DOM event for the specified element
*
* @param event The event we're hooking to
* @param element The element we want to monitor for the event
* @param callback The callback to be fired when the event is triggered
* @private
*/
var _addEvent = function( event, element, callback ) {
if( window.addEventListener ) {
element.addEventListener( event, callback, false );
}
else {
element.attachEvent( 'on' + event, callback );
}
};
/**
* Handles adding a new conditional row to the UI for the user
*
* @param e The event object
* @private
*/
var _addConditional = function( e ) {
e = e || window.event;
var target = e.srcElement || e.target;
var parent = target.parentNode.parentNode.querySelector( '.form-new-row' );
_addInlineConditionalRow( parent );
_killEvent( e );
};
/**
* Kills the passed event and prevents it from bubbling up the DOM
*
* @param e The event we're killing
* @private
*/
var _killEvent = function( e ) {
e.returnValue = false;
e.cancelBubble = true;
if( e.stopPropagation ) {
e.stopPropagation();
}
if( e.preventDefault ) {
e.preventDefault();
}
};
/**
* Handles checking delegated events for the add ad code area
*
* @param e The event object
* @private
*/
var _delegateNewAdClicks = function( e ) {
e = e || window.event;
var target = e.srcElement || e.target;
// check for remove conditional call
if( _hasClass( target, 'acm-remove-conditional' ) === true ) {
_removeInlineConditionalRow( target );
_killEvent( e );
}
};
/**
* Handles checking delegated key events for the inline editor and table rows
* @param e
* @private
*/
var _delegateListKeyEvents = function( e ) {
e = e || window.event;
var key = e.which || e.keyCode;
// 13 is Enter, which avoids the default form on the page from saving
if ( key === 13 ) {
_saveInlineEditorChanges();
_killEvent( e );
}
};
/**
* Handles checking delegated events for the inline editor and table rows
*
* @param e The event object
* @private
*/
var _delegateListClicks = function( e ) {
e = e || window.event;
var target = e.srcElement || e.target;
// check for ajax edit click
if( _hasClass( target, 'acm-ajax-edit' ) === true ) {
// close other editors
if( EDIT_ID !== false ) {
if( confirm( 'Are you sure you want to do this? Any unsaved data will be lost.' ) === false ) {
_killEvent( e );
return;
}
_toggleInlineEdit( false );
}
EDIT_ID = parseInt( target.id.replace( 'acm-edit-', '' ), 10 );
_toggleInlineEdit( true );
_killEvent( e );
}
// check for cancel button
else if( _hasClass( target, 'cancel' ) === true && EDIT_ID !== false ) {
_toggleInlineEdit( false );
EDIT_ID = false;
}
// check for remove conditional call
else if( _hasClass( target, 'acm-remove-conditional' ) === true ) {
_removeInlineConditionalRow( target );
_killEvent( e );
}
// check for save button
else if( _hasClass( target, 'save' ) === true ) {
_toggleLoader( true );
_saveInlineEditorChanges();
_killEvent( e );
}
// check for add more conditionals
else if( _hasClass( target, 'add-more-conditionals' ) === true ) {
_addInlineConditionalRow( UI.theList.querySelector( '#ad-code-' + EDIT_ID + ' .acm-editor-row .acm-conditional-fields .form-new-row' ) );
_killEvent( e );
}
};
/**
* Saves any inline editor changes that occurred
*
* @private
*/
var _saveInlineEditorChanges = function() {
$.post( window.ajaxurl, _getFormData(), function( result ) {
if( result ) {
if( result.indexOf( '
-1 ) {
$( document.getElementById( 'ad-code-' + EDIT_ID ) ).before( result).remove();
EDIT_ID = false;
}
else {
_showError( result );
}
}
else {
_showError( inlineEditL10n.error );
}
} );
};
/**
* Shows the error for this ad code if it exists
*
* @param html
* @private
*/
var _showError = function( html ) {
var errorContainer = document.getElementById( 'ad-code-' + EDIT_ID ).querySelector( '.acm-editor-row .inline-edit-save .error' );
errorContainer.innerHTML = html;
errorContainer.style.display = 'block';
};
/**
* Custom serialization function based off of $.serializeArray() - slimmed down to exactly what we need
*
* @return {Array}
* @private
*/
var _getFormData = function() {
var data = [];
var fields = document.getElementById( 'ad-code-' + EDIT_ID ).querySelector( '.acm-editor-row fieldset' );
var elements = fields.querySelectorAll( 'input, select, textarea' ), element, name;
for( var i = 0, len = elements.length; i < len; i++ ) {
element = elements[ i ];
name = element.name.replace( /^\s+|\s+$/i, '' );
if( name === '' ) {
continue;
}
data.push( { name : name, value : element.value } );
}
return data;
};
/**
* Removes the conditional row from the perspective of the button clicked
*
* @param target The `remove` button that was clicked.
* @private
*/
var _removeInlineConditionalRow = function( target ) {
var row = target.parentNode.parentNode;
var parent = row.parentNode;
parent.removeChild( row );
};
/**
* Add a new inline editor conditional row for the current ad-code
*
* @private
*/
var _addInlineConditionalRow = function( parent ) {
// create a new element
var newConditional = document.createElement( 'div' );
newConditional.className = 'conditional-single-field';
newConditional.innerHTML = document.getElementById( 'conditional-single-field-master' ).innerHTML;
newConditional.querySelector( '.conditional-arguments' ).innerHTML += 'Remove';
parent.appendChild( newConditional );
};
/**
* Toggles the loader for the form. This should only be used when the save button is clicked and we have a current
* EDIT_ID available
*
* @param showing Indicates whether the loader should be showing or not
* @private
*/
var _toggleLoader = function( showing ) {
var loader = document.querySelector( '#ad-code-' + EDIT_ID + ' .acm-editor-row .inline-edit-save .waiting' );
loader.style.display = ( showing === true ) ? 'block' : 'none';
};
/**
* Lightweight utility function that handles checking an element to see if it contains a class
*
* @param element The element we're checking against
* @param className The class name we're looking for
* @return {Boolean}
* @private
*/
var _hasClass = function( element, className ) {
return ( ' ' + element.className + ' ' ).indexOf( ' ' + className + ' ' ) > -1;
};
/**
* Handles toggling the inline editor. We assume EDIT_ID is being handled correctly for this to work.
*
* @param visible Indicates whether we are hiding/showing the inline editor.
* @private
*/
var _toggleInlineEdit = function( visible ) {
var row = document.getElementById( 'ad-code-' + EDIT_ID );
if( visible === true ) {
_toggleTableChildrenDisplay( row, false );
_createNewInlineRow( EDIT_ID, row );
}
else {
_toggleTableChildrenDisplay( row, true );
_removeEditInlineRow( row );
}
};
/**
* Toggles all the table children of the parent.
*
* @param parent The parent table row we're toggling td's for
* @param display Indicates whether or not the row children should be shown or not
* @private
*/
var _toggleTableChildrenDisplay = function( parent, display ) {
display = ( display === true ) ? 'table-cell' : 'none';
var children = parent.children;
for( var i = 0, len = children.length; i < len; i++ ) {
children[ i ].style.display = display;
}
};
/**
* Handles creating the new inline editor row with all of the necessary UI controls. Notice that we do not rebind
* events because they are already handled by the delegation technique in `_delegateListClicks`
*
* @param id The ID of the ad-code you are editing
* @param parentToBe The DOM element that the newRow will be inserted into
* @private
*/
var _createNewInlineRow = function( id, parentToBe ) {
var newRow = document.createElement( 'td' );
newRow.setAttribute( 'colspan', ( parentToBe.children.length - 1 ) );
newRow.className = 'acm-editor-row';
newRow.innerHTML = document.getElementById( 'inline-edit' ).innerHTML;
// fill in the rows with existing HTML here
var data = _getDataFromRow( id );
newRow.querySelector( 'input[name="id"]' ).value = id;
newRow.querySelector( '.acm-conditional-fields' ).innerHTML = data.conditionalFields;
newRow.querySelector( '.acm-column-fields' ).innerHTML = data.columnFields;
newRow.querySelector( '.acm-priority-field' ).innerHTML = data.priority;
newRow.querySelector( '.acm-operator-field' ).innerHTML = data.operator;
parentToBe.appendChild( newRow );
};
/**
* Removes the editor inline row if it exists
*
* @param parent The parent DOM element where we are removing the oldRow from
* @private
*/
var _removeEditInlineRow = function( parent ) {
var oldRow = parent.querySelector( '.acm-editor-row' );
parent.removeChild( oldRow );
parent.querySelector( '.column-id' ).style.display = 'none';
};
/**
* Builds an object literal containing HTML for the new Row based off of existing DOM elements and their HTML
*
* @param id The ID of the ad-code we're retrieving information from.
* @return {Object}
* @private
*/
var _getDataFromRow = function( id ) {
var dataParent = document.getElementById( 'inline_' + id );
return {
conditionalFields : dataParent.querySelector( '.acm-conditional-fields' ).innerHTML,
columnFields : dataParent.querySelector( '.acm-column-fields' ).innerHTML,
priority : dataParent.querySelector( '.acm-priority-field' ).innerHTML,
operator : dataParent.querySelector( '.acm-operator-field' ).innerHTML
};
};
// fire our initialization method
_init();
};
window.AdCodeManager = new AdCodeManager();
} )( window, jQuery );