"use strict";

/**------------------------------------------------------------------------
 * writes console.log output
 *------------------------------------------------------------------------*/
NX.log = function (data1, data2) {

	if (data1 != undefined) {
		console.log(data1);
	}
	if (data2 != undefined) {
		console.log(data2);
	}
};


/**------------------------------------------------------------------------
 * [returns] - unique id
 *------------------------------------------------------------------------*/
NX.uniqueID = function () {
	return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
		var r = Math.random() * 16 | 0,
			v = c == 'x' ? r : (r & 0x3 | 0x8);
		return v.toString(16);
	});
}


/**--------------------------------------------------------------------------------------------------------------------------------------
 * DISPLAY NOTIFICATIONS TO END USER
 * - You can edit this notifications function to suit the notification system being used by the main application *
 *
 * [PARAMS] - object
 *
 * [REQUIRED PARAM]
 *        obj['message']
 *
 * [OPTIONAL - FOR NOTY]
 *       obj['type']        - success|warning
 *       obj['position']    - top|topLeft|topCenter|topRight|center|centerLeft|centerRight|bottom|bottomLeft|bottomCenter|bottomRight
 *--------------------------------------------------------------------------------------------------------------------------------------*/

NX.notification = function (obj) {

	"use strict";

	//state
	var state = false;

	//validate required
	if (typeof obj.message == 'undefined' || obj.message == '') {
		NX.log('notification() - no message was provided with notification request - exiting');
		return;
	}

	//check if we have noty
	if (typeof noty === 'function') {

		//default duration
		var duration = 3000;

		var position = (typeof NX.notification_position != 'undefined' && NX.notification_position != '') ? NX.notification_position : 'bottomLeft';
		var error_duration = (typeof NX.notification_error_duration != 'undefined' && NX.notification_error_duration != '') ? NX.notification_error_duration : duration;
		var success_duration = (typeof NX.notification_success_duration != 'undefined' && NX.notification_success_duration != '') ? NX.notification_success_duration : duration;
		var type = (typeof obj.type != 'undefined' && obj.type == 'success') ? 'success' : 'warning';

		//short display time for success message
		if (type == 'success') {
			duration = success_duration;
		}
		if (type == 'warning') {
			duration = error_duration;
		}

		//hide any existing
		$(".noty_bar").remove();

		//display noty message
		noty({
			text: obj.message,
			layout: position,
			type: type,
			timeout: duration,
			progressBar: false,
			closeWith: ['click', 'button', 'backdrop'],
		});

		//finished
		return;
	}

};




/** ---------------------------------------------------------------------------------------------AJAX UX---------------------------------------------------------*/
/**-----------------------------------------------------------------------------------------------------------------------------
 * [NAME-SPACE]: NXAJAX
 *
 * [LAST-REVISED] 01 Octomer 2019
 *
 * [REQUIRES] - In this order
 *            - [nextloop.core.js]
 *            - [nextloop.toolbox.js]
 *            - [nprogress.js] - https://github.com/rstacruz/nprogress
 *
 *
 *-------------------------------------------------------------------------------------------------------------------------------

 ==================================================================================================================================
	[MAKING REQUEST WITH VIRTUAL DOM]
	
	                      //create a virtual domitem
 						  var virtualDom = $('<div>', {
 						        attr: {
 						            'data-url': 'http://dashboard.com/create/user',
 						            'data-progress-bar': 'hidden',
			                        'data-ajax-type' : 'post',
                                    'data-progress-bar': 'hidden'
 						        }
  						   });

						   //optional post data (optional)
						   var virtualPostArray = {
                                 user_id: 22,
                                 user_name: 'John Doe',
                           };

						   //initiate request
 						   nxAjaxUxRequest(virtualDom, virtualPostArray);
====================================================================================================================================	
 *
 *          [DATA-REQUIRED]
 *          -------------------
 * 
 *            action url
 *            - [data-url]                                    url of the ajax request
 * 
 *
 *          [DATA-OPTIONAL]
 *          --------------------
 * 
 *            result target
 *            - ['data-results-target']                         [#id] of containter where results will be added
 *            - ['data-results-placement']                      will the resulsts be [append] or [replace] within the container
 *
 *            Infinite Scrolling
 *            - ['data-infinite-scroll-marker']                  [id] if applicable, the div used to track screen position for autoloading
 * 
 *            ajax method
 *            - [data-ajax-type]                                  [GET|POST|DELETE|PUT] - default type is GET. If you want to post a form
 *
 *            loading annimations
 *            -[data-button-loading-annimation="yes"] add loading annimation to clicked button (css: button-loading-annimation)
 *            -[data-button-disable-on-click="yes"] disable clicked button
 *            -[data-loading-target="main-body"] the id name only of the taret element
 *            -[data-loading-class="loading"] #create this class in your css file (loading | loading-before | loading-before-centre)
 *            -[data-loading-class="loading-placeholder"] #useful if you want to add loading as if itw as placeholder content (positioned inline)
 * 
 *           Hide Progress bar
 *           - [data-progress-bar='hidden']                      if set to 'hidden' the progress bar will not be shown. default is 'show'
 * 
 *           Do not show notification popup (regardless of whether the backend has sent one)
 *           -[data-notifications="disabled"]
 *
 *            loading overlays
 *            - [data-loading-overlay-target="main-body"] the id name only of the taret element
 *            - [data-loading-overlay-classname="overlay"] #create this class in your css file
 * 
 *            callback function (executed after ajax completed)
 *                - function names cannot be scoped, like NX.someFunction, but rather a plain function like NXsomeFunction
 *                - callback functions can also be specified in tje json resposne
 *           - [data-postrun-functions="foofunction,barfunction"] no spaces between comma
 * 
 *           [on start actions]
 *            -[data-onstart-hide="#some-element"] an element or a class to hide when the request starts
 *            -[data-onstart-show="#some-element"] an element or a class to show when the request starts
 * 
 *           Reset loading target
 *           data-reset-loading-target="true" (default is false)
 * 
 *           Modal Footer (hide or show)
 *           data-footer-visibility="hidden"
 *
 *-------------------------------------------------------------------------------------------------------------------------------
 *
 *   [STANDARD-FORM-POST]
 *   You can also send data contained in a form that the button is not a part of. Infact, it does not even have to be a form
 *   but can be a div with form elements.
 *         - The buttom must have the following attributes
 *                  - [data-type="form"]
 *                  - [data-form-id="some-form-id"]
 *                  - [data-ajax-type="post"]
 *                  - [data-url="foo.com"]
 *         - All the [form-input] elements will be sent as post data with the request
 * 
 *   [DIRECT-FORM-POST]
 *   If post data is being sent to the backend, it can be passed [directly] as an array (e.g. ['name':'fred', 'surname':'marks'] )
 *           [example][optionalPostArray]
 *           var optionalPostArray = ['name':'fred', 'surname':'marks'];
 *           nxAjaxUxRequest($(this), optionalPostArray);
 * 
 *   [RADIO BUTTONS]
 *   The current version of this module does not properly support post values for radio check boxes. Below is a workaround
 *   You create a hidden field with the real name of the field value. The value for this field will be set either on click of
 *   submit button, or inside the valicator like this
 * 
 *      <input type="hidden" name="leadstatus_color" id="leadstatus_color" value=""> 
 * 
        //validation
        $("#commonModalForm").validate({
            rules: {
                leadstatus_title: "required"
            },
            submitHandler: function (form) {

                //set selector color
                var color = $("input[name='leadstatus_color_radio']:checked").val();
                $("#leadstatus_color").val(color);
                //ajax request
                nxAjaxUxRequest($("#commonModalSubmitButton"));
            }
        });
 *
 * 
 *
 * ---------------------------------------------------------------------------------------------------------------------------------
 * [BACKEND-JSON-RESPONSE] [EXAMPLES]

        [dom_attributes]
            $jsondata['dom_attributes'][] = [
                'selector' => '#js-sorting-id',
                'attr' => 'data-url',
                'value' => 'foobar',
			];

   ----------------------------------------------------------	

        [dom_attributes]
            $jsondata['dom_html'][] = [
                'selector' => '#ajax-project-tab-body',
                'action' => 'replace',
                'value' => $html,
			];

   ----------------------------------------------------------			

        [dom_classes]	
            //[action options] add|remove
            $jsondata['dom_classes'][] = [
                'selector' => '#main-table',
                'action' => 'add',
                'value' => 'some-class-name',
			];

   ----------------------------------------------------------			

        [next_url]	
			$jsondata['next_url'] = 'http://foo';"

   ----------------------------------------------------------			

        [redirect_url]	
			$jsondata['redirect_url'] = 'http://foo';"

   ----------------------------------------------------------			

        [more_results]	
			$jsondata['more_results'] = 1; //1|0"
			
   ----------------------------------------------------------			

        [notification]	
            //[type options] error|success
            $jsondata['notification'] = [
                'type' => 'error',
                'value' => 'request could not be completed',
			];
			
   ----------------------------------------------------------			

       [dom_visibility]	
           // show | hide | slideup | slideup-slow | slidedown | slidedown-slow
           // fadeout |fadeout-slow | slideup-remove | slideup-slow-remove
           // fadeout-remove | fadeout-slow-remove | hide-remove | close-modal
		   $jsondata['dom_visibility'][] = [
			'selector' => '#some-itme',
			'action' => 'slideout-slow',
		   ];
	

       [response]	
		   return response()->json($jsondata);

  ----------------------------------------------------------			

        [tinymce_reset]	
            //reset tinymce editors by id
            $jsondata['tinymce_reset'][] = [
                'selector' => 'some-editor', //no hash sign
			];
		   
/**------------------------------------------------------------------------
 * [GET RESULST FROM BACKEND]
 * ------------------------------------------------------------------------*/
function nxAjaxUxRequest(obj, virtualPostArray = {}) {

	//Nextloop Namespace
	var NXAJAX = (typeof NXAJAX == 'undefined') ? {} : NXAJAX;

	NXAJAX.OBJ = obj;

	//DEBUG MODE - CONSOLE DEBUG OUTPUT
	NXAJAX.debug_mode = (typeof js_debug_mode != 'undefined') ? js_debug_mode : 1;
	//set globally or toggle [1|0]

	NXAJAX.preRun = function () {

		//set some global objects
		NXAJAX.data = {};

		//first add any virtual post data (manual orray) that was passed in the request
		NXAJAX.post = virtualPostArray;
		console.log(NXAJAX.post);
		NXAJAX.payload = {};

		//some preset data -  autoloading (if applicable)
		NXAJAX.payload['more_results'] = 0;
		NXAJAX.payload['next_url'] = '';

	}();


	/**------------------------------------------------------------------------
	 * some actions on start
	 *------------------------------------------------------------------------*/

	NXAJAX.onStart = function () {

		//hide some elements
		if (typeof NXAJAX.OBJ.attr("data-onstart-hide") != 'undefined' && NXAJAX.OBJ.attr("data-onstart-hide") != '') {
			var dom_element = NXAJAX.OBJ.attr("data-onstart-hide");
			$(dom_element).hide();
		}

		//show some elements
		if (typeof NXAJAX.OBJ.attr("data-onstart-show") != 'undefined' && NXAJAX.OBJ.attr("data-onstart-show") != '') {
			var dom_element = NXAJAX.OBJ.attr("data-onstart-show");
			$(dom_element).show();
		}

	}();


	/**------------------------------------------------------------------------
	 * output debug data - only if debug mode is enabled
	 * [returns] - bool
	 *------------------------------------------------------------------------*/

	NXAJAX.log = function (payload1, payload2) {
		if (NX.debug_javascript) {
			if (payload1 != undefined) {
				console.log(payload1);
			}
			if (payload2 != undefined) {
				console.log(payload2);
			}
		}
	};

	/**------------------------------------------------------------------------
	 * get all the required data from the event
	 * [returns] - bool
	 *------------------------------------------------------------------------*/

	NXAJAX.eventData = function (obj) {

		//debug
		NXAJAX.log("[ajax] eventData() - setting all data attributes from event - [payload]:", obj, );

		///save this button/object etc
		NXAJAX.obj = obj;

		//require data
		NXAJAX.data.url = obj.attr("data-url");

		//ajax request method type
		if (typeof obj.attr("data-ajax-type") != 'undefined' && obj.attr("data-ajax-type") != '') {
			NXAJAX.data.ajax_type = obj.attr("data-ajax-type");
		} else {
			NXAJAX.data.ajax_type = 'GET';
		}

		//optional data (loading animation target)
		if (typeof obj.attr("data-loading-target") != 'undefined' && obj.attr("data-loading-target") != '') {
			NXAJAX.data.loading_target = obj.attr("data-loading-target");
		} else {
			NXAJAX.data.loading_target = 'foo';
		}

		//optional data (button loading animation target)
		if (typeof obj.attr("data-button-loading-annimation") != 'undefined' && obj.attr("data-button-loading-annimation") == 'yes') {
			NXAJAX.data.button_loading_animation = 'yes';
		} else {
			NXAJAX.data.button_loading_animation = 'no';
		}


		//optional data (disable button on click)
		if (typeof obj.attr("data-button-disable-on-click") != 'undefined' && obj.attr("data-button-disable-on-click") == 'yes') {
			NXAJAX.data.button_disable_on_click = 'yes';
		} else {
			NXAJAX.data.button_disable_on_click = 'no';
		}

		//optional data (loading annimation class)
		if (typeof obj.attr("data-loading-class") != 'undefined' && obj.attr("data-loading-class") != '') {
			NXAJAX.data.loading_class = obj.attr("data-loading-class");
		} else {
			NXAJAX.data.loading_class = 'loading';
		}

		//optional data (loading annimation overlay target)
		if (typeof obj.attr("data-loading-overlay-target") != 'undefined' && obj.attr("data-loading-overlay-target") != '') {
			NXAJAX.data.overlay_target = obj.attr("data-loading-overlay-target");
		} else {
			NXAJAX.data.overlay_target = 'foo';
		}

		//optional data (loading annimation overlay class)
		if (typeof obj.attr("data-loading-overlay-classname") != 'undefined' && obj.attr("data-loading-overlay-classname") != '') {
			NXAJAX.data.overlay_classname = obj.attr("data-loading-overlay-classname");
		} else {
			NXAJAX.data.overlay_classname = '';
		}

		//optional data (show or hide progress bar on top of page)
		if (typeof obj.attr("data-progress-bar") != 'undefined' && obj.attr("data-progress-bar") == 'hidden') {
			NXAJAX.data.progress_bar = 'hidden';
		} else {
			NXAJAX.data.progress_bar = 'show';
		}

		//optional data (enable or display popup notifciations)
		if (typeof obj.attr("data-notifications") != 'undefined' && obj.attr("data-notifications") == 'disabled') {
			NXAJAX.data.show_notification = false;
		} else {
			NXAJAX.data.show_notification = true;
		}

		//[optional] - infinite scroll if applicable - the div for tracking page
		NXAJAX.data.infinite_scroll_marker = obj.attr("data-infinite-scroll-marker");


		//call back function 
		if (typeof obj.attr("data-postrun-functions") != 'undefined') {
			NXAJAX.data.postrun_functions = obj.attr("data-postrun-functions").split(",");
		} else {
			NXAJAX.data.postrun_functions = [];
		}

		//do not reset checkboxes when completing the response
		NXAJAX.data.skip_checkboxes_reset = obj.attr("data-skip-checkboxes-reset");

		//debug
		NXAJAX.log("[ajax] eventData() - current NXAJAX.data array content - [payload]:", NXAJAX.data);


		//reset loading target
		if (obj.attr("data-reset-loading-target")) {
			var target = obj.attr("data-loading-target");
			if (target != '' && target != null) {
				$("#" + target).html("");
			}
		}

		return;

	};



	/**------------------------------------------------------------------------
	 * validates all the required data for a valid ajax request
	 * [returns] - bool
	 *------------------------------------------------------------------------*/

	NXAJAX.validateRequired = function () {

		//debug
		NXAJAX.log("[ajax] validateRequired() - validating required data - [payload]:", NXAJAX);

		//inital
		var state = true;

		//required items
		var required = ['url'];
		//can add more to this array

		//loop through and validate all required
		$.each(required, function (index, value) {
			if (NXAJAX.data[value] == undefined) {
				state = false;
				//debug
				NXAJAX.log('[ajax] NXAJAX.validateRequired() - [error] required [NXAJAX.data] item is missing: (' + value + ') - [suggest] check data attributes');
			}
		});
		return state;
	};

	/**------------------------------------------------------------------------
	 * If event is a form submission (i.e.e search for) get all form field data
	 * and save to an object
	 * [returns] - object
	 *------------------------------------------------------------------------*/

	NXAJAX.processPostData = function (obj) {

		//debug
		NXAJAX.log('[ajax] processPostData() - adding form post data, if available - [payload]:', obj);

		//reset post object
		//NXAJAX.post = {};

		if (obj.attr('type') == 'submit' || obj.attr('data-type') == 'search' || obj.attr('data-type') == 'form' || obj.attr('data-type') == 'PUT' || obj.attr('data-ajax-type') == 'post') {


			//get the form - a specified form ID (NB: it does not have to be an actual form, but can be a div, with form elements)
			if (typeof obj.attr('data-form-id') != 'undefined' && obj.attr('data-form-id') != '') {
				var form = $("#" + obj.attr('data-form-id'));
			} else {
				//assume the parent form of this button
				var form = obj.parents('form:first');
			}

			//find all [input, textarea, select]
			form.find("input, textarea, select").each(function () {
				var field_name = $(this).attr('name');

				//special consideration for ckeditor textarea (must have data attr [data-type = ckeditor])
				if ($(this).attr('data-type') == 'ckeditor') {
					var field_id = $(this).attr('id');
					var field_value = CKEDITOR.instances[field_id].getData();
				} else {
					//general form fields
					var field_value = $(this).val();

					//check boxes form fields (seems obvoius but without this does not worl as expected)
					if ($(this).is(':checkbox')) {
						if ($(this).prop('checked')) {
							var field_value = 'on';
						} else {
							var field_value = '';
						}

					}
				}
				//add to post object
				if (field_name != undefined) {
					NXAJAX.post[field_name] = field_value;
				}
			});
		}
		//debug
		NXAJAX.log(NXAJAX.post);

		return;
	};

	/**------------------------------------------------------------------------
	 * [RENDER AJAX VIEW]
	 * - places all the payload into the right places
	 *------------------------------------------------------------------------*/

	NXAJAX.loadingAnimation = function (action) {

		//debug
		NXAJAX.log('[ajax] loadingAnimation() - setting to (' + action + ')');

		/** ---------------------------------------------------------------------------
		 * [OVERLAYS]
		 * - An ovelay class will be added or removed from the target dom element
		 * - Selector must be a DOM element ID
		 * - Data can be set via dom attributes (e.g for click events)
		 *             - data-loading-overlay-target='foo'
		 *             - data-loading-overlay-class='bar'
		 * - Data can also be set manually as a var in the calling HTML page
		 *             - NXAJAX.data.overlay_target = 'foo';
		 *             - NXAJAX.data.overlay_classname = 'bar';
		 *-----------------------------------------------------------------------------*/
		var overlay_target = NXAJAX.data.overlay_target;
		var overlay_class = NXAJAX.data.overlay_classname;

		//show or hide overlays
		if (action != undefined) {
			if (overlay_target != undefined && overlay_class != undefined) {
				//show
				if (action == 'show') {
					$("#" + overlay_target).addClass(overlay_class);
				}
				//hide
				if (action == 'hide') {
					$("#" + overlay_target).removeClass(overlay_class);
				}
			}
		}

		/** ---------------------------------------------------------------------------
		 * [LOADING ANNIMATIONS]
		 * - A loading class will be added or removed from the target dom element
		 * - Selector must be a DOM element ID
		 * - Data can be set via dom attributes (e.g for click events)
		 *             - data-loading-loading-target='foo'
		 *             - data-loading-loading-class='bar'
		 * - Data can also be set manually as a var in the calling HTML page
		 *             - NXAJAX.data.loading_target = 'foo';
		 *             - NXAJAX.data.loading_class = 'bar';
		 *-----------------------------------------------------------------------------*/
		var loading_target = NXAJAX.data.loading_target;
		var loading_class = NXAJAX.data.loading_class;

		//show or hide loading annimations
		if (action != undefined) {
			if (loading_target != undefined && loading_class != undefined) {
				//show
				if (action == 'show') {
					$("#" + loading_target).addClass(loading_class);
				}
				//hide
				if (action == 'hide') {
					$("#" + loading_target).removeClass(loading_class);
					//also remove
					$("#" + loading_target).removeClass('loading-placeholder');

				}
			}
		}

		return;
	};


	/**------------------------------------------------------------------------
	 * annimate clicked button
	 *------------------------------------------------------------------------*/
	NXAJAX.annimateButton = function () {
		if (NXAJAX.data.button_loading_animation == 'yes') {
			NXAJAX.obj.addClass('button-loading-annimation');
		}
	}

	/**------------------------------------------------------------------------
	 * disable clicked button
	 *------------------------------------------------------------------------*/
	NXAJAX.disableButton = function () {
		if (NXAJAX.data.button_disable_on_click == 'yes') {
			NXAJAX.obj.prop("disabled", true);
		}
	}

	/**------------------------------------------------------------------------
	 * remove button anniation
	 *------------------------------------------------------------------------*/
	NXAJAX.resetAnnimateButton = function () {
		NXAJAX.obj.removeClass('button-loading-annimation');
		NXAJAX.obj.prop("disabled", false);
	}


	/**------------------------------------------------------------------------
	 * enable any previously disabled button
	 *------------------------------------------------------------------------*/
	NXAJAX.resetDisableButton = function () {
		if (NXAJAX.data.button_disable_on_click == 'yes') {
			NXAJAX.obj.prop("disabled", false);
		}
	}

	/**------------------------------------------------------------------------
	 * get the ajax response and save to the main object
	 *------------------------------------------------------------------------*/

	NXAJAX.getPayload = function (obj) {

		NXAJAX.log('[ajax] getPayload() - processing response data from backend - [payload]:', obj);

		//state
		var state = true;

		//redirect request
		NXAJAX.payload.redirect_url = obj.redirect_url;
		NXAJAX.payload.delayed_redirect_url = obj.delayed_redirect_url;

		//reset tinymce editors
		NXAJAX.payload.tinymce_reset = obj.tinymce_reset;

		//reset tinymce editors
		NXAJAX.payload.tinymce_new_data = obj.tinymce_new_data;

		//dom html()
		NXAJAX.payload.dom_html = obj.dom_html;


		//dom html that is done at the end of all other actions
		NXAJAX.payload.dom_html_end = obj.dom_html_end;

		//dom val()
		NXAJAX.payload.dom_val = obj.dom_val;

		//dom dom_move_element
		NXAJAX.payload.dom_move_element = obj.dom_move_element;


		//dom state
		NXAJAX.payload.dom_state = obj.dom_state;

		//dom attributes
		NXAJAX.payload.dom_attributes = obj.dom_attributes;

		//dom propery
		NXAJAX.payload.dom_property = obj.dom_property;

		//dom css
		NXAJAX.payload.dom_css = obj.dom_css;

		//dom classes
		NXAJAX.payload.dom_classes = obj.dom_classes;

		//dom visibility
		NXAJAX.payload.dom_visibility = obj.dom_visibility;

		//dom chained effects
		NXAJAX.payload.dom_chained_effects = obj.dom_chained_effects;

		//our next offset
		NXAJAX.payload.offset = obj.offset;

		//do we any more results to follow (used by autoload)
		NXAJAX.payload.more_results = obj.more_results;

		//if this is for autoloading, the next autoload url
		NXAJAX.payload.next_url = obj.next_url;

		//get any notification
		NXAJAX.payload.notification = obj.notification;

		//browser url update
		NXAJAX.payload.dom_browser_url = obj.dom_browser_url;

		//postrun function
		NXAJAX.payload.postrun_functions = obj.postrun_functions;

		//postrun function
		NXAJAX.payload.dom_action = obj.dom_action;

		return state;

	};

	/**-----------------------------------------------------------------------------------------------------------------------------------
	 * [UPDATE - DOM HTML] (if applicable)
	 *
	 * [JQUERY]
	 *    $("#foo").html(bar); //replace
	 *    $("#foo").append(bar); //append
	 *
	 * [EXAMPLE DATA SENT]
	 *    ['dom_html'][0]['selector'] = '#main-table'       : valid dom selector '.some_class' | '#some_id' | '[input-type=""]'
	 *    ['dom_html'][0]['action'] = 'replace'             : replace|append
	 *    ['dom_html'][0]['value'] = 'html code here'       : new value
	 *
	 *------------------------------------------------------------------------------------------------------------------------------------*/

	NXAJAX.updateDomHTML = function ($timing = '') {

		//get the payload
		if ($timing == 'end') {
			var payload = NXAJAX.payload.dom_html_end;
		} else {
			var payload = NXAJAX.payload.dom_html;
		}

		//debug
		NXAJAX.log('[ajax] updateDomHTML() - updating dom if applicable - [payload]:', payload);

		//update the DOM (id|class|literal)
		if (payload != undefined && typeof payload == 'object') {
			//loop through the payload and update the dom
			$.each(payload, function (index, value) {
				//sanity check - make sure its an object
				if (typeof value == 'object') {
					if (value.selector != undefined && value.action != undefined && value.value != undefined) {
						//replace
						if (value.action == 'replace') {
							$(value.selector).html(value.value);
						}
						//replace-wth
						if (value.action == 'replace-with') {
							$(value.selector).replaceWith(value.value);
						}
						//append
						if (value.action == 'append') {
							$(value.selector).append(value.value);
						}
						//prepend
						if (value.action == 'prepend') {
							$(value.selector).prepend(value.value);
						}
						//reset to empty
						if (value.action == 'reset') {
							$(value.selector).html('');
						}
					}
				}
			});
		}
		return;
	};

	/**-----------------------------------------------------------------------------------------------------------------------------------
	 * [UPDATE - DOM ATTRIBUTES] (if applicable)
	 *
	 * [JQUERY]
	 *    $("#foo").attr('data-age', '24');
	 *    $("#foo").attr('src', 'image.jpg');
	 *
	 * [EXAMPLE DATA SENT]
	 *    ['dom_attributes'][0]['selector'] = '#main-table'       : valid dom selector '.some_class' | '#some_id' | '[input-type=""]'
	 *    ['dom_attributes'][0]['attr'] = 'src'                   : valid dom attribute
	 *    ['dom_attributes'][0]['value'] = 'image.jpg'            : new value
	 *
	 *------------------------------------------------------------------------------------------------------------------------------------*/
	NXAJAX.updateDomAttributes = function () {

		//get the payload
		var payload = NXAJAX.payload.dom_attributes;

		//debug
		NXAJAX.log('[ajax] updateDomAttributes() - updating dom if applicable - [payload]:', payload);

		//update the DOM (id|class|literal)
		if (payload != undefined && typeof payload == 'object') {
			//loop through the payload and update the dom
			$.each(payload, function (index, value) {
				//sanity check - make sure its an object
				if (typeof value == 'object') {
					if (value.selector != undefined && value.attr != undefined && value.value != undefined) {
						//replace
						$(value.selector).attr(value.attr, value.value);
					}
				}
			});
		}
		return;
	};


	/**-----------------------------------------------------------------------------------------------------------------------------------
	 * [POST RUN FUNCTIONS]
	 *
	 * [EXAMPLE DATA SENT]
	 *    ['postrun_function'][0]['value'] = 'nxFooBar'            : new value
	 *
	 *------------------------------------------------------------------------------------------------------------------------------------*/
	NXAJAX.postRunFunctions = function () {

		//get the payload
		var payload = NXAJAX.payload.postrun_functions;

		//debug
		NXAJAX.log('[ajax] postRunFunctions() - running an specified postrun js', payload);

		//update the DOM (id|class|literal)
		if (payload != undefined && typeof payload == 'object') {
			//loop through the payload and update the dom
			$.each(payload, function (index, value) {
				//sanity check - make sure its an object
				if (typeof value == 'object') {
					if (value.value != undefined) {
						//i function exists, run it
						if (typeof window[value.value] === "function") {
							window[value.value]();
						}
					}
				}
			});
		}
		return;
	};

	/**-----------------------------------------------------------------------------------------------------------------------------------
	 * [UPDATE - DOM ATTRIBUTES] (if applicable)
	 *
	 * [JQUERY]
	 *    $("#foo").prop('checked', true);
	 *
	 * [EXAMPLE DATA SENT]
	 *    ['dom_property'][0]['selector'] = '#agree-terms'       : valid dom selector '.some_class' | '#some_id' | '[input-type=""]'
	 *    ['dom_property'][0]['prop'] = 'checked'               : valid dom attribute
	 *    ['dom_property'][0]['value'] = 'true'                : new value
	 *
	 *------------------------------------------------------------------------------------------------------------------------------------*/
	NXAJAX.updateDomProperty = function () {

		//get the payload
		var payload = NXAJAX.payload.dom_property;

		//debug
		NXAJAX.log('[ajax] updateDomProperty() - updating dom if applicable - [payload]:', payload);

		//update the DOM (id|class|literal)
		if (payload != undefined && typeof payload == 'object') {
			//loop through the payload and update the dom
			$.each(payload, function (index, value) {
				//sanity check - make sure its an object
				if (typeof value == 'object') {
					if (value.selector != undefined && value.prop != undefined && value.value != undefined) {
						//replace
						$(value.selector).prop(value.prop, value.value);
					}
				}
			});
		}
		return;
	};

	/**-----------------------------------------------------------------------------------------------------------------------------------
	 * [UPDATE - DOM CSS] (if applicable)
	 *
	 * [JQUERY]
	 *    $("#foo").css('font-size', '24px');
	 *    $("#foo").attr('src', 'image.jpg');
	 *
	 * [EXAMPLE DATA SENT]
	 *    ['dom_attributes'][0]['selector'] = '#main-table'       : valid dom selector '.some_class' | '#some_id' | '[input-type=""]'
	 *    ['dom_attributes'][0]['attr'] = 'font-size'                   : valid css attribute
	 *    ['dom_attributes'][0]['value'] = '12px'            : new value
	 *
	 *------------------------------------------------------------------------------------------------------------------------------------*/
	NXAJAX.updateDomCSS = function () {

		//get the payload
		var payload = NXAJAX.payload.dom_css;

		//debug
		NXAJAX.log('[ajax] updateDomCSS() - updating dom css if applicable - [payload]:', payload);

		//update the DOM (id|class|literal)
		if (payload != undefined && typeof payload == 'object') {
			//loop through the payload and update the dom
			$.each(payload, function (index, value) {
				//sanity check - make sure its an object
				if (typeof value == 'object') {
					if (value.selector != undefined && value.attr != undefined && value.value != undefined) {
						//replace
						$(value.selector).css(value.attr, value.value);
					}
				}
			});
		}
		return;
	};

	/**-----------------------------------------------------------------------------------------------------------------------------------
	 * [REDIRECT TO SPECIFIED URL]
	 *
	 * [JAVASCRIPT]
	 *    $("#foo").css('font-size', '24px');
	 *    $("#foo").attr('src', 'image.jpg');
	 *
	 * [EXAMPLE DATA SENT]
	 *    ['redirect_url'] = 'http://www.google.com'

	 *------------------------------------------------------------------------------------------------------------------------------------*/
	NXAJAX.updateRedirect = function () {

		//get the payload
		var payload = NXAJAX.payload.redirect_url;

		//debug
		NXAJAX.log('[ajax] updateRedirect() - url redirect request- [url]:', payload);

		//update the DOM (id|class|literal)
		if (payload != undefined && typeof payload != '') {

			//close any open modals
			$('.modal').modal('hide');

			//redirect
			window.location.replace(payload);

			//progress bar start
			if (NXAJAX.data.progress_bar == 'show') {
				NProgress.set(0.99);
			}
		}
		return;
	};

	/**-----------------------------------------------------------------------------------------------------------------------------------
	 * [REDIRECT TO SPECIFIED URL] after other actions have been done
	 *
	 * [EXAMPLE DATA SENT]
	 *    ['delayed_redirect_url'] = 'http://www.google.com'

	 *------------------------------------------------------------------------------------------------------------------------------------*/
	NXAJAX.updateDelayedRedirect = function () {

		//get the payload
		var payload = NXAJAX.payload.delayed_redirect_url;

		//debug
		NXAJAX.log('[ajax] updateRedirect() - url redirect request- [url]:', payload);

		//update the DOM (id|class|literal)
		if (payload != undefined && typeof payload != '') {

			//close any open modals
			$('.modal').modal('hide');

			//close any right panels
			if (typeof NXcloseSidePanel === "function") {
				NXcloseSidePanel();
			}

			//redirect
			window.location.replace(payload);

			//progress bar start
			if (NXAJAX.data.progress_bar == 'show') {
				NProgress.set(0.99);
			}
		}
		return;
	};

	/**-----------------------------------------------------------------------------------------------------------------------------------
	 * [UPDATE - DOM CLASSES] (if applicable)
	 *
	 * [JQUERY]
	 *    $("#foo").addClass('bar');
	 *    $("#foo").removeClass('bar');
	 *
	 * [EXAMPLE DATA SENT]
	 *    ['dom_classes'][0]['selector'] = '#main-table'       : valid dom selector '.some_class' | '#some_id' | '[input-type=""]'
	 *    ['dom_classes'][0]['action'] = 'add'                 : add|remove
	 *    ['dom_classes'][0]['value'] = 'some-class-name'      : new value
	 *
	 *------------------------------------------------------------------------------------------------------------------------------------*/
	NXAJAX.updateDomClasses = function () {

		//get the payload
		var payload = NXAJAX.payload.dom_classes;

		//debug
		NXAJAX.log('[ajax] updateDomClasses() - updating dom if applicable - [payload]:', payload);
		//update the DOM (id|class|literal)
		if (payload != undefined && typeof payload == 'object') {
			//loop through the payload and update the dom
			$.each(payload, function (index, value) {
				//sanity check - make sure its an object
				if (typeof value == 'object') {
					if (value.selector != undefined && value.action != undefined && value.value != undefined) {
						//add
						if (value.action == 'add') {
							$(value.selector).addClass(value.value);
						}
						//remove
						if (value.action == 'remove') {
							$(value.selector).removeClass(value.value);
						}
					}
				}
			});
		}
		return;
	};

	/**-----------------------------------------------------------------------------------------------------------------------------------
	 * [UPDATE - DOM FADEIN&OUT] (if applicable)
	 *
	 * This is for a smoother chained effects like fadeout one element and fadein another.
	 * You can add more effects here
	 *
	 * [JQUERY]
	 *          //example fadeout & fadein
	 *    		$("#foo").fadeOut(function() {
	 *             $("#foo").fadeIn();
	 *         });
	 *
	 * [EXAMPLE DATA SENT]
	 *    ['dom_chained_effects'][0]['selector_first'] = '#foo'       : valid dom selector '.some_class' | '#some_id' | '[input-type=""]'
	 *    ['dom_chained_effects'][0]['selector_second'] = '#bar'      : valid dom selector '.some_class' | '#some_id' | '[input-type=""]'
	 *    ['dom_chained_effects'][0]['effect'] = 'fadeout-fadein'     : valid css attribute
	 *
	 *------------------------------------------------------------------------------------------------------------------------------------*/
	NXAJAX.updateDomChainedEffects = function () {

		//get the payload
		var payload = NXAJAX.payload.dom_chained_effects;

		//debug
		NXAJAX.log('[ajax] updateDomChainedEffects() - updating dom chained effects if applicable - [payload]:', payload);

		//update the DOM (id|class|literal)
		if (payload != undefined && typeof payload == 'object') {
			//loop through the payload and update the dom
			$.each(payload, function (index, value) {
				//sanity check - make sure its an object
				if (typeof value == 'object') {
					if (value.selector_first != undefined && value.selector_second != undefined && value.effect != undefined) {

						//[effect] fadeout and fadein
						if (value.effect == 'fadeout-fadein') {
							$(value.selector_first).fadeOut(function () {
								$(value.selector_second).fadeIn();
							});
						}

						//[effect] fadeout and fadein
						if (value.effect == 'fadein-fadeout') {
							$(value.selector_first).fadeIn(function () {
								$(value.selector_second).fadeOut();
							});
						}

						//[effect] slideup slidedown
						//TO-DO																		
					}
				}
			});
		}
		return;
	};

	/**-----------------------------------------------------------------------------------------------------------------------------------
	 * [UPDATE - DOM VISIBILITY] (if applicable)
	 *
	 * [JQUERY]
	 *    $("#foo").('show');
	 *    $("#foo").('hide');
	 *
	 * [EXAMPLE DATA SENT]
	 *    ['dom_visibility'][0]['selector'] = '#main-table'       : valid dom selector '.some_class' | '#some_id' | '[input-type=""]'
	 *    ['dom_visibility'][0]['action'] = 'show'                 : show|hide|slideup|slideup-slow|fadeout|fadeout-slow|fadein|fadein-slow
	 *                                                               close-modal | enable | disable
	 * 
	 * [REMOVING DOM ELEMENT]
	 *   ['dom_visibility'][0]['action'] = 'show'                 : hide-remove|slideup-remove|slideup-slow-remove|fadeout-remove|fadeout-slow-remove
	 *
	 *------------------------------------------------------------------------------------------------------------------------------------*/
	NXAJAX.updateDomVisibility = function () {

		//get the payload
		var payload = NXAJAX.payload.dom_visibility;

		//debug
		NXAJAX.log('[ajax] updateDomVisibility() - updating dom if applicable - [payload]:', payload);

		//update the DOM (id|class|literal)
		if (payload != undefined && typeof payload == 'object') {
			//loop through the payload and update the dom
			$.each(payload, function (index, value) {
				//sanity check - make sure its an object
				if (typeof value == 'object') {
					if (value.selector != undefined && value.action != undefined) {
						//show
						if (value.action == 'show') {
							$(value.selector).show();
						}
						//remove
						if (value.action == 'hide') {
							$(value.selector).hide();
						}
						//show
						if (value.action == 'show-flex') {
							$(value.selector).css('display', 'flex');
						}
						//remove-remove
						if (value.action == 'hide-remove') {
							$(value.selector).hide();
							$(value.selector).remove();
						}
						//slide up
						if (value.action == 'slideup') {
							$(value.selector).slideUp();
						}
						//slide up & remove
						if (value.action == 'slideup-remove') {
							$(value.selector).slideUp();
							$(value.selector).remove();
						}
						//slide up slow
						if (value.action == 'slideup-slow') {
							$(value.selector).slideUp("slow");
						}
						//slide up slow & remove
						if (value.action == 'slideup-slow-remove') {
							$(value.selector).slideUp("slow");
							$(value.selector).remove();
						}
						//slide down
						if (value.action == 'slidedown') {
							$(value.selector).slideDown();
						}
						//slide down slow
						if (value.action == 'slidedown-slow') {
							$(value.selector).slideDown("slow");
						}
						//fadeout
						if (value.action == 'fadeout') {
							$(value.selector).fadeOut();
						}
						//fadeout & remove
						if (value.action == 'fadeout-remove') {
							$(value.selector).fadeOut();
							$(value.selector).remove();
						}
						//fadeout-slow
						if (value.action == 'fadeout-slow') {
							$(value.selector).fadeOut("slow");
						}
						//fadeout-slow-remove
						if (value.action == 'fadeout-slow-remove') {
							$(value.selector).fadeOut("slow");
							$(value.selector).remove();
						}
						//fadein
						if (value.action == 'fadein') {
							$(value.selector).fadeIn();
						}
						//fadein-slow
						if (value.action == 'fadein-slow') {
							$(value.selector).fadeIn("slow");
						}
						//close modal window
						if (value.action == 'close-modal') {
							$(value.selector).modal('hide');
						}
						//disable
						if (value.action == 'disable') {
							$(value.selector).prop("disabled", true);
						}
						//disable
						if (value.action == 'disable') {
							$(value.selector).prop("disabled", true);
						}
						//mode to end of section (used mostly for kanban, loadmore button
						if (value.action == 'move-to-end') {
							$(value.selector).prop("disabled", true);
						}
					}
				}
			});
		}
		return;
	};

	/**-----------------------------------------------------------------------------------------------------------------------------------
	 * [UPDATE - DOM STATE] (if applicable)
	 * enable or disable buttons, fields and links
	 *
	 * [EXAMPLE DATA SENT]
	 *    ['dom_visibility'][0]['selector'] = '#submit-button '       : valid dom selector '.some_class' | '#some_id' | '[input-type=""]'
	 *    ['dom_visibility'][0]['action'] = 'enabled'                 : enabled|disabled
	 *
	 *------------------------------------------------------------------------------------------------------------------------------------*/
	NXAJAX.updateDomState = function () {

		//get the payload
		var payload = NXAJAX.payload.dom_state;

		//debug
		NXAJAX.log('[ajax] js_toggle_editor_button() - updating dom if applicable - [payload]:', payload);

		//update the DOM (id|class|literal)
		if (payload != undefined && typeof payload == 'object') {
			//loop through the payload and update the dom
			$.each(payload, function (index, value) {
				//sanity check - make sure its an object
				if (typeof value == 'object') {
					if (value.selector != undefined && value.action != undefined) {

						//enable elements
						if (value.action == 'enabled') {
							//buttons
							if ($(value.selector).is('button') || $(value.selector).is('input')) {
								$(value.selector).prop("disabled", false);
							}
							//links
							if ($(value.selector).is('a')) {
								$(value.selector).attr('disabled', false);
							}
						}

						//disable elements
						if (value.action == 'disabled') {
							//buttons
							if ($(value.selector).is('button') || $(value.selector).is('input')) {
								$(value.selector).prop("disabled", true);
							}
							//links
							if ($(value.selector).is('a')) {
								$(value.selector).attr('disabled', true);
							}
						}
					}
				}
			});
		}
		return;
	};

	/**-----------------------------------------------------------------------------------------------------------------------------------
	 * [UPDATE - DOM VAL]
	 * update the value or form fields. Value can also be sent as blank, for resets
	 *
	 * [JQUERY]
	 *   $(foo).val(bar)
	 *
	 * [EXAMPLE DATA SENT]
	 *    ['dom_val'][0]['selector'] = '#submit-button '       : valid dom selector '.some_class' | '#some_id' | '[input-type=""]'
	 *    ['dom_val'][0]['value'] = 'bar'                 : string|null
	 *
	 *------------------------------------------------------------------------------------------------------------------------------------*/

	NXAJAX.updateDomVAL = function (obj) {

		//get the payload
		var payload = NXAJAX.payload.dom_val;

		//debug
		NXAJAX.log('[ajax] updateDomVAL() - resetting form field values - [payload]:', obj);

		//update the DOM (id|class)
		if (payload != undefined && typeof payload == 'object') {
			//loop through the payload and update the dom
			$.each(payload, function (index, value) {
				//sanity check - make sure its an object
				if (typeof value == 'object') {
					if (value.selector != undefined && value.value != undefined) {
						//check boxes
						if ($(value.selector).is('checkbox') || $(value.selector).is('radio')) {
							if (value.value == 'checked') {
								$(value.selector).removeAttr('checked');
							} else {
								$(value.selector).addAttr('checked');
							}
						} else {
							//all other input fields
							if ($(value.selector).is('textarea')) {
								//basic reset
								$(value.selector).val(value.value);
								//incase its a ckeditor textarea
								try {
									var element_name = value.selector;
									element_name = element_name.replace('.', '');
									element_name = element_name.replace('#', '');
									CKEDITOR.instances[element_name].updateElement();
									CKEDITOR.instances[element_name].setData(value.value);
								} catch (err) {
									//do nothing
								}
							} else {
								$(value.selector).val(value.value);
							}
						}
					}
				}
			});
		}
		return;
	};


	/**-----------------------------------------------------------------------------------------------------------------------------------
	 * [MOVE DOM ELEMENTS]
	 *  move a dom element from one location, to inside a new a parent element
	 *
	 * [EXAMPLE DATA SENT]
	 *    ['dom_move_element'][0]['element'] = '#somediv'       : valid dom selector '.some_class' | '#some_id''
	 *    ['dom_move_element'][0]['newparent'] = 'bar'          : valid dom selector '.some_class' | '#some_id''
	 *    ['dom_move_element'][0]['method'] = 'bar'             : prepend-to|append-to|replace|replace-with
	 *    ['dom_move_element'][0]['visibility'] = 'bar'         : show|hide|null
	 *------------------------------------------------------------------------------------------------------------------------------------*/

	NXAJAX.updateMoveElement = function (obj) {

		//get the payload
		var payload = NXAJAX.payload.dom_move_element;

		//debug
		NXAJAX.log('[ajax] updateMoveElement() - moving dom elements to a new location - [payload]:', obj);

		//update the DOM (id|class)
		if (payload != undefined && typeof payload == 'object') {
			//loop through the payload and update the dom
			$.each(payload, function (index, value) {
				//sanity check - make sure its an object
				if (typeof value == 'object') {
					if (value.element != undefined && value.newparent != undefined && value.method != undefined) {

						//prepend to
						if (value.method == 'prepend-to') {
							$(value.element).prependTo(value.newparent);
						}

						//append to
						if (value.method == 'append-to') {
							$(value.element).prependTo(value.newparent);
						}

						//replace
						if (value.method == 'replace') {
							$(value.newparent).html('');
							$(value.element).prependTo(value.newparent);
						}

						//replace
						if (value.method == 'replace-with') {
							$(value.newparent).replaceWith($(value.element));
						}

						//end visibility
						if (value.visibility != undefined) {
							if (value.visibility == 'show') {
								$(value.element).show();
							}
							if (value.visibility == 'hide') {
								$(value.element).hide();
							}
						}
					}
				}
			});
		}
		return;
	};


	/**-----------------------------------------------------------------------------------------------------------------------------------
	 * [RESET TINYMCE EDITORS]
	 * update the value or form fields. Value can also be sent as blank, for resets
	 *
	 * [JQUERY]
	 *   $(foo).val(bar)
	 *
	 * [EXAMPLE DATA SENT]
	 * 	NXAJAX.payload.tinymce_reset = obj.tinymce_reset;
	 *
	 *------------------------------------------------------------------------------------------------------------------------------------*/

	NXAJAX.tinyMCEReset = function () {

		//get the payload
		var payload = NXAJAX.payload.tinymce_reset;

		//debug
		NXAJAX.log('[ajax] resetTinyMCE() - resetting tinymce editors - [payload]:', payload);

		//update the DOM (id|class)
		if (payload != undefined && typeof payload == 'object') {
			//loop through the payload and update the dom
			$.each(payload, function (index, value) {
				//sanity check - make sure its an object
				if (typeof value == 'object') {
					if (value.selector != undefined) {
						//reset editor (only is it exixts)
						if ($("#" + value.selector).length) {
							tinymce.get(value.selector).setContent('');
						}
					}
				}
			});
		}
		return;
	};


	/**-----------------------------------------------------------------------------------------------------------------------------------
	 * [RESET TINYMCE EDITORS]
	 * set new data to tinymce
	 *------------------------------------------------------------------------------------------------------------------------------------*/

	NXAJAX.tinyMCENewData = function () {

		//get the payload
		var payload = NXAJAX.payload.tinymce_new_data;

		//debug
		NXAJAX.log('[ajax] tinyMCENewData() -setting new data for tinymce editors - [payload]:', payload);

		//update the DOM (id|class)
		if (payload != undefined && typeof payload == 'object') {
			//loop through the payload and update the dom
			$.each(payload, function (index, value) {
				//sanity check - make sure its an object
				if (typeof value == 'object') {
					if (value.selector != undefined && value.value != undefined) {
						//reset editor (only is it exixts)
						if ($("#" + value.selector).length) {
							//set html content
							$("#" + value.selector).val(value.value);
							//set tinymce content
							tinymce.get(value.selector).setContent(value.value);
						}
					}
				}
			});
		}
		return;
	};

	/**-----------------------------------------------------------------------------------------------------------------------------------
	 * [UPDATE - NOTIFICATION] (if applicable)
	 *
	 * [JQUERY]
	 *
	 *
	 * [EXAMPLE DATA SENT]
	 *    ['notification']['type'] = 'error'              : error|success
	 *    ['notification']['value'] = 'request error'       : message
	 *
	 *------------------------------------------------------------------------------------------------------------------------------------*/

	NXAJAX.notification = function () {

		//get the payload
		var payload = NXAJAX.payload.notification;

		//are we allowed to show notifications with this request
		if (!NXAJAX.data.show_notification) {
			if (payload != undefined && (payload.type != undefined && (payload.type == 'force-error' || payload.type == 'force-success'))) {
				//do nothing. this is a forced error. 
			} else {
				//exit - do not show notifications
				return;
			}
		}

		//to send to notification method
		var obj = {};

		//debug
		NXAJAX.log('[ajax] notification() - displaying notification if applicable - [payload]:', payload);

		//validation
		if (payload != undefined && typeof payload == 'object') {
			//loop through the payload and update the dom
			if (payload.type != undefined && payload.value != undefined) {
				//error & waring (same thing)
				if (payload.type == 'error' || payload.type == 'warning' || payload.type == 'force-error') {
					obj['type'] = 'error';
					obj['message'] = payload.value;
					NX.notification(obj);
				} else {
					//all others
					obj['type'] = payload.type;
					obj['message'] = payload.value;
					NX.notification(obj);
				}
			}
		}
		return;
	};


	/**-----------------------------------------------------------------------------------------------------------------------------------
	 * [UPDATE - BROWSER URL]
	 *
	 *
	 * [EXAMPLE DATA SENT]
	 *    ['dom_browser_url']['title']
	 *    ['dom_browser_url']['url'] 
	 *
	 *------------------------------------------------------------------------------------------------------------------------------------*/
	NXAJAX.updateDomBrowserUrl = function () {

		//get the payload
		var payload = NXAJAX.payload.dom_browser_url;

		//debug
		NXAJAX.log('[ajax] updateDomBrowserUrl() - updating browser url if applicable - [payload]:', payload);

		//update the DOM (id|class|literal)
		if (payload != undefined && typeof payload == 'object') {

			if (payload.title != undefined && payload.url != undefined) {
				//update browser url and history
				history.pushState({}, payload.title, payload.url);
			}
		}
		return;
	};



	/**-----------------------------------------------------------------------------------------------------------------------------------
	 * [DOM ACTIONS]
	 *
	 * Various other dom actions, such as trigger click etc
	 *
	 *------------------------------------------------------------------------------------------------------------------------------------*/
	NXAJAX.updateDomAction = function () {

		//get the payload
		var payload = NXAJAX.payload.dom_action;

		//debug
		NXAJAX.log('[ajax] updateDomAction() - doing an action on a dom element - [payload]:', payload);

		//update the DOM (id|class)
		if (payload != undefined && typeof payload == 'object') {
			//loop through the payload and update the dom
			$.each(payload, function (index, value) {
				//sanity check - make sure its an object
				if (typeof value == 'object') {
					if (value.selector != undefined && value.action != undefined && value.value != undefined) {

						//trigger [e.g. $(element).trigger('click')];
						if (value.action == 'trigger') {
							$(value.selector).trigger(value.value);
						}

						//trigger select change
						if (value.action == 'trigger-select-change') {
							$(value.selector).val(value.value).trigger('change');
						}

					}
				}
			});
		}
		return;
	};


	/**--------------------------------------------------------------CUSTOM FUNCTIONS HERE ---------------------------------------------- */

	/**-----------------------------------------------------------------------
	 * [REINITIALIZE JS FUNCTIONALITY FOR AJAX LOADED DOM]
	 * Some javascript functions are not available for content added to the dom
	 * using ajax. The solution is to just reinitialize the function for the
	 * whole page.
	 *
	 * [CHECKING IF FUNCTION EXISTS]
	 * Its good to check if function exists to avoid undefined errors
	 *     - jquery /jquery plugin functions
	 *              if ($.fn.someFunction) {
	 *
	 * 	   - plain js functions
	 *              if ( typeof someFunction === "function") {
	 *
	 *------------------------------------------------------------------------*/
	NXAJAX.reinitialiseDom = function () {

		//debug
		NXAJAX.log("[ajax] reinitialise_dom() - re-initialising some javascript functions to account for new dom content");

		/** -------------------------------------------
		 * bootstrap
		 * ------------------------------------------*/
		NXbootstrap();

		/** -------------------------------------------
		 * tooltipe (incase we changed text)
		 * ------------------------------------------*/
		$('[data-toggle="tooltip"]').tooltip('hide');
	};


	//get event data
	NXAJAX.eventData(obj);

	//get post data
	NXAJAX.processPostData(obj);

	var nxTime0 = performance.now();

	//state
	var state = true;

	//-----kill any similar ajax request thats already running (optional)----
	if (typeof ajax_request !== undefined && ajax_request && ajax_request.readyState !== 4) {
		ajax_request.abort();
	}

	//validate input data
	if (!NXAJAX.validateRequired()) {
		return;
	}

	/**------------------------------------------------------------------------
	 * [AJAX REQUEST]
	 * send request to the backend and get the results
	 *------------------------------------------------------------------------*/
	NXAJAX.log('[ajax] ajaxRequest() - starting ajax request - [url]: ' + NXAJAX.data.url + ' [post payload]:', NXAJAX.post);


	//Laravel DELETE & PUT Fix
	if (NXAJAX.data.ajax_type == 'PUT') {
		NXAJAX.data.ajax_type = 'POST';
		NXAJAX.post['_method'] = 'PUT';
	}
	if (NXAJAX.data.ajax_type == 'DELETE') {
		NXAJAX.data.ajax_type = 'POST';
		NXAJAX.post['_method'] = 'DELETE';
	}

	//start ajax request
	var ajax_request = $.ajax({
		headers: {
			'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
		},
		type: NXAJAX.data.ajax_type,
		url: NXAJAX.data.url,
		dataType: 'json',
		data: NXAJAX.post,

		/** --------------------------------------------------------------------*
		 * About to start ajax request
		 * Show any ajax loading annimation etc
		 *----------------------------------------------------------------------*/
		beforeSend: function (xhr) {
			//progress bar start
			if (NXAJAX.data.progress_bar == 'show') {
				NProgress.set(0.7);
			}
			//loading or overlay annimation
			NXAJAX.loadingAnimation('show');

			//annimate clicked button
			NXAJAX.annimateButton();

			//disable clicked button
			NXAJAX.disableButton();

			//disable submit button if applicable
			if (obj.attr('data-on-start-submit-button') == 'disable') {
				obj.prop('disabled', true);
			}
		},

		/**------------------------------------------------------------------------
		 * request was successful (header status:200)
		 *  - This is a list of all the various items we can now do.
		 *  - Validation and applicability will be done directly by each function
		 *------------------------------------------------------------------------*/
		success: function (data) {

			//start timer to monitor perfomenace
			var t1 = performance.now();

			NXAJAX.log('[ajax] ajaxRequest() - success - we have a response from the server ' + data);

			//process the payload
			NXAJAX.getPayload(data);

			//was there a redirect request
			NXAJAX.updateRedirect();

			///sto here if we are doing a redirect
			if (typeof NXAJAX.payload.redirect_url != 'undefined' && NXAJAX.payload.redirect_url != '') {
				return;
			}


			//dom attributes - before view render
			NXAJAX.updateDomAttributes();

			//dom css - before view render
			NXAJAX.updateDomCSS();

			//update class e.g. $(foo).addClass(bar)
			NXAJAX.updateDomClasses();

			//dom chained effects
			NXAJAX.updateDomChainedEffects();

			//update HTML
			NXAJAX.updateDomHTML();

			//update VAL
			NXAJAX.updateDomVAL();

			//update PROP
			NXAJAX.updateDomProperty()

			//move dom elements
			NXAJAX.updateMoveElement();

			//update visibility
			NXAJAX.updateDomVisibility();


			//update HTML
			NXAJAX.updateDomHTML('end');


			//update STATE e.g. $(foo)..prop("disabled", true);
			NXAJAX.updateDomState();

			//dom action = e,g trigger click
			NXAJAX.updateDomAction();

			//show any notifications
			NXAJAX.notification();

			//was there a delayed redirect request
			NXAJAX.updateDelayedRedirect();

			//reinitialise js function for new dom content (cam skip this if it causing a ux issue) : $jsondata['skip_dom_reset'] = true;
			if (typeof data.skip_dom_reset == 'undefined') {
				NXAJAX.reinitialiseDom();
			}

			//reinitialise js function for new dom content (cam skip this if it causing a ux issue) : $jsondata['skip_dom_tinymce'] = true;
			if (typeof data.skip_dom_tinymce == 'undefined') {
				nxTinyMCEPlain();
				nxTinyMCEBasic();
				nxTinyMCELite();
				nxTinyMCEExtended();
				nxTinyMCEExtendedLite();
			}

			/** progress bar and annimation - finished*/
			if (NXAJAX.data.progress_bar == 'show') {
				NProgress.done();
			}

			//enable submit button if applicable
			if (obj.attr('data-on-start-submit-button') == 'disable') {
				obj.prop('disabled', false);
			}

			//reset list pages - actions check boxes
			if (NXAJAX.data.skip_checkboxes_reset || typeof data.skip_checkboxes_reset != 'undefined') {
				//do nothing
			} else {
				if (typeof NX.listCheckboxesReset !== 'undefined') {
					NX.listCheckboxesReset();
				}
			}

			//custom post run functions
			$.each(NXAJAX.data.postrun_functions, function (index, val) {
				//i function exists, run it
				if (typeof window[val] === "function") {
					window[val]();
				}
			});

			//post run functions
			NXAJAX.postRunFunctions();

			///rerun some functions (needed for items in modals, where the html replace happens after)
			if (typeof data.rerun != 'undefined' && data.rerun != '') {
				if (data.rerun == 'updateDomAttributes') {
					NXAJAX.log('[ajax] re-executing updateDomAttributes()');
					NXAJAX.updateDomAttributes();
				}
			}

			//reset tinymce editors
			NXAJAX.tinyMCEReset();

			//set new data for tinymce editor
			NXAJAX.tinyMCENewData();

			//update browser url if applicable
			NXAJAX.updateDomBrowserUrl();

			//loading or overlay annimation
			NXAJAX.loadingAnimation('hide');

			//remove button annimation
			NXAJAX.resetAnnimateButton();

			//enable disabled button
			NXAJAX.resetDisableButton();


			//perfomance end
			var tx = performance.now();
			NXAJAX.log('[ajax] Complete request completed in ' + (tx - nxTime0) + 'milliseconds');
			NXAJAX.log('[ajax] Frontend part of the request completed in ' + (tx - t1) + 'milliseconds');

		},

		/**------------------------------------------------------------------------
		 * request was NOT successful (header status:400)
		 *------------------------------------------------------------------------*/
		error: function (data, jqXHR) {

			NXAJAX.log('[ajax] ajaxRequest() - error - we have an error from the server - payload:');

			//has the laravel session timedout?
			var $session_timeout = false;

			//laravel 401 timeout status
			if (typeof data.status != 'undefined' && data.status == 401) {
				NXAJAX.log('[ajax] ajaxRequest() - server error - session timeout');
				$session_timeout = true;
			}

			//laravel - token mismatch response
			if (typeof data.responseJSON != 'undefined' && typeof data.responseJSON.message != 'undefined' && (data.responseJSON.message == 'CSRF token mismatch.' || data.responseJSON.message == 'Unauthenticated.')) {
				NXAJAX.log('[ajax] ajaxRequest() - server error - session timeout');
				$session_timeout = true;
			}


			//Laaravel session authetication error - show login modal
			if ($session_timeout) {

				//close all modals & show login modal
				if (NX.session_login_popup == 'enabled') {
					//session timeout login popup login modal
					//$('.modal').modal('hide');
					//$("#reloginModal").modal("show");
				}

				//end annimations
				NXAJAX.loadingAnimation('hide');
				NProgress.done();

				//finish
				return;
			}


			//show any other error message
			if (typeof data.responseJSON != 'undefined') {
				//payload
				NXAJAX.getPayload(data.responseJSON);
				//show any notifications
				NXAJAX.notification();
			}


			/** progress bar and annimation - finished*/
			if (NXAJAX.data.progress_bar == 'show') {
				NProgress.done();
			}

			//enable submit button if applicable
			if (obj.attr('data-on-start-submit-button') == 'disable') {
				obj.prop('disabled', false);
			}

			//loading or overlay annimation
			NXAJAX.loadingAnimation('hide');

			//remove button annimation
			NXAJAX.resetAnnimateButton();

			//enable disabled button
			NXAJAX.resetDisableButton();

		}
	});
};

$(document).ready(function () {

	/**-----------------------------------------------------------------------
	 *  [NEW AJAX RECORD REQUEST]
	 *  [event] -  clicked on button or link etc
	 *  [action] - validate required data
	 *           - send request to ajax
	 *------------------------------------------------------------------------*/
	$(document).on('click', '.js-ajax-ux-request, .ajax-request, .js-ajax-request', function (e) {
		e.preventDefault();
		//call the function to process request
		nxAjaxUxRequest($(this));
	});

	/**-----------------------------------------------------------------------
	 *  [NEW AJAX RECORD REQUEST]
	 *  same as above but allows default object action
	 *  good when used with elements like checkboxes
	 *------------------------------------------------------------------------*/
	$(document).on('click', '.js-ajax-ux-request-default', function (e) {
		//call the function to process request
		nxAjaxUxRequest($(this));
	});
});