$(document).ready(function(){

	/**
	 * Answers & Marks Report
	 */
	if($('.reports-answers_marks').length) {
		$('#options_tenders').change(function(){
			// get parent input div
			var _parent = $(this).parent();

			// show loading image
			_parent.find('.loading_image').show();
			
			$.ajax({
				type:"POST",
				url:"/reports/ajax_get_tender_companies",
				data:"ajax=1&tender_id="+$(this).val(),
				dataType:"json",
				success:function(msg) {
					// hide loading image
					_parent.find('.loading_image').hide();
					if(msg.success) {
						$('#options_companies').html(msg.html);
					}
				}
			});
		});
	}

	/**
	 * CONFIRM UNSUBMIT
	 */
	$('.unsubmit_confirm').click(function(){
		var answer = confirm('Unsubmit this Bid?');
	return answer;
	});

	/**
	 * MATRIX QUESTIONS
	 */

	// show the add icons with JS
	$('.add_row:first').show();
	$('.add_col:first').show();
	$('.delete_row').show();
	$('.delete_col').show();

	// add multiple rows & columns
	$('.add_row').click(function(){
		var html = $(this).parents('.input').html();
		$('.add_row :last').parents('.input').after("<div class='input'>"+html+"</div>");
		$('.add_row :last').parents('.input').find(':input').val('');
		$('.add_row :last').hide();
	return false;
	});
	/*
	$('.add_col').click(function(){
		var html = $(this).parents('.input').html();
		$('.add_col :last').parents('.input').after("<div class='input'>"+html+"</div>");
		$('.add_col :last').parents('.input').find(':input').val('');
		$('.add_col :last').hide();
	return false;
	});
	*/

	function column() {

		var column = this;

		if(typeof column.number_of_cols == "undefined") {
			column.number_of_cols = 1;
		}

		this.add_col = function() {

			if(column.number_of_cols >= 6) {
				return false;	
			}

			var html = "<div class='input'>" + $(this).parents('.input').html() + "</div>";

			$('.add_col :last').parents('.input').after(html);
			
			$('.add_col :last').parents('.input').find(':input').val('');
			$('.add_col :last').hide();
			
			column.number_of_cols++;

			if(column.number_of_cols >= 6) {
				this.style.display = "none";
				return false;
			}

			return false;
		}

		this.remove_col = function() {
			var input = $(this).parents('.input');
			input.fadeOut("normal",function(){
				input.remove();
			});
			column.number_of_cols--;
			return false;
		}
	}

	var col = new column();

	$('.add_col').click(col.add_col);
	$('.delete_col').click(col.add_col);

	// delete rows/cols
	$('.delete_row').click(function(){
		var input = $(this).parents('.input');
		input.fadeOut("normal",function(){
		input.remove();
		});
	return false;
	});

	/*
	$('.delete_col').click(function(){
		var input = $(this).parents('.input');
		input.fadeOut("normal",function(){
		input.remove();
		});
	return false;
	});
	*/


	/**
	 * BID FILTERS
	 */
	//$('#bid_filters_div').hide();
	//$('.show_bid_filters_div').show();
	//$('.toggle_bid_filters_fieldset').click(function(){
	//$('#bid_filters_div').slideToggle();
	//	return false;
	//});

	/**
	 * Assign Markers NEW
	 */
	if($('.js_show').length) {
		$('.js_show').show();
	}

	// show/hide the questions if the section link was clicked
	if( $('.toggle_questions').length ) {
		$('.question').hide();
		$('.toggle_questions').click(function(){
			var parent_id = $(this).parents('.section').attr('id');
			var text = $(this).html();

			// find and toggle questions
			//$('.'+parent_id+'_q').toggle();

			if(text=='Show Questions') {
				$('.'+parent_id+'_q').show();
				$(this).html('Hide Questions');
			} else {
				$(this).html('Show Questions');
				$('.'+parent_id+'_q').hide();
			}
		return false;
		});
	}


	/**
	 * Marker's Instructions Report
	 */
	$('#reports-markers_instructions #tenders').change(function(){
		var tender_id = $(this).val();
		$('#ajax_loader').show();
		$.ajax({
			type:"POST",
			url:"/reports/ajax_get_tender_bids",
			data:"ajax=1&tender_id="+tender_id,
			dataType:"json",
			success:function(response){
				$('#ajax_loader').hide();
				if(response.success) {
					$('#reports-markers_instructions #sections').html( response.sections );
				}
			}
		});
	});


	/**
	 * Marker's Notes Report
	 */
	$('#reports-markers_notes #tenders').change(function(){
		var tender_id = $(this).val();
		$('#ajax_loader').show();
		$.ajax({
			type:"POST",
			url:"/reports/ajax_get_tender_bids",
			data:"ajax=1&tender_id="+tender_id,
			dataType:"json",
			success:function(response){
				$('#ajax_loader').hide();
				if(response.success) {
					$('#reports-markers_notes #sections').html( response.sections );
				}
			}
		});
	});

	
	/**
	 * Company Answers Report
	 */
	$('#reports_company_answers #tenders').change(function(){
		var tender_id = $(this).val();
		$('#ajax_loader').show();
		$.ajax({
			type:"POST",
			url:"/reports/ajax_get_tender_bids",
			data:"ajax=1&tender_id="+tender_id,
			dataType:"json",
			success:function(response){
				$('#ajax_loader').hide();
				if(response.success) {
					$('#reports_company_answers #bids').html( response.bids );
					$('#reports_company_answers #sections').html( response.sections );
				}
			}
		});
	});
	
	
	/**
	 * Question Answers Report
	 */
	$('#reports_question_answers #tenders').change(function(){
		var tender_id = $(this).val();
		$('#ajax_loader').show();
		$.ajax({
			type:"POST",
			url:"/reports/ajax_get_tender_sections",
			data:"ajax=1&tender_id="+tender_id,
			dataType:"json",
			success:function(response){
				$('#ajax_loader').hide();
				if(response.success) {
					$('#reports_question_answers #sections').html( response.sections );
				}
			}
		});
	});
	$('#reports_question_answers #sections').change(function(){
		var section_id = $(this).val();
		$('#ajax_loader').show();
		$.ajax({
			type:"POST",
			url:"/reports/ajax_get_section_questions",
			data:"ajax=1&section_id="+section_id,
			dataType:"json",
			success:function(response){
				$('#ajax_loader').hide();
				if(response.success) {
					$('#reports_question_answers #questions').html( response.questions );
				}
			}
		});
	});
	
	
	/**
	 * Register Form
	 */ 
	if($('.disable_paste').length) {
		// disable right click
		$(document).bind("contextmenu",function(e){
			return false;
		});
		// disable copy & paste
		$('.disable_paste').keyup(function(e){
			if(e.which == 86 && e.ctrlKey) {
				$(this).find(':input').val('');
				alert('Pasting has been disabled for this field, please type your text manually');
			}
		});
	}

	// hide password field
	$('.register_form #password').parent().hide();
	// get selected value
	var selection = $('.register_form .registered').val();
	
	// show/hide
	if(selection == 'yes') {
		$('.register_form .reg_no').hide();
		$('.register_form #password').parent().show();
	} else {
		$('.register_form #password').parent().hide();
		$('.register_form .reg_no').show();
	}

	$('.register_form .registered').change(function(){
		// get selected value
		var selected = $(this).val();

		// show/hide
		if(selected == 'yes') {
			$('.register_form .reg_no').hide();
			$('.register_form #password').parent().fadeIn();
		} else {
			$('.register_form .reg_no').fadeIn();
			$('.register_form #password').parent().hide();
		}
	});

	// add a confirm box to all delete links
	$('.confirm_delete').click(function(){
		var answer = confirm('Delete this Item?');
		return answer;
	});

	// loop through all tables on the page
	$('table.stripe').each(function() {
		// stripe table rows
		$(this).find('tbody tr:even').addClass('odd');

		// add hover functionality
		$(this).find('tbody tr').mouseover(function(){
			$(this).addClass('hover').mouseout(function(){$(this).removeClass('hover')});
		});
	});

	// loop through all tables on the page
	$('table.row_hover').each(function() {
		// add hover functionality
		$(this).find('tbody tr').mouseover(function(){
			$(this).addClass('hover').mouseout(function(){$(this).removeClass('hover')});
		});
	});

	// init search variable
	var search_value = '';

	// removes the standard text from a search bar when a user click inside
	$('table .search').click(function() {
		// save value
		search_value = this.value;
		// remove text
		this.value = '';
	});
	$('table .search').blur(function() {
		// if text is empty
		if(this.value == '') {
			// eneter generic message
			this.value = search_value;
		}
	});

	// focus the first field of forms
	//$('#form_style input[type=text]:first').focus();

	/**
	 * Tool Tips
	 * This deals with all the tool tips on the page
	 */
	assign_tooltips();

	/**
	 * Add Tender Files
	 * Adds another file input to the page
	 */
	$('.add_form_tender .file a').click(function(){
		// form input html
		var inputHTML = "<div class='input file'><label>Tender Document:</label><input type='file' name='files[]' /></div>";
		// add html
		$('.add_form_tender .file:last').after(inputHTML);
	return false;
	});
	// edit tender page
	$('.edit_form_tender .addfile').click(function(){
		// form input html
		var inputHTML = "<div class='input file'><label>Tender Document:</label><input type='file' name='files[]' /></div>";
		// add html
		$('.edit_form_tender .files').append(inputHTML);
	return false;
	});
	$('.edit_form_tender a.add_completed_file').click(function(){
		// form input html
		var inputHTML = "<div class='input file completed_file'><label>Completed Tender Document:</label><input type='file' name='completed[]' /></div>";
		// add html
		$('.edit_form_tender .after_files').append(inputHTML);
	return false;
	});
	

	/**
	 * Add Question Page
	 * This deals with the multiple select options
	 */

	// get the selected type
	var selected_type = $('.form_question #type option:selected').val();

	// hide options if not needed
	if(selected_type != 'Multiple' && selected_type != 'Multiples' && selected_type != 'MultiplesLimit') {
		$('.form_question .option').hide();
		$('.form_question .options_link').hide();
	}

	// Multiples Question Type
	if(selected_type == 'MultiplesLimit') {
		$('.answer_limit').show();
	} else {
		$('.answer_limit').hide();
	}

	// Matrix Question type
	if(selected_type == 'Matrix') {
		$('.matrix_options').show();
	}

	// when the form value is changed
	$('.form_question #type').change(function(){
		// get selected value
		var selected = $(this).val();

		// fadein/fadout question options and scores
		if(selected == 'Multiple' || selected == 'Multiples' || selected == 'MultiplesLimit') {
			$('.form_question .option').fadeIn();
			$('.form_question .options_link').fadeIn();
		} else {
			$('.form_question .option').fadeOut();
			$('.form_question .options_link').fadeOut();
			$('.answer_limit').fadeOut();
		}

		if(selected != 'Matrix') {
			$('.matrix_options').fadeOut();
		}

		// check multiple options
		if(selected == 'Multiple') {
			$('.answer_limit').fadeOut();
		}
		else if(selected == 'Multiples') {
			$('.answer_limit').fadeOut();
		}
		else if(selected == 'MultiplesLimit') {
			$('.answer_limit').fadeIn();
		}
		else if(selected == 'Matrix') {
			$('.matrix_options').fadeIn();
		}
	});

	// add form html for another question option & score field
	$('.form_question .options_link').click(function() {
		var optionHTML = '<div class="input option"><label>Options:</label>';
		optionHTML += '<input type="text" name="q_options[]" class="option_input" title="The Option for your multiple choice question (Required)"/> <strong>Default Score:</strong> ';
		optionHTML += '<input type="text" name="q_scores[]" class="score_input" title="The default Score for your Option (Optional)"/></div>';
		// add html
		$('.form_question .option:last').after(optionHTML);
	return false;
	});

	// deletes options from form
	$('.form_question .delete_option').click(function(){
		// fade out the input
		$(this).parent().fadeOut();
		// remove values
		$(this).parent().find('.option_input').val('');
		$(this).parent().find('.score_input').val('');
	return false;
	});

	/**
	 * Add Section Page
	 */

	// add more form elements
	$('.template_question a').click(function(){
		// get html
		var html = $(this).parent().find('select').html();

		// remove selected options
		html = html.replace('selected="selected"', '');

		// build up the input
		html = '<div class="input template_question"><label>Template Question</label><select name="template[]">' + html + '</select></div>';
		// add another element
		$('.template_question:last').after(html);
	return false;
	});


	/**
	 * Deal with drag and drop tables
	 */

	// drag tender sections
	$(".drag_sections").tableDnD({
		onDragClass : "dragclass",
		onDrop: function(table, row) {
			//console.log($.tableDnD.serialize());
			// set an image loader
			$(".drag_sections").after("<div class='ajax-loader'><img src='/images/icons/ajax-loader.gif' alt='Loading Image' /></div>");
			// serialise order
			var data = $.tableDnD.serialize();
			// setup new ajax request
			$.ajax({
				type	: "POST",
				url		: "/tenders/ajax_reorder_sections",
				data	: data + '&ajax[]=1',
				success	: function(msg) {
					//console.log( "Data Saved: " + msg );
					// display message
					$(".drag_sections").after('<div class="ajax_message">'+msg+'</div>');
					// fadeout message
					$('.ajax_message').fadeOut(4000);
					// hide ajax image loader
					$('.ajax-loader').hide();
			   }
			});
        }
	});
	
	// drag tender questions
	$(".drag_questions").tableDnD({
		onDragClass : "dragclass",
		onDrop: function(table, row) {
			//console.log($.tableDnD.serialize());
			// set an image loader
			$(".drag_questions").after("<div class='ajax-loader'><img src='/images/icons/ajax-loader.gif' alt='Loading Image' /></div>");
			// serialise order
			var data = $.tableDnD.serialize();
			// setup new ajax request
			$.ajax({
				type	: "POST",
				url		: "/tenders/ajax_reorder_questions",
				data	: data + '&ajax[]=1',
				success	: function(msg) {
					//console.log( "Data Saved: " + msg );
					// display message
					$(".drag_questions").after('<div class="ajax_message">'+msg+'</div>');
					// fadeout message
					$('.ajax_message').fadeOut(4000);
					// hide ajax image loader
					$('.ajax-loader').hide();
			   }
			});
        }
	});

	/**
	 * Tender Answer Page
	 * uses ajax to save tender answers when the user loses focus from a field
	 */

	$('.question .fileAdd').click(function(){
		// get name
		var name = $(this).parent().find('.file').attr('name');
		// build html
		var html = "<br/><br/><input type='file' name='"+name+"' class='file' />";
		// add to page
		$(this).after(html);

		//alert( $(this).parent().find('.file').attr('name') );
	return false;
	});
	
	// init variables
	var action		= $('.tenders-answer form').attr('action');
	var user_id		= $('.tenders-answer #user_id').attr('value');
	var count		= 0;
	if(action) {
		var tender_id 	= parse_form_url(action);
	}

	//console.log( 'action: ' + action );//console.log( 'projid: ' + tender_id );//console.log( 'userid: ' + user_id );

	// loop through all the form data
	//$('.ajax-active form :input').each(function(){
		// get input type
	//	var type = this.type;
	//});

	// on form change
	$('.ajax-active form :input').change(function(){
		// get type of form input
		var type 	= this.type;
		// get input name
		var name 	= parse_input_name( this.name );
		// get name
		var g_name	= this;

		//console.log(this.type);//console.log(this.value);//console.log(name);//console.log($(this).serialize());

		// ignore file types
		if(type != 'file') {
			// send ajax request to controller action
			$.ajax({
				type	: "POST",
				url		: "/tenders/ajax_save_answers",
				data	: $(this).serialize() + '&ajax[]=1&tender_id='+tender_id+'&user_id='+user_id,
				success	: function(msg) {
					var d = new Date();
					var curr_hour = d.getHours();

					if (curr_hour < 12) {a_p = "AM";}
					else {a_p = "PM";}
					if (curr_hour == 0) {curr_hour = 12;}
					if (curr_hour > 12) {curr_hour = curr_hour - 12;}

					var curr_min = d.getMinutes();
					curr_min = curr_min + "";
					if (curr_min.length == 1) {curr_min = "0" + curr_min;}
					var time = curr_hour + ":" + curr_min + " " + a_p;

					// if save success
					if(msg) {
						// remove red error class
						$(g_name).parent().removeClass('error');
						// set message
						$(g_name).after('<div class="ajax_message"><p class="valid">Your Answer was automatically saved at '+time+'</p></div>');
						// fade message
						$('.ajax_message').fadeOut(8000);
					} else {
						// set message
						$(g_name).after('<div class="ajax_message"><p class="invalid">There was an error automatically saving your answer, please submit by pressing the "Save" button.</p></div>');
						// fade message
						$('.ajax_message').fadeOut(8000);
					}
			   }
			});
		}
	});

	/**
	 * Tenders Summary Page
	 * Assign Markers via Ajax
	 */
/*
	// when the form value is changed
	$('.ajax-marker-dropdown').change(function(){
		// get selected value
		var selected = $(this).val();
		// get parent bid id
		var bid_id = $(this).parent().parent().attr('id');

		// do ajax request
		$.ajax({
			type	: "POST",
			url		: "/bids/ajax_assign_marker",
			data	: "bid_id="+bid_id+"&selected="+selected+"&ajax=1",
			success	: function(msg) {
				// save message
				$('.ajax-message').hide().html(msg).fadeIn();
				console.log( msg );
			}
		});
	});
*/
/*
	$('.form_assign_marker :input').change(function(){
		// send ajax request to controller action
		$.ajax({
			type	: "POST",
			url		: "/bids/assign_marker",
			data	: $(this).serialize() + '&ajax=1&save=save',
			success	: function(msg) {
				console.log(msg);
		   }
		});
	});
*/

	/**
	 * Character Counter for inputs and text areas
	 */
	$('.word_count').each(function(){
		// get current number of characters
		//var length = $(this).val().length;
		//var length = htmlentities($(this).val()).length;
		var length = strlen($(this).val().replace(/[^A-Za-z0-9 ]/gi,''));
		
		//console.log(length);
		//console.log(strlen($(this).val()));
		//console.log(htmlentities($(this).val()).length);
		//console.log(strlen(html_entity_decode($(this).val())));
		//console.log(strlen($(this).val().replace(/[^A-Za-z0-9 ]/gi,'')));
		//console.log($(this).val().replace(/[^A-Za-z0-9 ]/gi,''));
		//pr(strlen(preg_replace("/[^A-Za-z0-9 ]/","",$answer_found)));
		//console.log('');
		
		// update characters
		$(this).parent().find('.counter').html( length + ' characters');
		
		// bind on key up event
		$(this).keyup(function(){
			// get new length
			//var new_length = $(this).val().length;
			//var new_length = htmlentities($(this).val()).length;
			var new_length = strlen($(this).val().replace(/[^A-Za-z0-9 ]/gi,''));
			
			// update
			$(this).parent().find('.counter').html( new_length + ' characters');
		});
	});


	// for ie 6 only
	if($.browser.msie && /6.0/.test(navigator.userAgent)){
		// fix button issue
		buttonfix();
	}


	// toggles email form on tender summary
	$('a.email_registration_link').click(function(){
		$('.email_registration').slideToggle();
	return false;
	});


	// ticks all check boxes on page
	$('a.check_toggle').click(function(){
		if($(this).html() == 'check all') {
			$(this).html('check none');
			$(':checkbox').each(function(){
				$(this).attr('checked','checked');
			});
		} else {
			$(this).html('check all');
			$(':checkbox').each(function(){
				$(this).attr('checked','');
			});
		}
	return false;
	});
	
	
	/**
	 * Add multiple Markers to a section
	 */
	$('.bids-assignmarker .add_multiple_marker').click(function(){
		// get template list
		var list = $('#template_list').clone();
		// remove hide class
		list.removeClass('hide');
		// get name of current select
		var name = $(this).parent().find(':input').attr('name');
		// save name
		list.attr('name', name);
		// add to page
		$(this).parent().append('<br/>');
		$(this).parent().append(list);

	return false;
	});
	
	// show/hide add image
	$('.section_marker_list').change(function(){
		// selected value
		var selected = $(this).val();

		// if selected is not required
		if( selected=='nq' ) {
			$(this).parent().find('.add_multiple_marker').hide();
		} else {
			$(this).parent().find('.add_multiple_marker').show();
		}
	});

	// create another marker dropdown on '/bids/assign_markers_all'
	$('.bids-assign_markers_all .add_multiple_marker').click(function(){
		$(this).parent().find(':input:first').clone().appendTo( $(this).parent() );
		$(this).parent().find(':input:last').css('marginTop','10px').val('');
	});


	/**
	 * Dependent Questions
	 */

	// assign event handler to question
	assign_dependent_question_handler();

	// assign change handler
	$('.questions #required').change(function(){
		// selected value
		var selected = $(this).val();

		// selected is yes
		if( selected=='Dependent' ) {
			// display ajax loader
			$('.ajax_loader').show();
			// get tender section
			var section = $('#section').val();

			// get list of questions via ajax
			$.ajax({
				type	: "POST",
				url		: "/questions/ajax_get_questions",
				data	: "section_id="+section+"&ajax=1",
				success	: function(msg) {
					// populate question list
					$('.dependent_q').html( msg ).show();
					// hide ajax loader
					$('.ajax_loader').hide();
					// add question handler
					assign_dependent_question_handler();
			   }
			});
		} else {
			// hide loader
			$('.ajax_loader').hide();
			$('.dependent_q').hide();
		}
	});
});


/**
 * Assigns a handler to the dependent question and
 */
function assign_dependent_question_handler() {
	// assign change handler
	$('.questions #dependent_q').change(function(){
		var selected = $(this).val();
		// hide all answers
		$('.a').hide().find(':input').attr('name','');
		// show selected answer
		$('.answers_'+selected).show().find(':input').attr('name','dependent_a[]');
	});
}


/**
 * parse_form_url()
 * parses the form url to get the tender id
 */
function parse_form_url(url) {
	var explode = url.split('/');
return explode[3];
}

/**
 * parse_input_name()
 * parses a form input name to get the section and question id
 */
function parse_input_name(name) {
	// explode name
	var explode = name.split('_');
	// remove the '[' if a multiples option
	explode[2] = explode[2].split('[');
return explode;
}

/**
 * assign_tooltips()
 * function to encapsulate all the tool tips methods
 * useful when forms are added via js, this will rebind all the tooltips
 */
function assign_tooltips() {
	$('.excerpt').tooltip({id:"tooltip1",delay:0,track:true});
	$('#form_style input').tooltip({id:"tooltip1",track:true,delay:0});
	$('#form_style textarea').tooltip({id:"tooltip1",track:true,delay:0});
	$('#form_style select').tooltip({id:"tooltip1",delay:0});
	$('.tooltip').tooltip({id:"tooltip1",delay:0});
}

function buttonfix() {
    var buttons = document.getElementsByTagName('button');
    for (var i=0; i<buttons.length; i++) {
        if(buttons[i].onclick) continue;
        
        buttons[i].onclick = function () {
            for(j=0; j<this.form.elements.length; j++)
                if( this.form.elements[j].tagName == 'BUTTON' )
                    this.form.elements[j].disabled = true;
            this.disabled=false;
            this.value = this.attributes.getNamedItem("value").nodeValue ;
        }
    }
}

function strlen (string) {
    // http://kevin.vanzonneveld.net
    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: Sakimori
    // +      input by: Kirk Strobeck
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   bugfixed by: Onno Marsman
    // +    revised by: Brett Zamir (http://brett-zamir.me)
    // %        note 1: May look like overkill, but in order to be truly faithful to handling all Unicode
    // %        note 1: characters and to this function in PHP which does not count the number of bytes
    // %        note 1: but counts the number of characters, something like this is really necessary.
    // *     example 1: strlen('Kevin van Zonneveld');
    // *     returns 1: 19
    // *     example 2: strlen('A\ud87e\udc04Z');
    // *     returns 2: 3
    var str = string + '';
    var i = 0,
        chr = '',
        lgth = 0;

    if (!this.php_js || !this.php_js.ini || !this.php_js.ini['unicode.semantics'] || this.php_js.ini['unicode.semantics'].local_value.toLowerCase() !== 'on') {
        return string.length;
    }

    var getWholeChar = function (str, i) {
        var code = str.charCodeAt(i);
        var next = '',
            prev = '';
        if (0xD800 <= code && code <= 0xDBFF) { // High surrogate (could change last hex to 0xDB7F to treat high private surrogates as single characters)
            if (str.length <= (i + 1)) {
                throw 'High surrogate without following low surrogate';
            }
            next = str.charCodeAt(i + 1);
            if (0xDC00 > next || next > 0xDFFF) {
                throw 'High surrogate without following low surrogate';
            }
            return str.charAt(i) + str.charAt(i + 1);
        } else if (0xDC00 <= code && code <= 0xDFFF) { // Low surrogate
            if (i === 0) {
                throw 'Low surrogate without preceding high surrogate';
            }
            prev = str.charCodeAt(i - 1);
            if (0xD800 > prev || prev > 0xDBFF) { //(could change last hex to 0xDB7F to treat high private surrogates as single characters)
                throw 'Low surrogate without preceding high surrogate';
            }
            return false; // We can pass over low surrogates now as the second component in a pair which we have already processed
        }
        return str.charAt(i);
    };

    for (i = 0, lgth = 0; i < str.length; i++) {
        if ((chr = getWholeChar(str, i)) === false) {
            continue;
        } // Adapt this line at the top of any loop, passing in the whole string and the current iteration and returning a variable to represent the individual character; purpose is to treat the first part of a surrogate pair as the whole character and then ignore the second part
        lgth++;
    }
    return lgth;
}

function htmlentities (string, quote_style) {
    // http://kevin.vanzonneveld.net
    // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: nobbler
    // +    tweaked by: Jack
    // +   bugfixed by: Onno Marsman
    // +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +    bugfixed by: Brett Zamir (http://brett-zamir.me)
    // +      input by: Ratheous
    // -    depends on: get_html_translation_table
    // *     example 1: htmlentities('Kevin & van Zonneveld');
    // *     returns 1: 'Kevin &amp; van Zonneveld'
    // *     example 2: htmlentities("foo'bar","ENT_QUOTES");
    // *     returns 2: 'foo&#039;bar'
    var hash_map = {},
        symbol = '',
        tmp_str = '',
        entity = '';
    tmp_str = string.toString();

    if (false === (hash_map = this.get_html_translation_table('HTML_ENTITIES', quote_style))) {
        return false;
    }
    hash_map["'"] = '&#039;';
    for (symbol in hash_map) {
        entity = hash_map[symbol];
        tmp_str = tmp_str.split(symbol).join(entity);
    }

    return tmp_str;
}

function get_html_translation_table (table, quote_style) {
    // http://kevin.vanzonneveld.net
    // +   original by: Philip Peterson
    // +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   bugfixed by: noname
    // +   bugfixed by: Alex
    // +   bugfixed by: Marco
    // +   bugfixed by: madipta
    // +   improved by: KELAN
    // +   improved by: Brett Zamir (http://brett-zamir.me)
    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
    // +      input by: Frank Forte
    // +   bugfixed by: T.Wild
    // +      input by: Ratheous
    // %          note: It has been decided that we're not going to add global
    // %          note: dependencies to php.js, meaning the constants are not
    // %          note: real constants, but strings instead. Integers are also supported if someone
    // %          note: chooses to create the constants themselves.
    // *     example 1: get_html_translation_table('HTML_SPECIALCHARS');
    // *     returns 1: {'"': '&quot;', '&': '&amp;', '<': '&lt;', '>': '&gt;'}
    var entities = {},
        hash_map = {},
        decimal = 0,
        symbol = '';
    var constMappingTable = {},
        constMappingQuoteStyle = {};
    var useTable = {},
        useQuoteStyle = {};

    // Translate arguments
    constMappingTable[0] = 'HTML_SPECIALCHARS';
    constMappingTable[1] = 'HTML_ENTITIES';
    constMappingQuoteStyle[0] = 'ENT_NOQUOTES';
    constMappingQuoteStyle[2] = 'ENT_COMPAT';
    constMappingQuoteStyle[3] = 'ENT_QUOTES';

    useTable = !isNaN(table) ? constMappingTable[table] : table ? table.toUpperCase() : 'HTML_SPECIALCHARS';
    useQuoteStyle = !isNaN(quote_style) ? constMappingQuoteStyle[quote_style] : quote_style ? quote_style.toUpperCase() : 'ENT_COMPAT';

    if (useTable !== 'HTML_SPECIALCHARS' && useTable !== 'HTML_ENTITIES') {
        throw new Error("Table: " + useTable + ' not supported');
        // return false;
    }

    entities['38'] = '&amp;';
    if (useTable === 'HTML_ENTITIES') {
        entities['160'] = '&nbsp;';
        entities['161'] = '&iexcl;';
        entities['162'] = '&cent;';
        entities['163'] = '&pound;';
        entities['164'] = '&curren;';
        entities['165'] = '&yen;';
        entities['166'] = '&brvbar;';
        entities['167'] = '&sect;';
        entities['168'] = '&uml;';
        entities['169'] = '&copy;';
        entities['170'] = '&ordf;';
        entities['171'] = '&laquo;';
        entities['172'] = '&not;';
        entities['173'] = '&shy;';
        entities['174'] = '&reg;';
        entities['175'] = '&macr;';
        entities['176'] = '&deg;';
        entities['177'] = '&plusmn;';
        entities['178'] = '&sup2;';
        entities['179'] = '&sup3;';
        entities['180'] = '&acute;';
        entities['181'] = '&micro;';
        entities['182'] = '&para;';
        entities['183'] = '&middot;';
        entities['184'] = '&cedil;';
        entities['185'] = '&sup1;';
        entities['186'] = '&ordm;';
        entities['187'] = '&raquo;';
        entities['188'] = '&frac14;';
        entities['189'] = '&frac12;';
        entities['190'] = '&frac34;';
        entities['191'] = '&iquest;';
        entities['192'] = '&Agrave;';
        entities['193'] = '&Aacute;';
        entities['194'] = '&Acirc;';
        entities['195'] = '&Atilde;';
        entities['196'] = '&Auml;';
        entities['197'] = '&Aring;';
        entities['198'] = '&AElig;';
        entities['199'] = '&Ccedil;';
        entities['200'] = '&Egrave;';
        entities['201'] = '&Eacute;';
        entities['202'] = '&Ecirc;';
        entities['203'] = '&Euml;';
        entities['204'] = '&Igrave;';
        entities['205'] = '&Iacute;';
        entities['206'] = '&Icirc;';
        entities['207'] = '&Iuml;';
        entities['208'] = '&ETH;';
        entities['209'] = '&Ntilde;';
        entities['210'] = '&Ograve;';
        entities['211'] = '&Oacute;';
        entities['212'] = '&Ocirc;';
        entities['213'] = '&Otilde;';
        entities['214'] = '&Ouml;';
        entities['215'] = '&times;';
        entities['216'] = '&Oslash;';
        entities['217'] = '&Ugrave;';
        entities['218'] = '&Uacute;';
        entities['219'] = '&Ucirc;';
        entities['220'] = '&Uuml;';
        entities['221'] = '&Yacute;';
        entities['222'] = '&THORN;';
        entities['223'] = '&szlig;';
        entities['224'] = '&agrave;';
        entities['225'] = '&aacute;';
        entities['226'] = '&acirc;';
        entities['227'] = '&atilde;';
        entities['228'] = '&auml;';
        entities['229'] = '&aring;';
        entities['230'] = '&aelig;';
        entities['231'] = '&ccedil;';
        entities['232'] = '&egrave;';
        entities['233'] = '&eacute;';
        entities['234'] = '&ecirc;';
        entities['235'] = '&euml;';
        entities['236'] = '&igrave;';
        entities['237'] = '&iacute;';
        entities['238'] = '&icirc;';
        entities['239'] = '&iuml;';
        entities['240'] = '&eth;';
        entities['241'] = '&ntilde;';
        entities['242'] = '&ograve;';
        entities['243'] = '&oacute;';
        entities['244'] = '&ocirc;';
        entities['245'] = '&otilde;';
        entities['246'] = '&ouml;';
        entities['247'] = '&divide;';
        entities['248'] = '&oslash;';
        entities['249'] = '&ugrave;';
        entities['250'] = '&uacute;';
        entities['251'] = '&ucirc;';
        entities['252'] = '&uuml;';
        entities['253'] = '&yacute;';
        entities['254'] = '&thorn;';
        entities['255'] = '&yuml;';
    }

    if (useQuoteStyle !== 'ENT_NOQUOTES') {
        entities['34'] = '&quot;';
    }
    if (useQuoteStyle === 'ENT_QUOTES') {
        entities['39'] = '&#39;';
    }
    entities['60'] = '&lt;';
    entities['62'] = '&gt;';


    // ascii decimals to real symbols
    for (decimal in entities) {
        symbol = String.fromCharCode(decimal);
        hash_map[symbol] = entities[decimal];
    }

    return hash_map;
}

function html_entity_decode (string, quote_style) {
    // http://kevin.vanzonneveld.net
    // +   original by: john (http://www.jd-tech.net)
    // +      input by: ger
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   bugfixed by: Onno Marsman
    // +   improved by: marc andreu
    // +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +      input by: Ratheous
    // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
    // +      input by: Nick Kolosov (http://sammy.ru)
    // +   bugfixed by: Fox
    // -    depends on: get_html_translation_table
    // *     example 1: html_entity_decode('Kevin &amp; van Zonneveld');
    // *     returns 1: 'Kevin & van Zonneveld'
    // *     example 2: html_entity_decode('&amp;lt;');
    // *     returns 2: '&lt;'
    var hash_map = {},
        symbol = '',
        tmp_str = '',
        entity = '';
    tmp_str = string.toString();

    if (false === (hash_map = this.get_html_translation_table('HTML_ENTITIES', quote_style))) {
        return false;
    }

    // fix &amp; problem
    // http://phpjs.org/functions/get_html_translation_table:416#comment_97660
    delete(hash_map['&']);
    hash_map['&'] = '&amp;';

    for (symbol in hash_map) {
        entity = hash_map[symbol];
        tmp_str = tmp_str.split(entity).join(symbol);
    }
    tmp_str = tmp_str.split('&#039;').join("'");

    return tmp_str;
}

function mb_strlen(str) {
    var len = 0;
    for(var i = 0; i < str.length; i++) {
        len += str.charCodeAt(i) < 0 || str.charCodeAt(i) > 255 ? 2 : 1;
    }
    return len;
}
