/**
 * Server communication functions - widgets javascript interface
 * This file contains JS code to work with collection widgets in back-office:
 *    - Add new widget in list
 *    - Sort widgets in list by drag and drop
 *    - Edit widget in modal window
 *
 * This file is part of the evoCore framework - {@link http://evocore.net/}
 * See also {@link https://github.com/b2evolution/b2evolution}.
 * @author yabs - http://innervisions.org.uk/
 */


/**
 * @TODO yabs > finish docs
 */


/**
 * @internal string current_widgets
 * holds the list of current widgets
 */
var current_widgets = '';

/**
 * @internal object reorder_widgets_queue
 * re-order requests timer object
 */
var reorder_widgets_queue;

/**
 * @internal integer reorder_delay
 * time, in milliseconds, to buffer requests for
 * Does not work when set to 0 or even 20 !!
 */
var reorder_delay = 200;

/**
 * @internal integer reorder_delay_remaining
 * time in milliseconds before the request is sent
 */
var reorder_delay_remaining = 0;


/**
 * Init()
 *
 * Activates the new interface if javascript enabled
 */
jQuery(document).ready( function()
{
	if( typeof( b2evo_widgets_init ) != 'undefined' && b2evo_widgets_init === false )
	{	// Don't initialize widgets if it is special forced to stop it:
		return;
	}

	// Make widget rows drag and drop:
	makeDragnDrop( '.draggable_widget' );

	// make container title droppable -- fp> This works but gives no visual feedback. It would actually be cool to drop 'after' the current line in which case dropping on the title would make sense
	jQuery( '.fieldset_title' ).each( function()
	{
		jQuery( this ).droppable(
		{
			accept: ".draggable_widget", // classname of objects that can be dropped
			hoverClass: "droppable-hover", // classname when object is over this one
			greedy: true, // stops propogation if over more than one
			tolerance : "pointer", // droppable active when cursor over
			delay: 1000,
			drop: function(ev, ui)
			{	// function called when object dropped
				jQuery( ".fade_me" ).removeClass( "fade_me" ); // remove any existing fades
				jQuery( '.available_widgets' ).removeClass( 'available_widgets_active' ); // close any open windows

				jQuery( ui.draggable ).prependTo( jQuery( '#container_' + jQuery( this ).find( '.container_name' ).data( 'wico_id' ) ) ); // add the dragged widget to this container
				jQuery( ui.draggable ).addClass( "fade_me server_update" ); // add fade class
				jQuery( ui.draggable ).droppable( "enable" );	// enable dropping if disabled
				doFade( ".fade_me" ); // fade the widget
				colourWidgets();
				sendWidgetOrder(); // send the new order to the server
			}
		});
	});

	// disable dropping on empty containers:
	jQuery( '.no-drop .draggable_widget').droppable( "disable" );
	jQuery( '.draggable_widget' ).bind( 'mousedown', function()
	{ // hide any available widgets panes
		if( !jQuery( this ).hasClass( 'new_widget' ) )
		{	// we're dragging a current widget, close any open "available widgets" screens
			jQuery( '.available_widgets_active' ).removeClass( 'available_widgets_active' );
		}
	});

	colourWidgets(); // add odd/even classes to widgets

	convertAvailableList(); // converts available widgets list to something we can work with

	current_widgets = getWidgetOrder(); // save current widget order

	doFade( '.evo_highlight' );// highlight any changed widgets

	// Actions for buttons to select several widgets to activate/deactivate them by one action
	jQuery( '#widget_button_check_all' ).click( function()
	{
		jQuery( this ).closest( 'form' ).find( 'input[type=checkbox]:enabled' ).prop( 'checked', true );
	} );
	jQuery( '#widget_button_uncheck_all' ).click( function()
	{
		jQuery( this ).closest( 'form' ).find( 'input[type=checkbox]' ).prop( 'checked', false );
	} );
	jQuery( '#widget_button_check_active' ).click( function()
	{
		jQuery( this ).closest( 'form' ).find( '.widget_checkbox.widget_checkbox_enabled input[type=checkbox]:enabled' ).prop( 'checked', true );
		jQuery( this ).closest( 'form' ).find( '.widget_checkbox:not(.widget_checkbox_enabled) input[type=checkbox]' ).prop( 'checked', false );
	} );
	jQuery( '#widget_button_check_inactive' ).click( function()
	{
		jQuery( this ).closest( 'form' ).find( '.widget_checkbox.widget_checkbox_enabled input[type=checkbox]' ).prop( 'checked', false );
		jQuery( this ).closest( 'form' ).find( '.widget_checkbox:not(.widget_checkbox_enabled) input[type=checkbox]:enabled' ).prop( 'checked', true );
	} );

	// Close widget settings or available widgets popup windows if Escape key is pressed:
	jQuery( document ).keyup( function(e)
	{
		if( e.keyCode == 27 )
		{
			closeWidgetSettings();
			closeAvailableWidgets();
		}
	});
} );


/**
 * Makes the selector drag and drop .. because I'm lazy ;)
 *
 * @param mixed selector DOM ID or class or object
 */
function makeDragnDrop( selector )
{
	makeDraggable( selector );
	makeDroppable( selector );
}


/**
 * Makes an element / group of elements draggable
 *
 * @param mixed selector : the object to make draggable
 */
function makeDraggable( selector )
{
	jQuery( selector ).draggable(
	{
		helper: "clone", // use a copy of the image
		scroll: true, // scroll the window during dragging
		scrollSensitivity: 100, // distance from edge before scoll occurs
		zIndex: 999, // z-index whilst dragging
		opacity: .8, // opacity whilst dragging
		cursor: "move", // change the cursor whilst dragging
		cancel: 'input,textarea,button,select,option,a,span.fa,span.widget_checkbox', // prevents dragging from starting on specified elements
		start: function()
		{	// Hide original row during dragging:
			jQuery( this ).hide();
		},
		stop: function()
		{	// Show original row after dragging:
			jQuery( this ).show();
		},
	}).addClass( "draggable_widget" ); // add our css class
}

/**
 * Makes an element / group of elements droppable
 *
 * @param mixed selector : the object to make droppable
 */
function makeDroppable( selector )
{
	jQuery( selector ).droppable(
	{
		accept: ".draggable_widget", // classname of objects that can be dropped
		hoverClass: "droppable-hover", // classname when object is over this one
		greedy: true, // stops propogation if over more than one
		tolerance : "pointer", // droppable active when cursor over
		delay: 1000,
		drop: function(ev, ui)
		{	// function called when object dropped
			jQuery( ".fade_me" ).removeClass( "fade_me" ); // remove any existing fades
			jQuery( '.available_widgets' ).removeClass( 'available_widgets_active' ); // close any open windows
			if( !jQuery( this ).hasClass( "available_widgets" ) )
			{	// we're not deleting it
				if( jQuery( ui.draggable ).hasClass( "new_widget" ) )
				{	// this is a new widget, we need to treat it diffently
					addNewWidget( ui.draggable, this ); // add as new widget
				}
				else
				{	// this is an existing widget, just move it
					jQuery( ui.draggable ).insertAfter( this ); // add the dragged widget before this widget
					jQuery( ui.draggable ).addClass( "fade_me server_update" ); // add fade class
					jQuery( ui.draggable ).droppable( "enable" );	// enable dropping if disabled
				}
			}
			else
			{ // we might be deleting the widget
				if(  !jQuery( ui.draggable ).hasClass( "new_widget" ) )
				{ // we're deleting it
					jQuery( ui.draggable ).remove();
				}
			}
			doFade( ".fade_me" ); // fade the widget
			colourWidgets();
			sendWidgetOrder();// send the new order to the server
		}
	});
}

/**
 * Fades the relevant object
 */
function doFade( selector )
{
	jQuery( selector ).addClass( 'evo_highlight' );
	setTimeout( function ()
	{
		jQuery( selector ).removeClass( 'evo_highlight' );
	}, 5000 );
}


/**
 * Send the current widget containers and order to the server
 *
 * Successive calls within the buffer time resets the countdown
 * this reduces the number of server calls made
 */
function sendWidgetOrder()
{
	if( reorder_delay_remaining < 1 )
	{
		jQuery( '#server_messages' ).html( '<div class="log_container"><div class="log_error"></div></div>' );
	}
	// reset the clock
	reorder_delay_remaining = reorder_delay;
	bufferedServerCall();
}

/**
 * Callback funtion for sendWidgetOrder()
 *
 * Highlights the updated widgets and resets their odd/even style
 */
function sendWidgetOrderCallback( server_response )
{
	// alert( server_response+' vs '+blog );
	doFade( '.server_updating' ); // highlight updated widgets
	jQuery( '.server_updating' ).removeClass( 'server_updating' ); // remove "needs updating"
	colourWidgets(); // redo widget odd/even colours
}

/**
 * Buffered server call
 *
 * Waits until delay period is over and then sends new order to the server
 * only sends if the current widget order has changed since last update
 */
function bufferedServerCall()
{
	var new_widget_order = getWidgetOrder();
	if( new_widget_order != current_widgets )
	{	// widget order has changed, we need to update
		jQuery( '#server_messages' ).html( '<div class="action_messages container-fluid"><ul><li><div class="alert alert-dismissible alert-info fade in">'+T_( 'Saving changes' )+'</div></li></ul></div>' ); // inform user

		current_widgets = new_widget_order; // store current order
		//add crumbs here
		new_widget_order += '&' + widget_crumb_url_param;
		jQuery( '.pending_update' ).removeClass( 'pending_update' ).addClass( 'server_updating' ); // change class to "updating"

		if( jQuery( 'input[type=hidden][name=wico_ID]' ).length )
		{	// Send param to re-order only single container:
			new_widget_order += '&wico_ID=' + jQuery( 'input[type=hidden][name=wico_ID]' ).val();
		}

		SendAdminRequest( 'widgets', 're-order', new_widget_order, false ); // send current order to server
	}
	else
	{	// widget order either hasn't changed or has been changed back to original order
		jQuery( '#server_messages' ).html( '<div class="action_messages container-fluid"><ul><li><div class="alert alert-dismissible alert-warning fade in">'
						+T_( 'Widget order unchanged' )+'</div></li></ul></div>' ); // inform user
		jQuery( '.pending_update' ).removeClass( 'pending_update' ); // remove "needs updating"
		colourWidgets(); // redo widget colours
	}
}


/**
 * Gets the current widget order
 *
 * @return string widget order
 */
function getWidgetOrder()
{
	// need to get every container, then every widget in container and send the lot to the server
	var containers = new Array()
	jQuery( '.widget_container' ).each(function()
	{
		var container_name = jQuery( this ).attr('id');
		containers[ container_name ] = '';
		jQuery( '#'+container_name+' .draggable_widget' ).each( function(){
			if( jQuery( this ).attr( 'id' ) && jQuery( this ).attr( 'id' ) != 'undefined' )
			{	// this is a widget
				 containers[container_name] += jQuery( this ).attr( 'id' ) + ', ';
			}
		});
	});

	var query_string = '';
	var containers_list = '';
	for( container in containers )
	{
		query_string += container+'='+containers[container]+'&';
		containers_list += container+',';
	}

	var r = ( typeof( blog_id ) != 'undefined' ? 'blog='+blog_id : '' ) +'&'+query_string+'container_list='+containers_list;

	// console.log( r );

	return r;
}


/**
 * Redo odd / even classes
 */
function colourWidgets()
{
		jQuery( ".draggable_widget" ).removeClass( "odd" ); // remove any odd classes
		jQuery( ".draggable_widget" ).removeClass( "even" ); // remove any even classes
		var pos = false; // will be used as a toggle for odd / even rows
		jQuery( "#current_widgets .draggable_widget" ).each( function(){
				pos = !pos; // toggle
				jQuery(this).addClass( ( pos ? "even" : "odd" ) ); // add relevant new odd/even class
		});
}

/**
 * Delete widget
 */
function deleteWidget( widget )
{
	var widget_ID = widget.substr( 6, widget.length );
	jQuery( '#wi_ID_' + widget_ID ).animate({
			backgroundColor: "#f88"
		},"fast", function(){
			jQuery( this ).remove(); // remove the widget row from HTML page
			colourWidgets(); // redo widget colours
			SendAdminRequest( 'widgets', 'delete', 'wi_ID=' +  widget_ID + '& ' + widget_crumb_url_param, false ); // send a request to delete widget from DB
		});
	return false;
}

/**
 * Duplicate widget
 */
function duplicateWidget( widget, mode )
{
	var widget_id = widget.substr( 6, widget.length );
	var query_string = 'wi_ID=' + widget_id + '&' + widget_crumb_url_param;
	if( mode == 'customizer' )
	{
		query_string += '&mode=customizer';
	}
	SendAdminRequest( 'widgets', 'duplicate', query_string, true );
	return false;
}


/**
 * Request edit screen from server...
 */
function editWidget( widget )
{
	jQuery( '#server_messages' ).html( '' );
	msg = "wi_ID="+widget.substr( 6, widget.length ) + '&' + widget_crumb_url_param;
	SendAdminRequest( "widgets", "edit", msg, true );
	return false;
}

/*
 * This is called when we get the response from the server:
 */
function widgetSettings( the_html, wi_type, wi_code )
{
	// add placeholder for widgets settings form:
	jQuery( 'body' ).append( '<div id="screen_mask" onclick="closeWidgetSettings()"></div><div id="widget_settings" class="modal-content"></div>' );
	jQuery( '#screen_mask' ).fadeTo(1,0.5).fadeIn(200);
	jQuery( '#widget_settings' ).html( the_html ).addClass( 'widget_settings_active edit_widget_' + wi_type + '_' + wi_code );
	jQuery( '#widget_settings' ).prepend( jQuery( '#server_messages' ) );
	AttachServerRequest( 'widget_checkchanges' ); // send form via hidden iframe

	// Create modal header for bootstrap skin
	var page_title = jQuery( '#widget_settings' ).find( 'form > h2.page-title:first' );
	if( page_title.length > 0 )
	{
		var page_title_icons = page_title.parent().children( 'span.global_icons' );
		var page_title_icons_html = '';
		if( page_title_icons.length > 0 )
		{
			page_title_icons.find( 'a.close_link' ).remove()
			page_title_icons_html = '<span class="pull-right">' + page_title_icons.html() + '</span>';
			page_title_icons.remove();
		}
		jQuery( '#widget_settings' ).prepend( '<div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>' + page_title_icons_html + '<h4 class="modal-title">' + page_title.html() + '</h4></div>' );
		page_title.remove();
		jQuery( '#widget_settings button.close' ).bind( 'click', closeWidgetSettings );
	}

	jQuery( '#widget_settings a.close_link' ).bind( 'click', closeWidgetSettings );
}

function widgetSettingsCallback( wi_ID, wi_name, wi_cache_status )
{
	jQuery( '#wi_ID_' + wi_ID + ' .widget_name' ).html( wi_name );
	jQuery( '#wi_ID_' + wi_ID + ' .widget_cache_status' ).html( getWidgetCacheIcon( 'wi_ID_'+wi_ID, wi_cache_status ) );
}

function closeWidgetSettings( action )
{
	if( ! jQuery( '#widget_settings' ).is( ':visible' ) )
	{	// Close popup window with widget settings only if it is really opened,
		// in order to don't hide #screen_mask when available widgets popup is opened currently:
		return false;
	}

	if( typeof( bozo ) != 'undefined' &&
	    ( typeof( action ) == 'undefined' || action != 'update' ) ) // Don't use bozo validator when form is updating
	{	// Display a confirmation of bozo validator before closing a modified widget edit form:
		if( bozo.validate_close() && ! confirm( bozo.validate_close() ) )
		{	// Don't close a modified widget edit form without confirmation:
			return false;
		}
		else
		{	// After confirmation we should reset bozo validator modification counter to don't display a confirmation twice on page closing/refreshing:
			bozo.reset_changes();
		}
	}

	jQuery( '#widget_settings' ).hide(); // removeClass( 'widget_settings_active' );
	jQuery( '#server_messages' ).insertBefore( '.available_widgets' );
	jQuery( '#widget_settings' ).remove();
	jQuery( '#screen_mask' ).remove();
	return false;
}

function showMessagesWidgetSettings( result )
{
	if( typeof( result ) != 'undefined' && result == 'success' && typeof( bozo ) != 'undefined' )
	{	// Reset bozo validator modification counter on successful updating widget settings,
		// in order to allow close widget settings form popup without unexpected JS confirmation:
		bozo.reset_changes();
	}

	jQuery( '#widget_settings' ).animate( {
			scrollTop: jQuery( '#widget_settings' ).scrollTop() +  + jQuery( '#server_messages' ).position().top - 20
		}, 100 );
	return false;
}


function T_( native_string )
{
	if( typeof( T_arr[ native_string ] ) == "undefined" )
	{ // we don't have a translation
		return native_string;
	}
	else
	{	// we have a translation
		return T_arr[ native_string ];
	}
}

/**
 * Converts the widget available list to something we can work with
 */
function convertAvailableList()
{
	// Open list on click, not on hover!
	jQuery( ".fieldset_title > span > a[id^='add_new']" ).attr( 'href', '#' ).bind( 'click', function(e)
	{
		// add placeholder for widgets settings form:
		jQuery( 'body' ).append( '<div id="screen_mask" onclick="closeAvailableWidgets()"></div>' );
		jQuery( '#screen_mask' ).fadeTo(1,0.5).fadeIn(200);
		offset = jQuery( this ).offset();
		var y = offset.top;
		// can't dislay any lower than this!:
		// var max_y = jQuery( window ).height() - jQuery( '.available_widgets' ).height(); // this doesn't work when window is scrolled :(
		var max_y = jQuery( document ).height() - 10 - jQuery( '.available_widgets' ).height();
		if( max_y < 20 ) { max_y = 20 };
		if( y > max_y ) { y = max_y };
		jQuery( '.available_widgets' ).addClass( 'available_widgets_active' ).attr( 'id', 'available_'+jQuery( this ).attr( "id" ) );

		// cancel default href action:
		return false;
	});

	// Close action:
	jQuery( '.available_widgets_toolbar > a' ).bind( 'click', function(e)
	{
		closeAvailableWidgets();
		// cancel default href action:
		return false;
	});

	jQuery( ".available_widgets li" ).each( function()
	{ // shuffle things around
		jQuery( this ).addClass( "new_widget" ); // add hook for detecting new widgets

		var the_link = jQuery( this ).children( 'a' ).attr( 'href' ); // grab the url
		the_link = the_link.substr( the_link.indexOf( '&type' ) + 1, the_link.length );

		// replace href with JS addnewwidget action:
		jQuery( this ).children( 'a' ).attr( 'href', '#' ).bind( 'click', function(){
			addNewWidget( this, the_link );
			// cancel default href action:
			return false;
		});
	});
}

/**
 * Close available widgets overlay
 */
function closeAvailableWidgets()
{
	if( ! jQuery( '.available_widgets' ).is( ':visible' ) )
	{	// Close popup window with available widgets only if it is really opened,
		// in order to don't hide #screen_mask when widget edit form is opened currently:
		return false;
	}

	jQuery( '.available_widgets' ).removeClass( 'available_widgets_active' );
	jQuery( '#screen_mask' ).remove();
}


/**
 * Adds a new widget to a container
 */
function addNewWidget( widget_list_item, admin_call )
{
	closeAvailableWidgets()

	var widget_id = jQuery( widget_list_item ).attr( "id" );
	jQuery( widget_list_item ).attr( "id", widget_id );

	var destination = jQuery( '.available_widgets' ).attr( 'id' );
	destination = destination.substr( 18, destination.length );

	SendAdminRequest( 'widgets', 'create', admin_call+"&blog="+blog_id+"&container="+destination, true );
}


/**
 * Adds a new widget to a container
 *
 * @param integer wi_ID Id of the new widget
 * @param string container Container to add widget to
 * @param intger wi_order ( unused atm ) Order of the widget on the server
 * @param string wi_name Name of the new widget
 * @param boolean wi_enabled Is the widget enabled?
 * @param boolean wi_plugin_status
 * @param string wi_cache_status Cache status
 * @param integer wi_ID Id of widget where new widget will be added next to
 * @param string mode Mode
 */
function addNewWidgetCallback( wi_ID, container, wi_order, wi_name, wi_enabled, wi_plugin_status, wi_cache_status, next_to_wi_ID, mode )
{
	jQuery( '.fade_me' ).removeClass( 'fade_me' ); // kill any active fades
	createWidget( 'wi_ID_' + wi_ID, container.replace( / /g, '_' ).replace( /:/g, '-' ), wi_order, wi_name, '', wi_enabled, wi_plugin_status, wi_cache_status, next_to_wi_ID, mode );
	doFade( '#wi_ID_'+wi_ID );
	if( reorder_delay_remaining > 0 )
	{ // send outstanding updates
		reorder_delay_remaining = 0;
	}
	else
	{ // no outstanding updates, store current order
		current_widgets = getWidgetOrder(); // store current order
	}
}

/**
 * Create a new widget in a container
 *
 * @param integer wi_ID Id of the new widget
 * @param string container Container to add widget to
 * @param integer wi_order ( unused atm ) Order of the widget on the server
 * @param string wi_name Name of the new widget
 * @param string wi_class CSS class
 * @param boolean wi_enabled Is the widget enabled?
 * @param boolean wi_plugin_disabled
 * @param string wi_cache_status Cache status
 * @param integer wi_ID Id of widget where new widget will be added next to
 * @param string mode Mode
 */
function createWidget( wi_ID, container, wi_order, wi_name, wi_class, wi_enabled, wi_plugin_disabled, wi_cache_status, next_to_wi_ID, mode )
{
	var newWidget = jQuery( '<li id="'+ wi_ID +'" class="draggable_widget"><span class="widget_title">'+wi_name+'</span></li>' );
	newWidget.find( 'a.widget_name' ).click( function()
	{
		return editWidget( wi_ID );
	} );
	if( wi_class )
	{ // add class
		jQuery( newWidget ).addClass( wi_class );
	}

	// Add state indicator:
	jQuery( newWidget ).prepend( jQuery( '<span class="widget_state">'+
			( wi_plugin_disabled ? disabled_plugin_tag :
			'<a href="#" class="toggle_action" onclick="return toggleWidget( \''+ wi_ID +'\' );">' +
				( wi_enabled ? enabled_icon_tag : disabled_icon_tag )+
			'</a>' ) +
		'</span>' ) );

	if( mode != 'customizer' )
	{
		// Add checkbox:
		jQuery( newWidget ).prepend( jQuery( '<span class="widget_checkbox' + ( wi_enabled ? ' widget_checkbox_enabled' : '' ) + '">' +
				'<input type="checkbox" name="widgets[]" value="' + wi_ID.replace( 'wi_ID_', '' ) + '" ' + ( wi_plugin_disabled ? 'disabled="disabled" ' : '' ) + '/>'+
			'</span>' ) );


		// Add icon to toggle cache status:
		var cacheIcon = jQuery( '<span class="widget_cache_status">' + getWidgetCacheIcon( wi_ID, wi_cache_status ) + '</span>' );
		jQuery( newWidget ).append( cacheIcon ); // add widget action icons
	}

	// Add action icons:
	// Toggle state
	var actionIcons = '<span class="widget_actions">';

	if( mode != 'customizer' )
	{
		actionIcons += '<a href="#" onclick="return editWidget( \'' + wi_ID + '\' );">' + edit_icon_tag + '</a>';
	}
	actionIcons += '<a href="#" onclick="return duplicateWidget( \'' + wi_ID + '\', \''  + mode + '\' );">' + duplicate_icon_tag + '</a>'
			+ '<a href="#" onclick="return deleteWidget( \'' + wi_ID + '\' );">' + delete_icon_tag + '</a></span>'

	actionIcons = jQuery( actionIcons );
	jQuery( newWidget ).append( actionIcons ); // add widget action icons

	if( next_to_wi_ID )
	{
		jQuery( '#container_' + container + ' li#wi_ID_' + next_to_wi_ID ).after( newWidget ); // add next to specified widget
	}
	else
	{
		jQuery( '#container_' + container ).append( newWidget );	// add widget to container
	}

	makeDragnDrop( '#'+wi_ID );
	colourWidgets();	// recolour the widgets
}

/**
 * Toggle the widget state.
 *
 * @param string Widget ID.
 */
function toggleWidget( wi_ID )
{
	SendAdminRequest( 'widgets', 'toggle', 'wi_ID=' + wi_ID.substr( 6 ) + '&' + widget_crumb_url_param, true );
	return false;
}

/**
 * Callback for toggling a widget.
 *
 * @param integer Widget ID
 * @param integer new widget state
 */
function doToggle( wi_ID, wi_enabled, wi_plugin_disabled )
{
	if( wi_plugin_disabled )
	{
		jQuery( '#wi_ID_' + wi_ID + ' .widget_state' ).html( disabled_plugin_tag );
		jQuery( '#wi_ID_' + wi_ID + ' .toggle_action' ).css( 'visibility', 'hidden' );
	}
	else
	{
		jQuery( '#wi_ID_' + wi_ID + ' .toggle_action' ).css( 'visibility', 'visible' );
		jQuery( '#wi_ID_' + wi_ID + ' .widget_state' ).html( '<a href="#" class="toggle_state" onclick="return toggleWidget( \'wi_ID_'+wi_ID+'\', \''+widget_crumb_url_param+'\' );">'+
					( wi_enabled ? enabled_icon_tag : disabled_icon_tag )+
				'</a>' );
	}
	if( wi_enabled )
	{
		jQuery( '#wi_ID_' + wi_ID + ' .widget_checkbox' ).addClass( 'widget_checkbox_enabled' );
	}
	else
	{
		jQuery( '#wi_ID_' + wi_ID + ' .widget_checkbox' ).removeClass( 'widget_checkbox_enabled' );
	}
	jQuery( '#wi_ID_' + wi_ID + ' .toggle_action' ).html( wi_enabled ? deactivate_icon_tag : activate_icon_tag );

	doFade( '#wi_ID_' + wi_ID );
}

/**
 * Toggle the widget cache status.
 *
 * @param string Widget ID.
 * @param string Action: 'enable', 'disable'
 */
function toggleCacheWidget( wi_ID, action )
{
	SendAdminRequest( 'widgets', 'cache_' + action, 'wi_ID=' + wi_ID.substr( 6 ) + '&' + widget_crumb_url_param, true );
	return false;
}

/**
 * Callback for toggling a widget cache status.
 *
 * @param integer Widget ID
 * @param integer new widget cache status
 */
function doToggleCache( wi_ID, wi_cache_status )
{
	jQuery( '#wi_ID_' + wi_ID + ' .widget_cache_status' ).html( getWidgetCacheIcon( 'wi_ID_'+wi_ID, wi_cache_status ) );

	doFade( '#wi_ID_' + wi_ID );
}

/**
 * replicates PHP's str_repeat() function
 *
 * @param string data string to repeat
 * @param integer multiplier number of repeats required
 *
 * @return the multiplied string
 */
function str_repeat( data, multiplier )
{
	return new Array( multiplier + 1 ).join( data );
}


/**
 * Get icon for widget cache status
 *
 * @param string Widget ID
 * @param string Widget cache status
 * @return string
 */
function getWidgetCacheIcon( wi_ID, wi_cache_status )
{
	switch( wi_cache_status )
	{
		case 'enabled':
			return '<a href="#" class="cache_action" onclick="return toggleCacheWidget( \''+wi_ID+'\', \'disable\' );">' + cache_enabled_icon_tag + '</a>';

		case 'disabled':
			return '<a href="#" class="cache_action" onclick="return toggleCacheWidget( \''+wi_ID+'\', \'enable\' );">' + cache_disabled_icon_tag + '</a>';

		case 'disallowed':
			return cache_disallowed_icon_tag;

		case 'denied':
			return '<a href="?ctrl=coll_settings&amp;tab=advanced&amp;blog=' + blog_id + '#fieldset_wrapper_caching">' + cache_denied_icon_tag + '</a>';
	}
}
