/* Minification failed. Returning unminified contents.
(57077,66-72): run-time error JS1137: 'native' is a new reserved word and should not be used as an identifier: native
(57082,33-39): run-time error JS1137: 'native' is a new reserved word and should not be used as an identifier: native
(58227,8-14): run-time error JS1137: 'native' is a new reserved word and should not be used as an identifier: native
(64288,32-38): run-time error JS1137: 'native' is a new reserved word and should not be used as an identifier: native
(64293,32-38): run-time error JS1137: 'native' is a new reserved word and should not be used as an identifier: native
(71107,66-72): run-time error JS1137: 'native' is a new reserved word and should not be used as an identifier: native
(71112,33-39): run-time error JS1137: 'native' is a new reserved word and should not be used as an identifier: native
(72257,8-14): run-time error JS1137: 'native' is a new reserved word and should not be used as an identifier: native
(78318,32-38): run-time error JS1137: 'native' is a new reserved word and should not be used as an identifier: native
(78323,32-38): run-time error JS1137: 'native' is a new reserved word and should not be used as an identifier: native
 */
/************************************************************************************************************
*   Author: Vladimir Nechypurenko
*   Description: Override serializeArray method to get disabled elements serialized
************************************************************************************************************/
(function() {
    var rselectTextarea = /select|textarea/i,
	    rinput = /color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i;

    jQuery.fn.serializeArray = function() {
        return this.map(function() {
            return this.elements ? jQuery.makeArray(this.elements) : this;
        })
		    .filter(function() {
		        return this.name &&
				    (this.checked || rselectTextarea.test(this.nodeName) ||
					    rinput.test(this.type));
		    })
		    .map(function(i, elem) {
		        var val = jQuery(this).val();
		        //Only applicable if project contains jquery.inputConstraints plugin
		        if (jQuery(this).hasClass("numeric") && typeof (jQuery(this).getNumVal) == "function") {
		            val = jQuery(this).getNumVal(val) + "";
		        }

		        return val == null ?
				    null :
				    jQuery.isArray(val) ?
					    jQuery.map(val, function(val, i) {
					        return { name: elem.name, value: val };
					    }) :
					    { name: elem.name, value: val };
		    }).get();
    }
})();


///Serialize a form to JSON object
$.fn.serializeObject = function() {
    var o = {};
    var a = this.serializeArray();
    $.each(a, function() {
        if (o[this.name] && (this.value == "true" || this.value == "false")) {
            //Ignore double checked values for MVC CheckBox
        }
        else if (o[this.name]) {
            if (!o[this.name].push) {
                o[this.name] = [o[this.name]];
            }
            o[this.name].push(this.value || '');
        } else {
            o[this.name] = this.value || '';
        }
    });
    return o;
};;/**
 * jQuery Validation Plugin 1.8.1
 *
 * http://bassistance.de/jquery-plugins/jquery-plugin-validation/
 * http://docs.jquery.com/Plugins/Validation
 *
 * Copyright (c) 2006 - 2011 Jörn Zaefferer
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */

(function($) {

$.extend($.fn, {
	// http://docs.jquery.com/Plugins/Validation/validate
	validate: function( options ) {

		// if nothing is selected, return nothing; can't chain anyway
		if (!this.length) {
			options && options.debug && window.console && console.warn( "nothing selected, can't validate, returning nothing" );
			return;
		}

		// check if a validator for this form was already created
		var validator = $.data(this[0], 'validator');
		if ( validator ) {
			return validator;
		}

		validator = new $.validator( options, this[0] );
		$.data(this[0], 'validator', validator);

		if ( validator.settings.onsubmit ) {

			// allow suppresing validation by adding a cancel class to the submit button
			this.find("input, button").filter(".cancel").click(function() {
				validator.cancelSubmit = true;
			});

			// when a submitHandler is used, capture the submitting button
			if (validator.settings.submitHandler) {
				this.find("input, button").filter(":submit").click(function() {
					validator.submitButton = this;
				});
			}

			// validate the form on submit
			this.submit( function( event ) {
				if ( validator.settings.debug )
					// prevent form submit to be able to see console output
					event.preventDefault();

				function handle() {
					if ( validator.settings.submitHandler ) {
						if (validator.submitButton) {
							// insert a hidden input as a replacement for the missing submit button
							var hidden = $("<input type='hidden'/>").attr("name", validator.submitButton.name).val(validator.submitButton.value).appendTo(validator.currentForm);
						}
						validator.settings.submitHandler.call( validator, validator.currentForm );
						if (validator.submitButton) {
							// and clean up afterwards; thanks to no-block-scope, hidden can be referenced
							hidden.remove();
						}
						return false;
					}
					return true;
				}

				// prevent submit for invalid forms or custom submit handlers
				if ( validator.cancelSubmit ) {
					validator.cancelSubmit = false;
					return handle();
				}
				if ( validator.form() ) {
					if ( validator.pendingRequest ) {
						validator.formSubmitted = true;
						return false;
					}
					return handle();
				} else {
					validator.focusInvalid();
					return false;
				}
			});
		}

		return validator;
	},
	// http://docs.jquery.com/Plugins/Validation/valid
	valid: function() {
        if ( $(this[0]).is('form')) {
            return this.validate().form();
        } else {
            var valid = true;
            var validator = $(this[0].form).validate();
            this.each(function() {
				valid &= validator.element(this);
            });
            return valid;
        }
    },
	// attributes: space seperated list of attributes to retrieve and remove
	removeAttrs: function(attributes) {
		var result = {},
			$element = this;
		$.each(attributes.split(/\s/), function(index, value) {
			result[value] = $element.attr(value);
			$element.removeAttr(value);
		});
		return result;
	},
	// http://docs.jquery.com/Plugins/Validation/rules
	rules: function(command, argument) {
		var element = this[0];

		if (command) {
			var settings = $.data(element.form, 'validator').settings;
			var staticRules = settings.rules;
			var existingRules = $.validator.staticRules(element);
			switch(command) {
			case "add":
				$.extend(existingRules, $.validator.normalizeRule(argument));
				staticRules[element.name] = existingRules;
				if (argument.messages)
					settings.messages[element.name] = $.extend( settings.messages[element.name], argument.messages );
				break;
			case "remove":
				if (!argument) {
					delete staticRules[element.name];
					return existingRules;
				}
				var filtered = {};
				$.each(argument.split(/\s/), function(index, method) {
					filtered[method] = existingRules[method];
					delete existingRules[method];
				});
				return filtered;
			}
		}

		var data = $.validator.normalizeRules(
		$.extend(
			{},
			$.validator.metadataRules(element),
			$.validator.classRules(element),
			$.validator.attributeRules(element),
			$.validator.staticRules(element)
		), element);

		// make sure required is at front
		if (data.required) {
			var param = data.required;
			delete data.required;
			data = $.extend({required: param}, data);
		}

		return data;
	}
});

// Custom selectors
$.extend($.expr[":"], {
	// http://docs.jquery.com/Plugins/Validation/blank
	blank: function(a) {return !$.trim("" + a.value);},
	// http://docs.jquery.com/Plugins/Validation/filled
	filled: function(a) {return !!$.trim("" + a.value);},
	// http://docs.jquery.com/Plugins/Validation/unchecked
	unchecked: function(a) {return !a.checked;}
});

// constructor for validator
$.validator = function( options, form ) {
	this.settings = $.extend( true, {}, $.validator.defaults, options );
	this.currentForm = form;
	this.init();
};

$.validator.format = function(source, params) {
	if ( arguments.length == 1 )
		return function() {
			var args = $.makeArray(arguments);
			args.unshift(source);
			return $.validator.format.apply( this, args );
		};
	if ( arguments.length > 2 && params.constructor != Array  ) {
		params = $.makeArray(arguments).slice(1);
	}
	if ( params.constructor != Array ) {
		params = [ params ];
	}
	$.each(params, function(i, n) {
		source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n);
	});
	return source;
};

$.extend($.validator, {

	defaults: {
		messages: {},
		groups: {},
		rules: {},
		errorClass: "error",
		validClass: "valid",
		errorElement: "label",
		focusInvalid: true,
		errorContainer: $( [] ),
		errorLabelContainer: $( [] ),
		onsubmit: true,
		ignore: [],
		ignoreTitle: false,
		onfocusin: function(element) {
			this.lastActive = element;

			// hide error label and remove error class on focus if enabled
			if ( this.settings.focusCleanup && !this.blockFocusCleanup ) {
				this.settings.unhighlight && this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );
				this.addWrapper(this.errorsFor(element)).hide();
			}
		},
		onfocusout: function(element) {
			if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) {
				this.element(element);
			}
		},
		onkeyup: function(element) {
			if ( element.name in this.submitted || element == this.lastElement ) {
				this.element(element);
			}
		},
		onclick: function(element) {
			// click on selects, radiobuttons and checkboxes
			if ( element.name in this.submitted )
				this.element(element);
			// or option elements, check parent select in that case
			else if (element.parentNode.name in this.submitted)
				this.element(element.parentNode);
		},
		highlight: function(element, errorClass, validClass) {
			if (element.type === 'radio') {
				this.findByName(element.name).addClass(errorClass).removeClass(validClass);
			} else {
				$(element).addClass(errorClass).removeClass(validClass);
			}
		},
		unhighlight: function(element, errorClass, validClass) {
			if (element.type === 'radio') {
				this.findByName(element.name).removeClass(errorClass).addClass(validClass);
			} else {
				$(element).removeClass(errorClass).addClass(validClass);
			}
		}
	},

	// http://docs.jquery.com/Plugins/Validation/Validator/setDefaults
	setDefaults: function(settings) {
		$.extend( $.validator.defaults, settings );
	},

	messages: {
		required: "This field is required.",
		remote: "Please fix this field.",
		email: "Please enter a valid email address.",
		url: "Please enter a valid URL.",
		date: "Please enter a valid date.",
		dateISO: "Please enter a valid date (ISO).",
		number: "Please enter a valid number.",
		digits: "Please enter only digits.",
		creditcard: "Please enter a valid credit card number.",
		equalTo: "Please enter the same value again.",
		accept: "Please enter a value with a valid extension.",
		maxlength: $.validator.format("Please enter no more than {0} characters."),
		minlength: $.validator.format("Please enter at least {0} characters."),
		rangelength: $.validator.format("Please enter a value between {0} and {1} characters long."),
		range: $.validator.format("Please enter a value between {0} and {1}."),
		max: $.validator.format("Please enter a value less than or equal to {0}."),
		min: $.validator.format("Please enter a value greater than or equal to {0}.")
	},

	autoCreateRanges: false,

	prototype: {

		init: function() {
			this.labelContainer = $(this.settings.errorLabelContainer);
			this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm);
			this.containers = $(this.settings.errorContainer).add( this.settings.errorLabelContainer );
			this.submitted = {};
			this.valueCache = {};
			this.pendingRequest = 0;
			this.pending = {};
			this.invalid = {};
			this.reset();

			var groups = (this.groups = {});
			$.each(this.settings.groups, function(key, value) {
				$.each(value.split(/\s/), function(index, name) {
					groups[name] = key;
				});
			});
			var rules = this.settings.rules;
			$.each(rules, function(key, value) {
				rules[key] = $.validator.normalizeRule(value);
			});

			function delegate(event) {
				var validator = $.data(this[0].form, "validator"),
					eventType = "on" + event.type.replace(/^validate/, "");
				validator.settings[eventType] && validator.settings[eventType].call(validator, this[0] );
			}
			$(this.currentForm)
				.validateDelegate(":text, :password, :file, select, textarea", "focusin focusout keyup", delegate)
				.validateDelegate(":radio, :checkbox, select, option", "click", delegate);

			if (this.settings.invalidHandler)
				$(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler);
		},

		// http://docs.jquery.com/Plugins/Validation/Validator/form
		form: function() {
			this.checkForm();
			$.extend(this.submitted, this.errorMap);
			this.invalid = $.extend({}, this.errorMap);
			if (!this.valid())
				$(this.currentForm).triggerHandler("invalid-form", [this]);
			this.showErrors();
			return this.valid();
		},

		checkForm: function() {
			this.prepareForm();
			for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) {
				this.check( elements[i] );
			}
			return this.valid();
		},

		// http://docs.jquery.com/Plugins/Validation/Validator/element
		element: function( element ) {
			element = this.clean( element );
			this.lastElement = element;
			this.prepareElement( element );
			this.currentElements = $(element);
			var result = this.check( element );
			if ( result ) {
				delete this.invalid[element.name];
			} else {
				this.invalid[element.name] = true;
			}
			if ( !this.numberOfInvalids() ) {
				// Hide error containers on last error
				this.toHide = this.toHide.add( this.containers );
			}
			this.showErrors();
			return result;
		},

		// http://docs.jquery.com/Plugins/Validation/Validator/showErrors
		showErrors: function(errors) {
			if(errors) {
				// add items to error list and map
				$.extend( this.errorMap, errors );
				this.errorList = [];
				for ( var name in errors ) {
					this.errorList.push({
						message: errors[name],
						element: this.findByName(name)[0]
					});
				}
				// remove items from success list
				this.successList = $.grep( this.successList, function(element) {
					return !(element.name in errors);
				});
			}
			this.settings.showErrors
				? this.settings.showErrors.call( this, this.errorMap, this.errorList )
				: this.defaultShowErrors();
		},

		// http://docs.jquery.com/Plugins/Validation/Validator/resetForm
		resetForm: function() {
			if ( $.fn.resetForm )
				$( this.currentForm ).resetForm();
			this.submitted = {};
			this.prepareForm();
			this.hideErrors();
			this.elements().removeClass( this.settings.errorClass );
		},

		numberOfInvalids: function() {
			return this.objectLength(this.invalid);
		},

		objectLength: function( obj ) {
			var count = 0;
			for ( var i in obj )
				count++;
			return count;
		},

		hideErrors: function() {
			this.addWrapper( this.toHide ).hide();
		},

		valid: function() {
			return this.size() == 0;
		},

		size: function() {
			return this.errorList.length;
		},

		focusInvalid: function() {
			if( this.settings.focusInvalid ) {
				try {
					$(this.findLastActive() || this.errorList.length && this.errorList[0].element || [])
					.filter(":visible")
					.focus()
					// manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
					.trigger("focusin");
				} catch(e) {
					// ignore IE throwing errors when focusing hidden elements
				}
			}
		},

		findLastActive: function() {
			var lastActive = this.lastActive;
			return lastActive && $.grep(this.errorList, function(n) {
				return n.element.name == lastActive.name;
			}).length == 1 && lastActive;
		},

		elements: function() {
			var validator = this,
				rulesCache = {};

			// select all valid inputs inside the form (no submit or reset buttons)
			return $(this.currentForm)
			.find("input, select, textarea")
			.not(":submit, :reset, :image, [disabled]")
			.not( this.settings.ignore )
			.filter(function() {
				!this.name && validator.settings.debug && window.console && console.error( "%o has no name assigned", this);

				// select only the first element for each name, and only those with rules specified
				if ( this.name in rulesCache || !validator.objectLength($(this).rules()) )
					return false;

				rulesCache[this.name] = true;
				return true;
			});
		},

		clean: function( selector ) {
			return $( selector )[0];
		},

		errors: function() {
			return $( this.settings.errorElement + "." + this.settings.errorClass, this.errorContext );
		},

		reset: function() {
			this.successList = [];
			this.errorList = [];
			this.errorMap = {};
			this.toShow = $([]);
			this.toHide = $([]);
			this.currentElements = $([]);
		},

		prepareForm: function() {
			this.reset();
			this.toHide = this.errors().add( this.containers );
		},

		prepareElement: function( element ) {
			this.reset();
			this.toHide = this.errorsFor(element);
		},

		check: function( element ) {
			element = this.clean( element );

			// if radio/checkbox, validate first element in group instead
			if (this.checkable(element)) {
				element = this.findByName( element.name ).not(this.settings.ignore)[0];
			}

			var rules = $(element).rules();
			var dependencyMismatch = false;
			for (var method in rules ) {
				var rule = { method: method, parameters: rules[method] };
				try {
					var result = $.validator.methods[method].call( this, element.value.replace(/\r/g, ""), element, rule.parameters );

					// if a method indicates that the field is optional and therefore valid,
					// don't mark it as valid when there are no other rules
					if ( result == "dependency-mismatch" ) {
						dependencyMismatch = true;
						continue;
					}
					dependencyMismatch = false;

					if ( result == "pending" ) {
						this.toHide = this.toHide.not( this.errorsFor(element) );
						return;
					}

					if( !result ) {
						this.formatAndAdd( element, rule );
						return false;
					}
				} catch(e) {
					this.settings.debug && window.console && console.log("exception occured when checking element " + element.id
						 + ", check the '" + rule.method + "' method", e);
					throw e;
				}
			}
			if (dependencyMismatch)
				return;
			if ( this.objectLength(rules) )
				this.successList.push(element);
			return true;
		},

		// return the custom message for the given element and validation method
		// specified in the element's "messages" metadata
		customMetaMessage: function(element, method) {
			if (!$.metadata)
				return;

			var meta = this.settings.meta
				? $(element).metadata()[this.settings.meta]
				: $(element).metadata();

			return meta && meta.messages && meta.messages[method];
		},

		// return the custom message for the given element name and validation method
		customMessage: function( name, method ) {
			var m = this.settings.messages[name];
			return m && (m.constructor == String
				? m
				: m[method]);
		},

		// return the first defined argument, allowing empty strings
		findDefined: function() {
			for(var i = 0; i < arguments.length; i++) {
				if (arguments[i] !== undefined)
					return arguments[i];
			}
			return undefined;
		},

		defaultMessage: function( element, method) {
			return this.findDefined(
				this.customMessage( element.name, method ),
				this.customMetaMessage( element, method ),
				// title is never undefined, so handle empty string as undefined
				!this.settings.ignoreTitle && element.title || undefined,
				$.validator.messages[method],
				"<strong>Warning: No message defined for " + element.name + "</strong>"
			);
		},

		formatAndAdd: function( element, rule ) {
			var message = this.defaultMessage( element, rule.method ),
				theregex = /\$?\{(\d+)\}/g;
			if ( typeof message == "function" ) {
				message = message.call(this, rule.parameters, element);
			} else if (theregex.test(message)) {
				message = jQuery.format(message.replace(theregex, '{$1}'), rule.parameters);
			}
			this.errorList.push({
				message: message,
				element: element
			});

			this.errorMap[element.name] = message;
			this.submitted[element.name] = message;
		},

		addWrapper: function(toToggle) {
			if ( this.settings.wrapper )
				toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );
			return toToggle;
		},

		defaultShowErrors: function() {
			for ( var i = 0; this.errorList[i]; i++ ) {
				var error = this.errorList[i];
				this.settings.highlight && this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );
				this.showLabel( error.element, error.message );
			}
			if( this.errorList.length ) {
				this.toShow = this.toShow.add( this.containers );
			}
			if (this.settings.success) {
				for ( var i = 0; this.successList[i]; i++ ) {
					this.showLabel( this.successList[i] );
				}
			}
			if (this.settings.unhighlight) {
				for ( var i = 0, elements = this.validElements(); elements[i]; i++ ) {
					this.settings.unhighlight.call( this, elements[i], this.settings.errorClass, this.settings.validClass );
				}
			}
			this.toHide = this.toHide.not( this.toShow );
			this.hideErrors();
			this.addWrapper( this.toShow ).show();
		},

		validElements: function() {
			return this.currentElements.not(this.invalidElements());
		},

		invalidElements: function() {
			return $(this.errorList).map(function() {
				return this.element;
			});
		},

		showLabel: function(element, message) {
			var label = this.errorsFor( element );
			if ( label.length ) {
				// refresh error/success class
				label.removeClass().addClass( this.settings.errorClass );

				// check if we have a generated label, replace the message then
				label.attr("generated") && label.html(message);
			} else {
				// create label
				label = $("<" + this.settings.errorElement + "/>")
					.attr({"for":  this.idOrName(element), generated: true})
					.addClass(this.settings.errorClass)
					.html(message || "");
				if ( this.settings.wrapper ) {
					// make sure the element is visible, even in IE
					// actually showing the wrapped element is handled elsewhere
					label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent();
				}
				if ( !this.labelContainer.append(label).length )
					this.settings.errorPlacement
						? this.settings.errorPlacement(label, $(element) )
						: label.insertAfter(element);
			}
			if ( !message && this.settings.success ) {
				label.text("");
				typeof this.settings.success == "string"
					? label.addClass( this.settings.success )
					: this.settings.success( label );
			}
			this.toShow = this.toShow.add(label);
		},

		errorsFor: function(element) {
			var name = this.idOrName(element);
    		return this.errors().filter(function() {
				return $(this).attr('for') == name;
			});
		},

		idOrName: function(element) {
			return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name);
		},

		checkable: function( element ) {
			return /radio|checkbox/i.test(element.type);
		},

		findByName: function( name ) {
			// select by name and filter by form for performance over form.find("[name=...]")
			var form = this.currentForm;
			return $(document.getElementsByName(name)).map(function(index, element) {
				return element.form == form && element.name == name && element  || null;
			});
		},

		getLength: function(value, element) {
			switch( element.nodeName.toLowerCase() ) {
			case 'select':
				return $("option:selected", element).length;
			case 'input':
				if( this.checkable( element) )
					return this.findByName(element.name).filter(':checked').length;
			}
			return value.length;
		},

		depend: function(param, element) {
			return this.dependTypes[typeof param]
				? this.dependTypes[typeof param](param, element)
				: true;
		},

		dependTypes: {
			"boolean": function(param, element) {
				return param;
			},
			"string": function(param, element) {
				return !!$(param, element.form).length;
			},
			"function": function(param, element) {
				return param(element);
			}
		},

		optional: function(element) {
			return !$.validator.methods.required.call(this, $.trim(element.value), element) && "dependency-mismatch";
		},

		startRequest: function(element) {
			if (!this.pending[element.name]) {
				this.pendingRequest++;
				this.pending[element.name] = true;
			}
		},

		stopRequest: function(element, valid) {
			this.pendingRequest--;
			// sometimes synchronization fails, make sure pendingRequest is never < 0
			if (this.pendingRequest < 0)
				this.pendingRequest = 0;
			delete this.pending[element.name];
			if ( valid && this.pendingRequest == 0 && this.formSubmitted && this.form() ) {
				$(this.currentForm).submit();
				this.formSubmitted = false;
			} else if (!valid && this.pendingRequest == 0 && this.formSubmitted) {
				$(this.currentForm).triggerHandler("invalid-form", [this]);
				this.formSubmitted = false;
			}
		},

		previousValue: function(element) {
			return $.data(element, "previousValue") || $.data(element, "previousValue", {
				old: null,
				valid: true,
				message: this.defaultMessage( element, "remote" )
			});
		}

	},

	classRuleSettings: {
		required: {required: true},
		email: {email: true},
		url: {url: true},
		date: {date: true},
		dateISO: {dateISO: true},
		dateDE: {dateDE: true},
		number: {number: true},
		numberDE: {numberDE: true},
		digits: {digits: true},
		creditcard: {creditcard: true}
	},

	addClassRules: function(className, rules) {
		className.constructor == String ?
			this.classRuleSettings[className] = rules :
			$.extend(this.classRuleSettings, className);
	},

	classRules: function(element) {
		var rules = {};
		var classes = $(element).attr('class');
		classes && $.each(classes.split(' '), function() {
			if (this in $.validator.classRuleSettings) {
				$.extend(rules, $.validator.classRuleSettings[this]);
			}
		});
		return rules;
	},

	attributeRules: function(element) {
		var rules = {};
		var $element = $(element);

		for (var method in $.validator.methods) {
			var value = $element.attr(method);
			if (value) {
				rules[method] = value;
			}
		}

		// maxlength may be returned as -1, 2147483647 (IE) and 524288 (safari) for text inputs
		if (rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength)) {
			delete rules.maxlength;
		}

		return rules;
	},

	metadataRules: function(element) {
		if (!$.metadata) return {};

		var meta = $.data(element.form, 'validator').settings.meta;
		return meta ?
			$(element).metadata()[meta] :
			$(element).metadata();
	},

	staticRules: function(element) {
		var rules = {};
		var validator = $.data(element.form, 'validator');
		if (validator.settings.rules) {
			rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {};
		}
		return rules;
	},

	normalizeRules: function(rules, element) {
		// handle dependency check
		$.each(rules, function(prop, val) {
			// ignore rule when param is explicitly false, eg. required:false
			if (val === false) {
				delete rules[prop];
				return;
			}
			if (val.param || val.depends) {
				var keepRule = true;
				switch (typeof val.depends) {
					case "string":
						keepRule = !!$(val.depends, element.form).length;
						break;
					case "function":
						keepRule = val.depends.call(element, element);
						break;
				}
				if (keepRule) {
					rules[prop] = val.param !== undefined ? val.param : true;
				} else {
					delete rules[prop];
				}
			}
		});

		// evaluate parameters
		$.each(rules, function(rule, parameter) {
			rules[rule] = $.isFunction(parameter) ? parameter(element) : parameter;
		});

		// clean number parameters
		$.each(['minlength', 'maxlength', 'min', 'max'], function() {
			if (rules[this]) {
				rules[this] = Number(rules[this]);
			}
		});
		$.each(['rangelength', 'range'], function() {
			if (rules[this]) {
				rules[this] = [Number(rules[this][0]), Number(rules[this][1])];
			}
		});

		if ($.validator.autoCreateRanges) {
			// auto-create ranges
			if (rules.min && rules.max) {
				rules.range = [rules.min, rules.max];
				delete rules.min;
				delete rules.max;
			}
			if (rules.minlength && rules.maxlength) {
				rules.rangelength = [rules.minlength, rules.maxlength];
				delete rules.minlength;
				delete rules.maxlength;
			}
		}

		// To support custom messages in metadata ignore rule methods titled "messages"
		if (rules.messages) {
			delete rules.messages;
		}

		return rules;
	},

	// Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
	normalizeRule: function(data) {
		if( typeof data == "string" ) {
			var transformed = {};
			$.each(data.split(/\s/), function() {
				transformed[this] = true;
			});
			data = transformed;
		}
		return data;
	},

	// http://docs.jquery.com/Plugins/Validation/Validator/addMethod
	addMethod: function(name, method, message) {
		$.validator.methods[name] = method;
		$.validator.messages[name] = message != undefined ? message : $.validator.messages[name];
		if (method.length < 3) {
			$.validator.addClassRules(name, $.validator.normalizeRule(name));
		}
	},

	methods: {

		// http://docs.jquery.com/Plugins/Validation/Methods/required
		required: function(value, element, param) {
			// check if dependency is met
			if ( !this.depend(param, element) )
				return "dependency-mismatch";
			switch( element.nodeName.toLowerCase() ) {
			case 'select':
				// could be an array for select-multiple or a string, both are fine this way
				var val = $(element).val();
				return val && val.length > 0;
			case 'input':
				if ( this.checkable(element) )
					return this.getLength(value, element) > 0;
			default:
				return $.trim(value).length > 0;
			}
		},

		// http://docs.jquery.com/Plugins/Validation/Methods/remote
		remote: function(value, element, param) {
			if ( this.optional(element) )
				return "dependency-mismatch";

			var previous = this.previousValue(element);
			if (!this.settings.messages[element.name] )
				this.settings.messages[element.name] = {};
			previous.originalMessage = this.settings.messages[element.name].remote;
			this.settings.messages[element.name].remote = previous.message;

			param = typeof param == "string" && {url:param} || param;

			if ( this.pending[element.name] ) {
				return "pending";
			}
			if ( previous.old === value ) {
				return previous.valid;
			}

			previous.old = value;
			var validator = this;
			this.startRequest(element);
			var data = {};
			data[element.name] = value;
			$.ajax($.extend(true, {
				url: param,
				mode: "abort",
				port: "validate" + element.name,
				dataType: "json",
				data: data,
				success: function(response) {
					validator.settings.messages[element.name].remote = previous.originalMessage;
					var valid = response === true;
					if ( valid ) {
						var submitted = validator.formSubmitted;
						validator.prepareElement(element);
						validator.formSubmitted = submitted;
						validator.successList.push(element);
						validator.showErrors();
					} else {
						var errors = {};
						var message = response || validator.defaultMessage( element, "remote" );
						errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message;
						validator.showErrors(errors);
					}
					previous.valid = valid;
					validator.stopRequest(element, valid);
				}
			}, param));
			return "pending";
		},

		// http://docs.jquery.com/Plugins/Validation/Methods/minlength
		minlength: function(value, element, param) {
			return this.optional(element) || this.getLength($.trim(value), element) >= param;
		},

		// http://docs.jquery.com/Plugins/Validation/Methods/maxlength
		maxlength: function(value, element, param) {
			return this.optional(element) || this.getLength($.trim(value), element) <= param;
		},

		// http://docs.jquery.com/Plugins/Validation/Methods/rangelength
		rangelength: function(value, element, param) {
			var length = this.getLength($.trim(value), element);
			return this.optional(element) || ( length >= param[0] && length <= param[1] );
		},

		// http://docs.jquery.com/Plugins/Validation/Methods/min
		min: function( value, element, param ) {
			return this.optional(element) || value >= param;
		},

		// http://docs.jquery.com/Plugins/Validation/Methods/max
		max: function( value, element, param ) {
			return this.optional(element) || value <= param;
		},

		// http://docs.jquery.com/Plugins/Validation/Methods/range
		range: function( value, element, param ) {
			return this.optional(element) || ( value >= param[0] && value <= param[1] );
		},

		// http://docs.jquery.com/Plugins/Validation/Methods/email
		email: function(value, element) {
			// contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
			return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
		},

		// http://docs.jquery.com/Plugins/Validation/Methods/url
		url: function(value, element) {
			// contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/
			return this.optional(element) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
		},

		// http://docs.jquery.com/Plugins/Validation/Methods/date
		date: function(value, element) {
			return this.optional(element) || !/Invalid|NaN/.test(new Date(value));
		},

		// http://docs.jquery.com/Plugins/Validation/Methods/dateISO
		dateISO: function(value, element) {
			return this.optional(element) || /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(value);
		},

		// http://docs.jquery.com/Plugins/Validation/Methods/number
		number: function(value, element) {
			return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(value);
		},

		// http://docs.jquery.com/Plugins/Validation/Methods/digits
		digits: function(value, element) {
			return this.optional(element) || /^\d+$/.test(value);
		},

		// http://docs.jquery.com/Plugins/Validation/Methods/creditcard
		// based on http://en.wikipedia.org/wiki/Luhn
		creditcard: function(value, element) {
			if ( this.optional(element) )
				return "dependency-mismatch";
			// accept only digits and dashes
			if (/[^0-9-]+/.test(value))
				return false;
			var nCheck = 0,
				nDigit = 0,
				bEven = false;

			value = value.replace(/\D/g, "");

			for (var n = value.length - 1; n >= 0; n--) {
				var cDigit = value.charAt(n);
				var nDigit = parseInt(cDigit, 10);
				if (bEven) {
					if ((nDigit *= 2) > 9)
						nDigit -= 9;
				}
				nCheck += nDigit;
				bEven = !bEven;
			}

			return (nCheck % 10) == 0;
		},

		// http://docs.jquery.com/Plugins/Validation/Methods/accept
		accept: function(value, element, param) {
			param = typeof param == "string" ? param.replace(/,/g, '|') : "png|jpe?g|gif";
			return this.optional(element) || value.match(new RegExp(".(" + param + ")$", "i"));
		},

		// http://docs.jquery.com/Plugins/Validation/Methods/equalTo
		equalTo: function(value, element, param) {
			// bind to the blur event of the target in order to revalidate whenever the target field is updated
			// TODO find a way to bind the event just once, avoiding the unbind-rebind overhead
			var target = $(param).unbind(".validate-equalTo").bind("blur.validate-equalTo", function() {
				$(element).valid();
			});
			return value == target.val();
		}

	}

});

// deprecated, use $.validator.format instead
$.format = $.validator.format;

})(jQuery);

// ajax mode: abort
// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()
;(function($) {
	var pendingRequests = {};
	// Use a prefilter if available (1.5+)
	if ( $.ajaxPrefilter ) {
		$.ajaxPrefilter(function(settings, _, xhr) {
			var port = settings.port;
			if (settings.mode == "abort") {
				if ( pendingRequests[port] ) {
					pendingRequests[port].abort();
				}
				pendingRequests[port] = xhr;
			}
		});
	} else {
		// Proxy ajax
		var ajax = $.ajax;
		$.ajax = function(settings) {
			var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode,
				port = ( "port" in settings ? settings : $.ajaxSettings ).port;
			if (mode == "abort") {
				if ( pendingRequests[port] ) {
					pendingRequests[port].abort();
				}
				return (pendingRequests[port] = ajax.apply(this, arguments));
			}
			return ajax.apply(this, arguments);
		};
	}
})(jQuery);

// provides cross-browser focusin and focusout events
// IE has native support, in other browsers, use event caputuring (neither bubbles)

// provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation
// handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target
;(function($) {
	// only implement if not provided by jQuery core (since 1.4)
	// TODO verify if jQuery 1.4's implementation is compatible with older jQuery special-event APIs
	if (!jQuery.event.special.focusin && !jQuery.event.special.focusout && document.addEventListener) {
		$.each({
			focus: 'focusin',
			blur: 'focusout'
		}, function( original, fix ){
			$.event.special[fix] = {
				setup:function() {
					this.addEventListener( original, handler, true );
				},
				teardown:function() {
					this.removeEventListener( original, handler, true );
				},
				handler: function(e) {
					arguments[0] = $.event.fix(e);
					arguments[0].type = fix;
					return $.event.handle.apply(this, arguments);
				}
			};
			function handler(e) {
				e = $.event.fix(e);
				e.type = fix;
				return $.event.handle.call(this, e);
			}
		});
	};
	$.extend($.fn, {
		validateDelegate: function(delegate, type, handler) {
			return this.bind(type, function(event) {
				var target = $(event.target);
				if (target.is(delegate)) {
					return handler.apply(target, arguments);
				}
			});
		}
	});
})(jQuery);
;/*
 * jQuery clueTip plugin
 * Version 1.0.6  (January 13, 2010)
 * @requires jQuery v1.3+
 *
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 */
 
/*
 *
 * Full list of options/settings can be found at the bottom of this file and at http://plugins.learningjquery.com/cluetip/
 *
 * Examples can be found at http://plugins.learningjquery.com/cluetip/demo/
 *
*/

;(function($) { 
  $.cluetip = {version: '1.0.6'};
  var $cluetip, $cluetipInner, $cluetipOuter, $cluetipTitle, $cluetipArrows, $cluetipWait, $dropShadow, imgCount;
  
  $.fn.cluetip = function(js, options) {
    if (typeof js == 'object') {
      options = js;
      js = null;
    }
    if (js == 'destroy') {
      return this.removeData('thisInfo').unbind('.cluetip');
    }
    return this.each(function(index) {
      var link = this, $this = $(this);
      
      // support metadata plugin (v1.0 and 2.0)
      var opts = $.extend(true, {}, $.fn.cluetip.defaults, options || {}, $.metadata ? $this.metadata() : $.meta ? $this.data() : {});

      // start out with no contents (for ajax activation)
      var cluetipContents = false;
      var cluezIndex = +opts.cluezIndex;
      $this.data('thisInfo', {title: link.title, zIndex: cluezIndex});
      var isActive = false, closeOnDelay = 0;

      // create the cluetip divs
      if (!$('#cluetip').length) {
        $(['<div id="cluetip">',
          '<div id="cluetip-outer">',
            '<h3 id="cluetip-title"></h3>',
            '<div id="cluetip-inner"></div>',
          '</div>',
          '<div id="cluetip-extra"></div>',
          '<div id="cluetip-arrows" class="cluetip-arrows"></div>',
        '</div>'].join(''))
        [insertionType](insertionElement).hide();
        
        $cluetip = $('#cluetip').css({position: 'absolute'});
        $cluetipOuter = $('#cluetip-outer').css({position: 'relative', zIndex: cluezIndex});
        $cluetipInner = $('#cluetip-inner');
        $cluetipTitle = $('#cluetip-title');        
        $cluetipArrows = $('#cluetip-arrows');
        $cluetipWait = $('<div id="cluetip-waitimage"></div>')
          .css({position: 'absolute'}).insertBefore($cluetip).hide();
      }
      var dropShadowSteps = (opts.dropShadow) ? +opts.dropShadowSteps : 0;
      if (!$dropShadow) {
        $dropShadow = $([]);
        for (var i=0; i < dropShadowSteps; i++) {
          $dropShadow = $dropShadow.add($('<div></div>').css({zIndex: cluezIndex-1, opacity:.1, top: 1+i, left: 1+i}));
        }
        $dropShadow.css({position: 'absolute', backgroundColor: '#000'})
        .prependTo($cluetip);
      }
      var tipAttribute = $this.attr(opts.attribute), ctClass = opts.cluetipClass;
      if (!tipAttribute && !opts.splitTitle && !js) {
        return true;
      }
      // if hideLocal is set to true, on DOM ready hide the local content that will be displayed in the clueTip
      if (opts.local && opts.localPrefix) {tipAttribute = opts.localPrefix + tipAttribute;}
      if (opts.local && opts.hideLocal) { $(tipAttribute + ':first').hide(); }
      var tOffset = parseInt(opts.topOffset, 10), lOffset = parseInt(opts.leftOffset, 10);
      // vertical measurement variables
      var tipHeight, wHeight,
          defHeight = isNaN(parseInt(opts.height, 10)) ? 'auto' : (/\D/g).test(opts.height) ? opts.height : opts.height + 'px';
      var sTop, linkTop, posY, tipY, mouseY, baseline;
      // horizontal measurement variables
      var tipInnerWidth = parseInt(opts.width, 10) || 275,
          tipWidth = tipInnerWidth + (parseInt($cluetip.css('paddingLeft'),10)||0) + (parseInt($cluetip.css('paddingRight'),10)||0) + dropShadowSteps,
          linkWidth = this.offsetWidth,
          linkLeft, posX, tipX, mouseX, winWidth;
            
      // parse the title
      var tipParts;
      var tipTitle = (opts.attribute != 'title') ? $this.attr(opts.titleAttribute) : '';
      if (opts.splitTitle) {
        if (tipTitle == undefined) {tipTitle = '';}
        tipParts = tipTitle.split(opts.splitTitle);
        tipTitle = tipParts.shift();
      }
      if (opts.escapeTitle) {
        tipTitle = tipTitle.replace(/&/g,'&amp;').replace(/>/g,'&gt;').replace(/</g,'&lt;');
      }
      
      var localContent;
      function returnFalse() { return false; }

/***************************************      
* ACTIVATION
****************************************/
    
//activate clueTip
    var activate = function(event) {
      if (!opts.onActivate($this)) {
        return false;
      }
      isActive = true;
      $cluetip.removeClass().css({width: tipInnerWidth});
      if (tipAttribute == $this.attr('href')) {
        $this.css('cursor', opts.cursor);
      }
      if (opts.hoverClass) {
        $this.addClass(opts.hoverClass);
      }
      linkTop = posY = $this.offset().top;
      linkLeft = $this.offset().left;
      mouseX = event.pageX;
      mouseY = event.pageY;
      if (link.tagName.toLowerCase() != 'area') {
        sTop = $(document).scrollTop();
        winWidth = $(window).width();
      }
// position clueTip horizontally
      if (opts.positionBy == 'fixed') {
        posX = linkWidth + linkLeft + lOffset;
        $cluetip.css({left: posX});
      } else {
        posX = (linkWidth > linkLeft && linkLeft > tipWidth)
          || linkLeft + linkWidth + tipWidth + lOffset > winWidth 
          ? linkLeft - tipWidth - lOffset 
          : linkWidth + linkLeft + lOffset;
        if (link.tagName.toLowerCase() == 'area' || opts.positionBy == 'mouse' || linkWidth + tipWidth > winWidth) { // position by mouse
          if (mouseX + 20 + tipWidth > winWidth) {  
            $cluetip.addClass(' cluetip-' + ctClass);
            posX = (mouseX - tipWidth - lOffset) >= 0 ? mouseX - tipWidth - lOffset - parseInt($cluetip.css('marginLeft'),10) + parseInt($cluetipInner.css('marginRight'),10) :  mouseX - (tipWidth/2);
          } else {
            posX = mouseX + lOffset;
          }
        }
        var pY = posX < 0 ? event.pageY + tOffset : event.pageY;
        $cluetip.css({
          left: (posX > 0 && opts.positionBy != 'bottomTop') ? posX : (mouseX + (tipWidth/2) > winWidth) ? winWidth/2 - tipWidth/2 : Math.max(mouseX - (tipWidth/2),0),
          zIndex: $this.data('thisInfo').zIndex
        });
        $cluetipArrows.css({zIndex: $this.data('thisInfo').zIndex+1});
      }
        wHeight = $(window).height();

/***************************************
* load a string from cluetip method's first argument
***************************************/
      if (js) {
        if (typeof js == 'function') {
          js = js.call(link);
        }
        $cluetipInner.html(js);
        cluetipShow(pY);
      }
/***************************************
* load the title attribute only (or user-selected attribute). 
* clueTip title is the string before the first delimiter
* subsequent delimiters place clueTip body text on separate lines
***************************************/

      else if (tipParts) {
        var tpl = tipParts.length;
        $cluetipInner.html(tpl ? tipParts[0] : '');
        if (tpl > 1) {
          for (var i=1; i < tpl; i++){
            $cluetipInner.append('<div class="split-body">' + tipParts[i] + '</div>');
          }          
        }
        cluetipShow(pY);
      }
/***************************************
* load external file via ajax          
***************************************/

      else if (!opts.local && tipAttribute.indexOf('#') !== 0) {
        if (/\.(jpe?g|tiff?|gif|png)$/i.test(tipAttribute)) {
          $cluetipInner.html('<img src="' + tipAttribute + '" alt="' + tipTitle + '" />');
          cluetipShow(pY);
        } else if (cluetipContents && opts.ajaxCache) {
          $cluetipInner.html(cluetipContents);
          cluetipShow(pY);
        } else {
          var optionBeforeSend = opts.ajaxSettings.beforeSend,
              optionError = opts.ajaxSettings.error,
              optionSuccess = opts.ajaxSettings.success,
              optionComplete = opts.ajaxSettings.complete;
          var ajaxSettings = {
            cache: false, // force requested page not to be cached by browser
            url: tipAttribute,
            beforeSend: function(xhr) {
              if (optionBeforeSend) {optionBeforeSend.call(link, xhr, $cluetip, $cluetipInner);}
              $cluetipOuter.children().empty();
              if (opts.waitImage) {
                $cluetipWait
                .css({top: mouseY+20, left: mouseX+20, zIndex: $this.data('thisInfo').zIndex-1})
                .show();
              }
            },
            error: function(xhr, textStatus) {
              if (isActive) {
                if (optionError) {
                  optionError.call(link, xhr, textStatus, $cluetip, $cluetipInner);
                } else {
                  $cluetipInner.html('<i>sorry, the contents could not be loaded</i>');  
                }
              }
            },
            success: function(data, textStatus) {       
              cluetipContents = opts.ajaxProcess.call(link, data);
              if (isActive) {
                if (optionSuccess) {optionSuccess.call(link, data, textStatus, $cluetip, $cluetipInner);}
                $cluetipInner.html(cluetipContents);
              }
            },
            complete: function(xhr, textStatus) {
              if (optionComplete) {optionComplete.call(link, xhr, textStatus, $cluetip, $cluetipInner);}
              imgCount = $('#cluetip-inner img').length;
              if (imgCount && !$.browser.opera) {
                $('#cluetip-inner img').bind('load error', function() {
                  imgCount--;
                  if (imgCount<1) {
                    $cluetipWait.hide();
                    if (isActive) { cluetipShow(pY); }
                  }
                }); 
              } else {
                $cluetipWait.hide();
                if (isActive) { cluetipShow(pY); }
              } 
            }
          };
          var ajaxMergedSettings = $.extend(true, {}, opts.ajaxSettings, ajaxSettings);
          
          $.ajax(ajaxMergedSettings);
        }

/***************************************
* load an element from the same page
***************************************/
      } else if (opts.local) {
        
        var $localContent = $(tipAttribute + (/#\S+$/.test(tipAttribute) ? '' : ':eq(' + index + ')')).clone(true).show();
        $cluetipInner.html($localContent);
        cluetipShow(pY);
      }
    };

// get dimensions and options for cluetip and prepare it to be shown
    var cluetipShow = function(bpY) {
      $cluetip.addClass('cluetip-' + ctClass);
      if (opts.truncate) { 
        var $truncloaded = $cluetipInner.text().slice(0,opts.truncate) + '...';
        $cluetipInner.html($truncloaded);
      }
      function doNothing() {}; //empty function
      tipTitle ? $cluetipTitle.show().html(tipTitle) : (opts.showTitle) ? $cluetipTitle.show().html('&nbsp;') : $cluetipTitle.hide();
      if (opts.sticky) {
        var $closeLink = $('<div id="cluetip-close"><a href="#">' + opts.closeText + '</a></div>');
        (opts.closePosition == 'bottom') ? $closeLink.appendTo($cluetipInner) : (opts.closePosition == 'title') ? $closeLink.prependTo($cluetipTitle) : $closeLink.prependTo($cluetipInner);
        $closeLink.bind('click.cluetip', function() {
          cluetipClose();
          return false;
        });
        if (opts.mouseOutClose) {
          $cluetip.bind('mouseleave.cluetip', function() {
            cluetipClose();
          });
        } else {
          $cluetip.unbind('mouseleave.cluetip');
        }
      }
// now that content is loaded, finish the positioning 
      var direction = '';
      $cluetipOuter.css({zIndex: $this.data('thisInfo').zIndex, overflow: defHeight == 'auto' ? 'visible' : 'auto', height: defHeight});
      tipHeight = defHeight == 'auto' ? Math.max($cluetip.outerHeight(),$cluetip.height()) : parseInt(defHeight,10);   
      tipY = posY;
      baseline = sTop + wHeight;
      if (opts.positionBy == 'fixed') {
        tipY = posY - opts.dropShadowSteps + tOffset;
      } else if ( (posX < mouseX && Math.max(posX, 0) + tipWidth > mouseX) || opts.positionBy == 'bottomTop') {
        if (posY + tipHeight + tOffset > baseline && mouseY - sTop > tipHeight + tOffset) { 
          tipY = mouseY - tipHeight - tOffset;
          direction = 'top';
        } else { 
          tipY = mouseY + tOffset;
          direction = 'bottom';
        }
      } else if ( posY + tipHeight + tOffset > baseline ) {
        tipY = (tipHeight >= wHeight) ? sTop : baseline - tipHeight - tOffset;
      } else if ($this.css('display') == 'block' || link.tagName.toLowerCase() == 'area' || opts.positionBy == "mouse") {
        tipY = bpY - tOffset;
      } else {
        tipY = posY - opts.dropShadowSteps;
      }
      if (direction == '') {
        posX < linkLeft ? direction = 'left' : direction = 'right';
      }
      $cluetip.css({top: tipY + 'px'}).removeClass().addClass('clue-' + direction + '-' + ctClass).addClass(' cluetip-' + ctClass);
      if (opts.arrows) { // set up arrow positioning to align with element
        var bgY = (posY - tipY - opts.dropShadowSteps);
        $cluetipArrows.css({top: (/(left|right)/.test(direction) && posX >=0 && bgY > 0) ? bgY + 'px' : /(left|right)/.test(direction) ? 0 : ''}).show();
      } else {
        $cluetipArrows.hide();
      }

// (first hide, then) ***SHOW THE CLUETIP***
      $dropShadow.hide();
      $cluetip.hide()[opts.fx.open](opts.fx.openSpeed || 0);
      if (opts.dropShadow) { $dropShadow.css({height: tipHeight, width: tipInnerWidth, zIndex: $this.data('thisInfo').zIndex-1}).show(); }
      if ($.fn.bgiframe) { $cluetip.bgiframe(); }
      // delayed close (not fully tested)
      if (opts.delayedClose > 0) {
        closeOnDelay = setTimeout(cluetipClose, opts.delayedClose);
      }
      // trigger the optional onShow function
      opts.onShow.call(link, $cluetip, $cluetipInner);
    };

/***************************************
   =INACTIVATION
-------------------------------------- */
    var inactivate = function(event) {
      isActive = false;
      $cluetipWait.hide();
      if (!opts.sticky || (/click|toggle/).test(opts.activation) ) {
        cluetipClose();
        clearTimeout(closeOnDelay);        
      }
      if (opts.hoverClass) {
        $this.removeClass(opts.hoverClass);
      }
    };
// close cluetip and reset some things
    var cluetipClose = function() {
      $cluetipOuter 
      .parent().hide().removeClass();
      opts.onHide.call(link, $cluetip, $cluetipInner);
      $this.removeClass('cluetip-clicked');
      if (tipTitle) {
        $this.attr(opts.titleAttribute, tipTitle);
      }
      $this.css('cursor','');
      if (opts.arrows) {
        $cluetipArrows.css({top: ''});
      }
    };

    $(document).bind('hideCluetip', function(e) {
      cluetipClose();
    });
/***************************************
   =BIND EVENTS
-------------------------------------- */
  // activate by click
      if ( (/click|toggle/).test(opts.activation) ) {
        $this.bind('click.cluetip', function(event) {
          if ($cluetip.is(':hidden') || !$this.is('.cluetip-clicked')) {
            activate(event);
            $('.cluetip-clicked').removeClass('cluetip-clicked');
            $this.addClass('cluetip-clicked');
          } else {
            inactivate(event);
          }
          this.blur();
          return false;
        });
  // activate by focus; inactivate by blur    
      } else if (opts.activation == 'focus') {
        $this.bind('focus.cluetip', function(event) {
          activate(event);
        });
        $this.bind('blur.cluetip', function(event) {
          inactivate(event);
        });
  // activate by hover
      } else {
        // clicking is returned false if clickThrough option is set to false
        $this[opts.clickThrough ? 'unbind' : 'bind']('click', returnFalse);
        //set up mouse tracking
        var mouseTracks = function(evt) {
          if (opts.tracking == true) {
            var trackX = posX - evt.pageX;
            var trackY = tipY ? tipY - evt.pageY : posY - evt.pageY;
            $this.bind('mousemove.cluetip', function(evt) {
              $cluetip.css({left: evt.pageX + trackX, top: evt.pageY + trackY });
            });
          }
        };
        if ($.fn.hoverIntent && opts.hoverIntent) {
          $this.hoverIntent({
            sensitivity: opts.hoverIntent.sensitivity,
            interval: opts.hoverIntent.interval,  
            over: function(event) {
              activate(event);
              mouseTracks(event);
            }, 
            timeout: opts.hoverIntent.timeout,  
            out: function(event) {inactivate(event); $this.unbind('mousemove.cluetip');}
          });           
        } else {
          $this.bind('mouseenter.cluetip', function(event) {
            activate(event);
            mouseTracks(event);
          })
          .bind('mouseleave.cluetip', function(event) {
            inactivate(event);
            $this.unbind('mousemove.cluetip');
          });
        }
        $this.bind('mouseover.cluetip', function(event) {
          $this.attr('title','');
        }).bind('mouseleave.cluetip', function(event) {
          $this.attr('title', $this.data('thisInfo').title);
        });
      }
    });
  };
  
/*
 * options for clueTip
 *
 * each one can be explicitly overridden by changing its value. 
 * for example: $.fn.cluetip.defaults.width = 200; 
 * would change the default width for all clueTips to 200. 
 *
 * each one can also be overridden by passing an options map to the cluetip method.
 * for example: $('a.example').cluetip({width: 200}); 
 * would change the default width to 200 for clueTips invoked by a link with class of "example"
 *
 */
  
  $.fn.cluetip.defaults = {  // set up default options
    width:            275,      // The width of the clueTip
    height:           'auto',   // The height of the clueTip
    cluezIndex:       97,       // Sets the z-index style property of the clueTip
    positionBy:       'auto',   // Sets the type of positioning: 'auto', 'mouse','bottomTop', 'fixed'
    topOffset:        15,       // Number of px to offset clueTip from top of invoking element
    leftOffset:       15,       // Number of px to offset clueTip from left of invoking element
    local:            false,    // Whether to use content from the same page for the clueTip's body
    localPrefix:      null,       // string to be prepended to the tip attribute if local is true
    hideLocal:        true,     // If local option is set to true, this determines whether local content
                                // to be shown in clueTip should be hidden at its original location
    attribute:        'rel',    // the attribute to be used for fetching the clueTip's body content
    titleAttribute:   'title',  // the attribute to be used for fetching the clueTip's title
    splitTitle:       '',       // A character used to split the title attribute into the clueTip title and divs
                                // within the clueTip body. more info below [6]
    escapeTitle:      false,    // whether to html escape the title attribute
    showTitle:        true,     // show title bar of the clueTip, even if title attribute not set
    cluetipClass:     'default',// class added to outermost clueTip div in the form of 'cluetip-' + clueTipClass.
    hoverClass:       '',       // class applied to the invoking element onmouseover and removed onmouseout
    waitImage:        true,     // whether to show a "loading" img, which is set in jquery.cluetip.css
    cursor:           'help',
    arrows:           false,    // if true, displays arrow on appropriate side of clueTip
    dropShadow:       true,     // set to false if you don't want the drop-shadow effect on the clueTip
    dropShadowSteps:  6,        // adjusts the size of the drop shadow
    sticky:           false,    // keep visible until manually closed
    mouseOutClose:    false,    // close when clueTip is moused out
    activation:       'hover',  // set to 'click' to force user to click to show clueTip
                                // set to 'focus' to show on focus of a form element and hide on blur
    clickThrough:     false,    // if true, and activation is not 'click', then clicking on link will take user to the link's href,
                                // even if href and tipAttribute are equal
    tracking:         false,    // if true, clueTip will track mouse movement (experimental)
    delayedClose:     0,        // close clueTip on a timed delay (experimental)
    closePosition:    'top',    // location of close text for sticky cluetips; can be 'top' or 'bottom' or 'title'
    closeText:        'Close',  // text (or HTML) to to be clicked to close sticky clueTips
    truncate:         0,        // number of characters to truncate clueTip's contents. if 0, no truncation occurs
    
    // effect and speed for opening clueTips
    fx: {             
                      open:       'show', // can be 'show' or 'slideDown' or 'fadeIn'
                      openSpeed:  ''
    },     

    // settings for when hoverIntent plugin is used             
    hoverIntent: {    
                      sensitivity:  3,
              			  interval:     50,
              			  timeout:      0
    },

    // short-circuit function to run just before clueTip is shown. 
    onActivate:       function(e) {return true;},

    // function to run just after clueTip is shown. 
    onShow:           function(ct, ci){},
    // function to run just after clueTip is hidden.
    onHide:           function(ct, ci){},
    // whether to cache results of ajax request to avoid unnecessary hits to server    
    ajaxCache:        true,  

    // process data retrieved via xhr before it's displayed
    ajaxProcess:      function(data) {
                        data = data.replace(/<(script|style|title)[^<]+<\/(script|style|title)>/gm, '').replace(/<(link|meta)[^>]+>/g,'');
                        return data;
    },                

    // can pass in standard $.ajax() parameters. Callback functions, such as beforeSend, 
    // will be queued first within the default callbacks. 
    // The only exception is error, which overrides the default
    ajaxSettings: {
                      // error: function(ct, ci) { /* override default error callback */ }
                      // beforeSend: function(ct, ci) { /* called first within default beforeSend callback }
                      dataType: 'html'
    },
    debug: false
  };


/*
 * Global defaults for clueTips. Apply to all calls to the clueTip plugin.
 *
 * @example $.cluetip.setup({
 *   insertionType: 'prependTo',
 *   insertionElement: '#container'
 * });
 * 
 * @property
 * @name $.cluetip.setup
 * @type Map
 * @cat Plugins/tooltip
 * @option String insertionType: Default is 'appendTo'. Determines the method to be used for inserting the clueTip into the DOM. Permitted values are 'appendTo', 'prependTo', 'insertBefore', and 'insertAfter'
 * @option String insertionElement: Default is 'body'. Determines which element in the DOM the plugin will reference when inserting the clueTip.
 *
 */
   
  var insertionType = 'appendTo', insertionElement = 'body';

  $.cluetip.setup = function(options) {
    if (options && options.insertionType && (options.insertionType).match(/appendTo|prependTo|insertBefore|insertAfter/)) {
      insertionType = options.insertionType;
    }
    if (options && options.insertionElement) {
      insertionElement = options.insertionElement;
    }
  };
  
})(jQuery);
;/*
 * jquery.layout 1.3.0 - Release Candidate 24
 *
 * Copyright (c) 2010 
 *   Fabrizio Balliano (http://www.fabrizioballiano.net)
 *   Kevin Dalman (http://allpro.net)
 *
 * Dual licensed under the GPL (http://www.gnu.org/licenses/gpl.html)
 * and MIT (http://www.opensource.org/licenses/mit-license.php) licenses.
 *
 * Docs: http://layout.jquery-dev.net/documentation.html
 * Tips: http://layout.jquery-dev.net/tips.html
 * Help: http://groups.google.com/group/jquery-ui-layout
 *
 * $Date: 2010-02-12 08:00:00 -0800 (Fri, 12 Feb 2010) $
 * $Rev: 3024 $
 * 
 * NOTE: For best code readability, view this with a fixed-width font and tabs equal to 4-chars
 */
;(function ($) {

$.fn.layout = function (opts) {

/*
 * ###########################
 *   WIDGET CONFIG & OPTIONS
 * ###########################
 */

	// LANGUAGE CUSTOMIZATION - will be *externally customizable* in next version
	var lang = {
		Pane:		"Pane"
	,	Open:		"Open"	// eg: "Open Pane"
	,	Close:		"Close"
	,	Resize:		"Resize"
	,	Slide:		"Slide Open"
	,	Pin:		"Pin"
	,	Unpin:		"Un-Pin"
	,	selector:	"selector"
	,	msgNoRoom:	"Not enough room to show this pane."
	,	errContainerMissing:	"UI.Layout Initialization Error\n\nThe specified layout-container does not exist."
	,	errContainerHeight:		"UI.Layout Initialization Error\n\nThe layout-container \"CONTAINER\" has no height!"
	,	errButton:				"Error Adding Button \n\nInvalid "
	};

	// DEFAULT OPTIONS - CHANGE IF DESIRED
	var options = {
		name:						""			// Not required, but useful for buttons and used for the state-cookie
	,	scrollToBookmarkOnLoad:		true		// after creating a layout, scroll to bookmark in URL (.../page.htm#myBookmark)
	,	resizeWithWindow:			true		// bind thisLayout.resizeAll() to the window.resize event
	,	resizeWithWindowDelay:		200			// delay calling resizeAll because makes window resizing very jerky
	,	resizeWithWindowMaxDelay:	0			// 0 = none - force resize every XX ms while window is being resized
	,	onresizeall_start:			null		// CALLBACK when resizeAll() STARTS	- NOT pane-specific
	,	onresizeall_end:			null		// CALLBACK when resizeAll() ENDS	- NOT pane-specific
	,	onload:						null		// CALLBACK when Layout inits - after options initialized, but before elements
	,	onunload:					null		// CALLBACK when Layout is destroyed OR onWindowUnload
	,	autoBindCustomButtons:		false		// search for buttons with ui-layout-button class and auto-bind them
	,	zIndex:						null		// the PANE zIndex - resizers and masks will be +1
	//	PANE SETTINGS
	,	defaults: { // default options for 'all panes' - will be overridden by 'per-pane settings'
			applyDemoStyles: 		false		// NOTE: renamed from applyDefaultStyles for clarity
		,	closable:				true		// pane can open & close
		,	resizable:				true		// when open, pane can be resized 
		,	slidable:				true		// when closed, pane can 'slide open' over other panes - closes on mouse-out
		,	initClosed:				false		// true = init pane as 'closed'
		,	initHidden: 			false 		// true = init pane as 'hidden' - no resizer-bar/spacing
		//	SELECTORS
		//,	paneSelector:			""			// MUST be pane-specific - jQuery selector for pane
		,	contentSelector:		".ui-layout-content" // INNER div/element to auto-size so only it scrolls, not the entire pane!
		//	GENERIC ROOT-CLASSES - for auto-generated classNames
		,	paneClass:				"ui-layout-pane"	// border-Pane - default: 'ui-layout-pane'
		,	resizerClass:			"ui-layout-resizer"	// Resizer Bar		- default: 'ui-layout-resizer'
		,	togglerClass:			"ui-layout-toggler"	// Toggler Button	- default: 'ui-layout-toggler'
		,	buttonClass:			"ui-layout-button"	// CUSTOM Buttons	- default: 'ui-layout-button-toggle/-open/-close/-pin'
		//	ELEMENT SIZE & SPACING
		//,	size:					100			// MUST be pane-specific -initial size of pane
		,	minSize:				0			// when manually resizing a pane
		,	maxSize:				0			// ditto, 0 = no limit
		,	spacing_open:			6			// space between pane and adjacent panes - when pane is 'open'
		,	spacing_closed:			6			// ditto - when pane is 'closed'
		,	togglerLength_open:		50			// Length = WIDTH of toggler button on north/south sides - HEIGHT on east/west sides
		,	togglerLength_closed: 	50			// 100% OR -1 means 'full height/width of resizer bar' - 0 means 'hidden'
		,	togglerAlign_open:		"center"	// top/left, bottom/right, center, OR...
		,	togglerAlign_closed:	"center"	// 1 => nn = offset from top/left, -1 => -nn == offset from bottom/right
		,	togglerTip_open:		lang.Close	// Toggler tool-tip (title)
		,	togglerTip_closed:		lang.Open	// ditto
		//	RESIZING OPTIONS
		,	resizerDblClickToggle:	true		// 
		,	noSelectionWhileDragging: true		// set $(document).disableSelection to avoid selecting text while dragging the resizer
		,	autoResize:				true		// IF size is 'auto' or a percentage, then recalc 'pixel size' whenever the layout resizes
		,	autoReopen:				true		// IF a pane was auto-closed due to noRoom, reopen it when there is room? False = leave it closed
		,	resizerDragOpacity:		1			// option for ui.draggable
		//,	resizerCursor:			""			// MUST be pane-specific - cursor when over resizer-bar
		,	maskIframesOnResize:	true		// true = all iframes OR = iframe-selector(s) - adds masking-div during resizing/dragging
		,	resizeWhileDragging:	false		// true = LIVE Resizing as resizer is dragged
		,	resizeContentWhileDragging:	false	// true = re-measure header/footer heights as resizer is dragged
		//	TIPS & MESSAGES - also see lang object
		,	noRoomToOpenTip:		lang.msgNoRoom
		,	resizerTip:				lang.Resize	// Resizer tool-tip (title)
		,	sliderTip:				lang.Slide // resizer-bar triggers 'sliding' when pane is closed
		,	sliderCursor:			"pointer"	// cursor when resizer-bar will trigger 'sliding'
		,	slideTrigger_open:		"click"		// click, dblclick, mouseover
		,	slideTrigger_close:		"mouseout"	// click, mouseout
		,	hideTogglerOnSlide:		false		// when pane is slid-open, should the toggler show?
		,	togglerContent_open:	""			// text or HTML to put INSIDE the toggler
		,	togglerContent_closed:	""			// ditto
		//	HOT-KEYS & MISC
		,	showOverflowOnHover:	false		// will bind allowOverflow() utility to pane.onMouseOver
		,	trackMouseWhenSliding:	false		// true = check isMouseOver to avoid premature slide-closed
		,	enableCursorHotkey:		true		// enabled 'cursor' hotkeys
		//,	customHotkey:			""			// MUST be pane-specific - EITHER a charCode OR a character
		,	customHotkeyModifier:	"SHIFT"		// either 'SHIFT', 'CTRL' or 'CTRL+SHIFT' - NOT 'ALT'
		//	PANE ANIMATION
		//	NOTE: fxSss_open & fxSss_close options (eg: fxName_open) are auto-generated if not passed
		,	fxName:					"slide" 	// ('none' or blank), slide, drop, scale
		,	fxSpeed:				null		// slow, normal, fast, 200, nnn - if passed, will OVERRIDE fxSettings.duration
		,	fxSettings:				{}			// can be passed, eg: { easing: "easeOutBounce", duration: 1500 }
		,	fxOpacityFix:			true		// tries to fix opacity in IE to restore anti-aliasing after animation
		//	CALLBACKS
		,	triggerEventsOnLoad:	false		// true = trigger onopen OR onclose callbacks when layout initializes
		,	triggerEventsWhileDragging: true	// true = trigger onresize callback REPEATEDLY if resizeWhileDragging==true
		,	onshow_start:			null		// CALLBACK when pane STARTS to Show	- BEFORE onopen/onhide_start
		,	onshow_end:				null		// CALLBACK when pane ENDS being Shown	- AFTER  onopen/onhide_end
		,	onhide_start:			null		// CALLBACK when pane STARTS to Close	- BEFORE onclose_start
		,	onhide_end:				null		// CALLBACK when pane ENDS being Closed	- AFTER  onclose_end
		,	onopen_start:			null		// CALLBACK when pane STARTS to Open
		,	onopen_end:				null		// CALLBACK when pane ENDS being Opened
		,	onclose_start:			null		// CALLBACK when pane STARTS to Close
		,	onclose_end:			null		// CALLBACK when pane ENDS being Closed
		,	onresize_start:			null		// CALLBACK when pane STARTS to be ***MANUALLY*** Resized
		,	onresize_end:			null		// CALLBACK when pane ENDS being Resized ***FOR ANY REASON***
		}
	,	north: {
			paneSelector:			".ui-layout-north"
		,	size:					"auto"		// eg: "auto", "30%", 200
		,	resizerCursor:			"n-resize"	// custom = url(myCursor.cur)
		,	customHotkey:			""			// EITHER a charCode OR a character
		}
	,	south: {
			paneSelector:			".ui-layout-south"
		,	size:					"auto"
		,	resizerCursor:			"s-resize"
		,	customHotkey:			""
		}
	,	east: {
			paneSelector:			".ui-layout-east"
		,	size:					200
		,	resizerCursor:			"e-resize"
		,	customHotkey:			""
		}
	,	west: {
			paneSelector:			".ui-layout-west"
		,	size:					200
		,	resizerCursor:			"w-resize"
		,	customHotkey:			""
		}
	,	center: {
			paneSelector:			".ui-layout-center"
		,	minWidth:				0
		,	minHeight:				0
		}

	//	STATE MANAGMENT
	,	useStateCookie:				false		// Enable cookie-based state-management - can fine-tune with cookie.autoLoad/autoSave
	,	cookie: {
			name:					""			// If not specified, will use Layout.name, else just "Layout"
		,	autoSave:				true		// Save a state cookie when page exits?
		,	autoLoad:				true		// Load the state cookie when Layout inits?
		//	Cookie Options
		,	domain:					""
		,	path:					""
		,	expires:				""			// 'days' to keep cookie - leave blank for 'session cookie'
		,	secure:					false
		//	List of options to save in the cookie - must be pane-specific
		,	keys:					"north.size,south.size,east.size,west.size,"+
									"north.isClosed,south.isClosed,east.isClosed,west.isClosed,"+
									"north.isHidden,south.isHidden,east.isHidden,west.isHidden"
		}
	};


	// PREDEFINED EFFECTS / DEFAULTS
	var effects = { // LIST *PREDEFINED EFFECTS* HERE, even if effect has no settings
		slide:	{
			all:	{ duration:  "fast"	} // eg: duration: 1000, easing: "easeOutBounce"
		,	north:	{ direction: "up"	}
		,	south:	{ direction: "down"	}
		,	east:	{ direction: "right"}
		,	west:	{ direction: "left"	}
		}
	,	drop:	{
			all:	{ duration:  "slow"	} // eg: duration: 1000, easing: "easeOutQuint"
		,	north:	{ direction: "up"	}
		,	south:	{ direction: "down"	}
		,	east:	{ direction: "right"}
		,	west:	{ direction: "left"	}
		}
	,	scale:	{
			all:	{ duration:  "fast"	}
		}
	};


	// DYNAMIC DATA - IS READ-ONLY EXTERNALLY!
	var state = {
		// generate unique ID to use for event.namespace so can unbind only events added by 'this layout'
		id:			"layout"+ new Date().getTime()	// code uses alias: sID
	,	initialized: false
	,	container:	{} // init all keys
	,	north:		{}
	,	south:		{}
	,	east:		{}
	,	west:		{}
	,	center:		{}
	,	cookie:		{} // State Managment data storage
	};


	// INTERNAL CONFIG DATA - DO NOT CHANGE THIS!
	var _c = {
		allPanes:		"north,south,west,east,center"
	,	borderPanes:	"north,south,west,east"
	,	altSide: {
			north:	"south"
		,	south:	"north"
		,	east: 	"west"
		,	west: 	"east"
		}
	//	CSS used in multiple places
	,	hidden:  { visibility: "hidden" }
	,	visible: { visibility: "visible" }
	//	layout element settings
	,	zIndex: { // set z-index values here
			pane_normal:	1		// normal z-index for panes
		,	resizer_normal:	2		// normal z-index for resizer-bars
		,	iframe_mask:	2		// overlay div used to mask pane(s) during resizing
		,	pane_sliding:	100		// applied to *BOTH* the pane and its resizer when a pane is 'slid open'
		,	pane_animate:	1000	// applied to the pane when being animated - not applied to the resizer
		,	resizer_drag:	10000	// applied to the CLONED resizer-bar when being 'dragged'
		}
	,	resizers: {
			cssReq: {
				position: 	"absolute"
			,	padding: 	0
			,	margin: 	0
			,	fontSize:	"1px"
			,	textAlign:	"left"	// to counter-act "center" alignment!
			,	overflow: 	"hidden" // prevent toggler-button from overflowing
			//	SEE c.zIndex.resizer_normal
			}
		,	cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
				background: "#DDD"
			,	border:		"none"
			}
		}
	,	togglers: {
			cssReq: {
				position: 	"absolute"
			,	display: 	"block"
			,	padding: 	0
			,	margin: 	0
			,	overflow:	"hidden"
			,	textAlign:	"center"
			,	fontSize:	"1px"
			,	cursor: 	"pointer"
			,	zIndex: 	1
			}
		,	cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
				background: "#AAA"
			}
		}
	,	content: {
			cssReq: {
				position:	"relative" /* contain floated or positioned elements */
			}
		,	cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
				overflow:	"auto"
			,	padding:	"10px"
			}
		,	cssDemoPane: { // DEMO CSS - REMOVE scrolling from 'pane' when it has a content-div
				overflow:	"hidden"
			,	padding:	0
			}
		}
	,	panes: { // defaults for ALL panes - overridden by 'per-pane settings' below
			cssReq: {
				position: 	"absolute"
			,	margin:		0
			//	SEE c.zIndex.pane_normal
			}
		,	cssDemo: { // DEMO CSS - applied if: options.PANE.applyDemoStyles=true
				padding:	"10px"
			,	background:	"#FFF"
			,	border:		"1px solid #BBB"
			,	overflow:	"auto"
			}
		}
	,	north: {
			side:			"Top"
		,	sizeType:		"Height"
		,	dir:			"horz"
		,	cssReq: {
				top: 		0
			,	bottom: 	"auto"
			,	left: 		0
			,	right: 		0
			,	width: 		"auto"
			//	height: 	DYNAMIC
			}
		,	pins:			[]	// array of 'pin buttons' to be auto-updated on open/close (classNames)
		}
	,	south: {
			side:			"Bottom"
		,	sizeType:		"Height"
		,	dir:			"horz"
		,	cssReq: {
				top: 		"auto"
			,	bottom: 	0
			,	left: 		0
			,	right: 		0
			,	width: 		"auto"
			//	height: 	DYNAMIC
			}
		,	pins:			[]
		}
	,	east: {
			side:			"Right"
		,	sizeType:		"Width"
		,	dir:			"vert"
		,	cssReq: {
				left: 		"auto"
			,	right: 		0
			,	top: 		"auto" // DYNAMIC
			,	bottom: 	"auto" // DYNAMIC
			,	height: 	"auto"
			//	width: 		DYNAMIC
			}
		,	pins:			[]
		}
	,	west: {
			side:			"Left"
		,	sizeType:		"Width"
		,	dir:			"vert"
		,	cssReq: {
				left: 		0
			,	right: 		"auto"
			,	top: 		"auto" // DYNAMIC
			,	bottom: 	"auto" // DYNAMIC
			,	height: 	"auto"
			//	width: 		DYNAMIC
			}
		,	pins:			[]
		}
	,	center: {
			dir:			"center"
		,	cssReq: {
				left: 		"auto" // DYNAMIC
			,	right: 		"auto" // DYNAMIC
			,	top: 		"auto" // DYNAMIC
			,	bottom: 	"auto" // DYNAMIC
			,	height: 	"auto"
			,	width: 		"auto"
			}
		}
	//	internal tracking
	,	timers: {}
	};


/*
 * ###########################
 *  INTERNAL HELPER FUNCTIONS
 * ###########################
 */

	/**
	 * min / max
	 *
	 * Aliases for Math methods to simplify coding
	 */
	var min = function (x,y) { return Math.min(x,y); };
	var max = function (x,y) { return Math.max(x,y); };

	/**
	 * _transformData
	 *
	 * Processes the options passed in and transforms them into the format used by layout()
	 * Missing keys are added, and converts the data if passed in 'flat-format' (no sub-keys)
	 * In flat-format, pane-specific-settings are prefixed like: north__optName  (2-underscores)
	 * To update effects, options MUST use nested-keys format, with an effects key ???
	 *
	 * @callers	initOptions()
	 * @params  JSON	d	Data/options passed by user - may be a single level or nested levels
	 * @returns JSON		Creates a data struture that perfectly matches 'options', ready to be imported
	 */
	var _transformData = function (d) {
		var json = { cookie:{}, defaults:{fxSettings:{}}, north:{fxSettings:{}}, south:{fxSettings:{}}, east:{fxSettings:{}}, west:{fxSettings:{}}, center:{fxSettings:{}} };
		d = d || {};
		if (d.effects || d.cookie || d.defaults || d.north || d.south || d.west || d.east || d.center)
			json = $.extend( true, json, d ); // already in json format - add to base keys
		else
			// convert 'flat' to 'nest-keys' format - also handles 'empty' user-options
			$.each( d, function (key,val) {
				a = key.split("__");
				if (!a[1] || json[a[0]]) // check for invalid keys
					json[ a[1] ? a[0] : "defaults" ][ a[1] ? a[1] : a[0] ] = val;
			});
		return json;
	};

	/**
	 * _queue
	 *
	 * Set an INTERNAL callback to avoid simultaneous animation
	 * Runs only if needed and only if all callbacks are not 'already set'
	 * Called by open() and close() when isLayoutBusy=true
	 *
	 * @param String   action  Either 'open' or 'close'
	 * @param String   pane    A valid border-pane name, eg 'west'
	 * @param Boolean  param   Extra param for callback (optional)
	 */
	var _queue = function (action, pane, param) {
		var tried = [];

		// if isLayoutBusy, then some pane must be 'moving'
		$.each(_c.borderPanes.split(","), function (i, p) {
			if (_c[p].isMoving) {
				bindCallback(p); // TRY to bind a callback
				return false;	// BREAK
			}
		});

		// if pane does NOT have a callback, then add one, else follow the callback chain...
		function bindCallback (p) {
			var c = _c[p];
			if (!c.doCallback) {
				c.doCallback = true;
				c.callback = action +","+ pane +","+ (param ? 1 : 0);
			}
			else { // try to 'chain' this callback
				tried.push(p);
				var cbPane = c.callback.split(",")[1]; // 2nd param of callback is 'pane'
				// ensure callback target NOT 'itself' and NOT 'target pane' and NOT already tried (avoid loop)
				if (cbPane != p && cbPane != pane && !$.inArray(p, tried))
					bindCallback(cbPane); // RECURSE
			}
		}
	};

	/**
	 * _dequeue
	 *
	 * RUN the INTERNAL callback for this pane - if one exists
	 *
	 * @param String   action  Either 'open' or 'close'
	 * @param String   pane    A valid border-pane name, eg 'west'
	 * @param Boolean  param   Extra param for callback (optional)
	 */
	var _dequeue = function (pane) {
		var c = _c[pane];

		// RESET flow-control flags
		_c.isLayoutBusy = false;
		delete c.isMoving;
		if (!c.doCallback || !c.callback) return;

		c.doCallback = false; // RESET logic flag

		// EXECUTE the callback
		var
			cb = c.callback.split(",")
		,	param = (cb[2] > 0 ? true : false)
		;
		if (cb[0] == "open")
			open( cb[1], param  );
		else if (cb[0] == "close")
			close( cb[1], param );

		if (!c.doCallback) c.callback = null; // RESET - unless callback above enabled it again!
	};

	/**
	 * _execCallback
	 *
	 * Executes a Callback function after a trigger event, like resize, open or close
	 *
	 * @param String  pane   This is passed only so we can pass the 'pane object' to the callback
	 * @param String  v_fn  Accepts a function name, OR a comma-delimited array: [0]=function name, [1]=argument
	 */
	var _execCallback = function (pane, v_fn) {
		if (!v_fn) return;
		var fn;
		try {
			if (typeof v_fn == "function")
				fn = v_fn;	
			else if (typeof v_fn != "string")
				return;
			else if (v_fn.match(/,/)) {
				// function name cannot contain a comma, so must be a function name AND a 'name' parameter
				var
					args = v_fn.split(",")
				,	fn = eval(args[0])
				;
				if (typeof fn=="function" && args.length > 1)
					return fn(args[1]); // pass the argument parsed from 'list'
			}
			else // just the name of an external function?
				fn = eval(v_fn);

			if (typeof fn=="function") {
				if (pane && $Ps[pane])
					// pass data: pane-name, pane-element, pane-state (copy), pane-options, and layout-name
					return fn( pane, $Ps[pane], $.extend({},state[pane]), options[pane], options.name );
				else // must be a layout/container callback - pass suitable info
					return fn( Instance, $.extend({},state), options, options.name );
			}
		}
		catch (ex) {}
	};

	/**
	 * _showInvisibly
	 *
	 * Returns hash container 'display' and 'visibility'
	 *
	 * @TODO: SEE $.swap() - swaps CSS, runs callback, resets CSS
	 */
	var _showInvisibly = function ($E, force) {
		if (!$E) return {};
		if (!$E.jquery) $E = $($E);
		var CSS = {
			display:	$E.css('display')
		,	visibility:	$E.css('visibility')
		};
		if (force || CSS.display == "none") { // only if not *already hidden*
			$E.css({ display: "block", visibility: "hidden" }); // show element 'invisibly' so can be measured
			return CSS;
		}
		else return {};
	};

	/**
	 * _fixIframe
	 *
	 * cure iframe display issues in IE & other browsers
	 */
	var _fixIframe = function (pane) {
		if (state.browser.mozilla) return; // skip FireFox - it auto-refreshes iframes onShow
		var $P = $Ps[pane];
		// if the 'pane' is an iframe, do it
		if (state[pane].tagName == "IFRAME")
			$P.css(_c.hidden).css(_c.visible); 
		else // ditto for any iframes INSIDE the pane
			$P.find('IFRAME').css(_c.hidden).css(_c.visible);
	};

	/**
	 * _cssNum
	 *
	 * Returns the 'current CSS numeric value' for an element - returns 0 if property does not exist
	 *
	 * @callers  Called by many methods
	 * @param jQuery  $Elem  Must pass a jQuery object - first element is processed
	 * @param String  property  The name of the CSS property, eg: top, width, etc.
	 * @returns Variant  Usually is used to get an integer value for position (top, left) or size (height, width)
	 */
	var _cssNum = function ($E, prop) {
		if (!$E.jquery) $E = $($E);
		var CSS = _showInvisibly($E);
		var val = parseInt($.curCSS($E[0], prop, true), 10) || 0;
		$E.css( CSS ); // RESET
		return val;
	};

	var _borderWidth = function (E, side) {
		if (E.jquery) E = E[0];
		var b = "border"+ side.substr(0,1).toUpperCase() + side.substr(1); // left => Left
		return $.curCSS(E, b+"Style", true) == "none" ? 0 : (parseInt($.curCSS(E, b+"Width", true), 10) || 0);
	};

	/**
	 * cssW / cssH / cssSize / cssMinDims
	 *
	 * Contains logic to check boxModel & browser, and return the correct width/height for the current browser/doctype
	 *
	 * @callers  initPanes(), sizeMidPanes(), initHandles(), sizeHandles()
	 * @param Variant  el  Can accept a 'pane' (east, west, etc) OR a DOM object OR a jQuery object
	 * @param Integer  outerWidth/outerHeight  (optional) Can pass a width, allowing calculations BEFORE element is resized
	 * @returns Integer  Returns the innerWidth/Height of the elem by subtracting padding and borders
	 *
	 * @TODO  May need additional logic for other browser/doctype variations? Maybe use more jQuery methods?
	 */
	var cssW = function (el, outerWidth) {
		var
			str	= typeof el == "string"
		,	$E	= str ? $Ps[el] : $(el)
		;
		if (isNaN(outerWidth)) // not specified
			outerWidth = str ? getPaneSize(el) : $E.outerWidth();

		// a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
		if (outerWidth <= 0) return 0;

		if (!state.browser.boxModel) return outerWidth;

		// strip border and padding from outerWidth to get CSS Width
		var W = outerWidth
			- _borderWidth($E, "Left")
			- _borderWidth($E, "Right")
			- _cssNum($E, "paddingLeft")		
			- _cssNum($E, "paddingRight")
		;

		return W > 0 ? W : 0;
	};

	var cssH = function (el, outerHeight) {
		var
			str	= typeof el == "string"
		,	$E	= str ? $Ps[el] : $(el)
		;
		if (isNaN(outerHeight)) // not specified
			outerHeight = str ? getPaneSize(el) : $E.outerHeight();

		// a 'calculated' outerHeight can be passed so borders and/or padding are removed if needed
		if (outerHeight <= 0) return 0;

		if (!state.browser.boxModel) return outerHeight;

		// strip border and padding from outerHeight to get CSS Height
		var H = outerHeight
			- _borderWidth($E, "Top")
			- _borderWidth($E, "Bottom")
			- _cssNum($E, "paddingTop")
			- _cssNum($E, "paddingBottom")
		;

		return H > 0 ? H : 0;
	};

	var cssSize = function (pane, outerSize) {
		if (_c[pane].dir=="horz") // pane = north or south
			return cssH(pane, outerSize);
		else // pane = east or west
			return cssW(pane, outerSize);
	};

	var cssMinDims = function (pane) {
		// minWidth/Height means CSS width/height = 1px
		var
			dir = _c[pane].dir
		,	d = {
				minWidth:	1001 - cssW(pane, 1000)
			,	minHeight:	1001 - cssH(pane, 1000)
			}
		;
		if (dir == "horz") d.minSize = d.minHeight;
		if (dir == "vert") d.minSize = d.minWidth;
		return d;
	};

	// TODO: see if these methods can be made more useful...
	// TODO: *maybe* return cssW/H from these so caller can use this info

	var setOuterWidth = function (el, outerWidth, autoHide) {
		var $E = el, w;
		if (typeof el == "string") $E = $Ps[el]; // west
		else if (!el.jquery) $E = $(el);
		w = cssW($E, outerWidth);
		$E.css({ width: w });
		if (w > 0) {
			if (autoHide && $E.data('autoHidden') && $E.innerHeight() > 0) {
				$E.show().data('autoHidden', false);
				if (!state.browser.mozilla) // FireFox refreshes iframes - IE doesn't
					// make hidden, then visible to 'refresh' display after animation
					$E.css(_c.hidden).css(_c.visible);
			}
		}
		else if (autoHide && !$E.data('autoHidden'))
			$E.hide().data('autoHidden', true);
	};

	var setOuterHeight = function (el, outerHeight, autoHide) {
		var $E = el;
		if (typeof el == "string") $E = $Ps[el]; // west
		else if (!el.jquery) $E = $(el);
		h = cssH($E, outerHeight);
		$E.css({ height: h, visibility: "visible" }); // may have been 'hidden' by sizeContent
		if (h > 0 && $E.innerWidth() > 0) {
			if (autoHide && $E.data('autoHidden')) {
				$E.show().data('autoHidden', false);
				if (!state.browser.mozilla) // FireFox refreshes iframes - IE doesn't
					$E.css(_c.hidden).css(_c.visible);
			}
		}
		else if (autoHide && !$E.data('autoHidden'))
			$E.hide().data('autoHidden', true);
	};

	var setOuterSize = function (el, outerSize, autoHide) {
		if (_c[pane].dir=="horz") // pane = north or south
			setOuterHeight(el, outerSize, autoHide);
		else // pane = east or west
			setOuterWidth(el, outerSize, autoHide);
	};


	/**
	 * _parseSize
	 *
	 * Converts any 'size' params to a pixel/integer size, if not already
	 * If 'auto' or a decimal/percentage is passed as 'size', a pixel-size is calculated
	 *
	 * @returns Integer
	 */
	var _parseSize = function (pane, size, dir) {
		if (!dir) dir = _c[pane].dir;

		if (typeof size=='string' && size.match(/%/))
			size = parseInt(size) / 100; // convert % to decimal

		if (size === 0)
			return 0;
		else if (size >= 1)
			return parseInt(size,10);
		else if (size > 0) { // percentage, eg: .25
			var o = options, avail;
			if (dir=="horz") // north or south or center.minHeight
				avail = sC.innerHeight - ($Ps.north ? o.north.spacing_open : 0) - ($Ps.south ? o.south.spacing_open : 0);
			else if (dir=="vert") // east or west or center.minWidth
				avail = sC.innerWidth - ($Ps.west ? o.west.spacing_open : 0) - ($Ps.east ? o.east.spacing_open : 0);
			return Math.floor(avail * size);
		}
		else if (pane=="center")
			return 0;
		else { // size < 0 || size=='auto' || size==Missing || size==Invalid
			// auto-size the pane
			var
				$P	= $Ps[pane]
			,	dim	= (dir == "horz" ? "height" : "width")
			,	vis	= _showInvisibly($P) // show pane invisibly if hidden
			,	s	= $P.css(dim); // SAVE current size
			;
			$P.css(dim, "auto");
			size = (dim == "height") ? $P.outerHeight() : $P.outerWidth(); // MEASURE
			$P.css(dim, s).css(vis); // RESET size & visibility
			return size;
		}
	};

	/**
	 * getPaneSize
	 *
	 * Calculates current 'size' (outer-width or outer-height) of a border-pane - optionally with 'pane-spacing' added
	 *
	 * @returns Integer  Returns EITHER Width for east/west panes OR Height for north/south panes - adjusted for boxModel & browser
	 */
	var getPaneSize = function (pane, inclSpace) {
		var 
			$P	= $Ps[pane]
		,	o	= options[pane]
		,	s	= state[pane]
		,	oSp	= (inclSpace ? o.spacing_open : 0)
		,	cSp	= (inclSpace ? o.spacing_closed : 0)
		;
		if (!$P || s.isHidden)
			return 0;
		else if (s.isClosed || (s.isSliding && inclSpace))
			return cSp;
		else if (_c[pane].dir == "horz")
			return $P.outerHeight() + oSp;
		else // dir == "vert"
			return $P.outerWidth() + oSp;
	};

	/**
	 * setSizeLimits
	 *
	 * Calculate min/max pane dimensions and limits for resizing
	 */
	var setSizeLimits = function (pane, slide) {
		var 
			o				= options[pane]
		,	s				= state[pane]
		,	c				= _c[pane]
		,	dir				= c.dir
		,	side			= c.side.toLowerCase()
		,	type			= c.sizeType.toLowerCase()
		,	isSliding		= (slide != undefined ? slide : s.isSliding) // only open() passes 'slide' param
		,	$P				= $Ps[pane]
		,	paneSpacing		= o.spacing_open
		//	measure the pane on the *opposite side* from this pane
		,	altPane			= _c.altSide[pane]
		,	altS			= state[altPane]
		,	$altP			= $Ps[altPane]
		,	altPaneSize		= (!$altP || altS.isVisible===false || altS.isSliding ? 0 : (dir=="horz" ? $altP.outerHeight() : $altP.outerWidth()))
		,	altPaneSpacing	= ((!$altP || altS.isHidden ? 0 : options[altPane][ altS.isClosed !== false ? "spacing_closed" : "spacing_open" ]) || 0)
		//	limitSize prevents this pane from 'overlapping' opposite pane
		,	containerSize	= (dir=="horz" ? sC.innerHeight : sC.innerWidth)
		,	minCenterDims	= cssMinDims("center")
		,	minCenterSize	= dir=="horz" ? max(options.center.minHeight, minCenterDims.minHeight) : max(options.center.minWidth, minCenterDims.minWidth)
		//	if pane is 'sliding', then ignore center and alt-pane sizes - because 'overlays' them
		,	limitSize		= (containerSize - paneSpacing - (isSliding ? 0 : (_parseSize("center", minCenterSize, dir) + altPaneSize + altPaneSpacing)))
		,	minSize			= s.minSize = max( _parseSize(pane, o.minSize), cssMinDims(pane).minSize )
		,	maxSize			= s.maxSize = min( (o.maxSize ? _parseSize(pane, o.maxSize) : 100000), limitSize )
		,	r				= s.resizerPosition = {} // used to set resizing limits
		,	top				= sC.insetTop
		,	left			= sC.insetLeft
		,	W				= sC.innerWidth
		,	H				= sC.innerHeight
		,	rW				= o.spacing_open // subtract resizer-width to get top/left position for south/east
		;
		switch (pane) {
			case "north":	r.min = top + minSize;
							r.max = top + maxSize;
							break;
			case "west":	r.min = left + minSize;
							r.max = left + maxSize;
							break;
			case "south":	r.min = top + H - maxSize - rW;
							r.max = top + H - minSize - rW;
							break;
			case "east":	r.min = left + W - maxSize - rW;
							r.max = left + W - minSize - rW;
							break;
		};
	};

	/**
	 * calcNewCenterPaneDims
	 *
	 * Returns data for setting the size/position of center pane. Also used to set Height for east/west panes
	 *
	 * @returns JSON  Returns a hash of all dimensions: top, bottom, left, right, (outer) width and (outer) height
	 */
	var calcNewCenterPaneDims = function () {
		var d = {
			top:	getPaneSize("north", true) // true = include 'spacing' value for pane
		,	bottom:	getPaneSize("south", true)
		,	left:	getPaneSize("west", true)
		,	right:	getPaneSize("east", true)
		,	width:	0
		,	height:	0
		};

		with (d) { // NOTE: sC = state.container
			// calc center-pane's outer dimensions
			width	= sC.innerWidth - left - right;  // outerWidth
			height	= sC.innerHeight - bottom - top; // outerHeight
			// add the 'container border/padding' to get final positions relative to the container
			top		+= sC.insetTop;
			bottom	+= sC.insetBottom;
			left	+= sC.insetLeft;
			right	+= sC.insetRight;
		}

		return d;
	};


	/**
	 * getElemDims
	 *
	 * Returns data for setting size of an element (container or a pane).
	 *
	 * @callers  create(), onWindowResize() for container, plus others for pane
	 * @returns JSON  Returns a hash of all dimensions: top, bottom, left, right, outerWidth, innerHeight, etc
	 */
	var getElemDims = function ($E) {
		var
			d	= {}			// dimensions hash
		,	x	= d.css = {}	// CSS hash
		,	i	= {}			// TEMP insets
		,	b, p				// TEMP border, padding
		,	off = $E.offset()
		;
		d.offsetLeft = off.left;
		d.offsetTop  = off.top;

		$.each("Left,Right,Top,Bottom".split(","), function (idx, e) {
			b = x["border" + e] = _borderWidth($E, e);
			p = x["padding"+ e] = _cssNum($E, "padding"+e);
			i[e] = b + p; // total offset of content from outer side
			d["inset"+ e] = p;
			/* WRONG ???
			// if BOX MODEL, then 'position' = PADDING (ignore borderWidth)
			if ($E == $Container)
				d["inset"+ e] = (state.browser.boxModel ? p : 0); 
			*/
		});

		d.offsetWidth	= $E.innerWidth(true); // true=include Padding
		d.offsetHeight	= $E.innerHeight(true);
		d.outerWidth	= $E.outerWidth();
		d.outerHeight	= $E.outerHeight();
		d.innerWidth	= d.outerWidth  - i.Left - i.Right;
		d.innerHeight	= d.outerHeight - i.Top  - i.Bottom;

		// TESTING
		x.width  = $E.width();
		x.height = $E.height();

		return d;
	};

	var getElemCSS = function ($E, list) {
		var
			CSS	= {}
		,	style	= $E[0].style
		,	props	= list.split(",")
		,	sides	= "Top,Bottom,Left,Right".split(",")
		,	attrs	= "Color,Style,Width".split(",")
		,	p, s, a, i, j, k
		;
		for (i=0; i < props.length; i++) {
			p = props[i];
			if (p.match(/(border|padding|margin)$/))
				for (j=0; j < 4; j++) {
					s = sides[j];
					if (p == "border")
						for (k=0; k < 3; k++) {
							a = attrs[k];
							CSS[p+s+a] = style[p+s+a];
						}
					else
						CSS[p+s] = style[p+s];
				}
			else
				CSS[p] = style[p];
		};
		return CSS
	};


	var setTimer = function (name, fn, ms) {
		clearTimer(name); // clear previous timer if exists
		_c.timers[name] = setTimeout(fn, ms);
	};

	var clearTimer = function (name) {
		if (_c.timers[name]) {
			clearTimeout(_c.timers[name]);
			delete _c.timers[name];
		}
	};

	var isTimerRunning = function (name) {
		return !!_c.timers[name];
	}

	var getHoverClasses = function (el, allStates) {
		var
			$El		= $(el)
		,	type	= $El.data("layoutRole")
		,	pane	= $El.data("layoutEdge")
		,	o		= options[pane]
		,	root	= o[type +"Class"]
		,	_pane	= "-"+ pane // eg: "-west"
		,	_open	= "-open"
		,	_closed	= "-closed"
		,	_slide	= "-sliding"
		,	_hover	= "-hover " // NOTE the trailing space
		,	_state	= $El.hasClass(root+_closed) ? _closed : _open
		,	_alt	= _state == _closed ? _open : _closed
		,	classes = (root+_hover) + (root+_pane+_hover) + (root+_state+_hover) + (root+_pane+_state+_hover)
		;
		if (allStates) // when 'removing' classes, also remove alternate-state classes
			classes += (root+_alt+_hover) + (root+_pane+_alt+_hover);

		if (type=="resizer" && $El.hasClass(root+_slide))
			classes += (root+_slide+_hover) + (root+_pane+_slide+_hover);

		return $.trim(classes);
	};
	var addHover	= function (evt, el) {
		var e = el || this;
		$(e).addClass( getHoverClasses(e) );
		//if (evt && $(e).data("layoutRole") == "toggler") evt.stopPropagation();
	};
	var removeHover	= function (evt, el) {
		var e = el || this;
		$(e).removeClass( getHoverClasses(e, true) );
	};

/*
 * ###########################
 *   INITIALIZATION METHODS
 * ###########################
 */

	/**
	 * create
	 *
	 * Initialize the layout - called automatically whenever an instance of layout is created
	 *
	 * @callers  NEVER explicity called
	 * @returns  An object pointer to the instance created
	 */
	var create = function () {
		// initialize config/options
		initOptions();
		var o = options;

		// onload will CANCEL resizing if returns false
		if (false === _execCallback(null, o.onload)) return false;

		// update options with saved state, if option enabled
		if (o.useStateCookie && o.cookie.autoLoad)
			loadCookie(); // Update options from state-cookie

		// set environment - can update code here if $.browser is phased out
		state.browser = {
			mozilla:	$.browser.mozilla
		,	msie:		$.browser.msie
		,	isIE6:		$.browser.msie && $.browser.version == 6
		,	boxModel:	$.support.boxModel
		//,	version:	$.browser.version - not used
		};

		// initialize all layout elements
		initContainer();	// set CSS as needed and init state.container dimensions
		initPanes();		// size & position all panes - calls initHandles()
		//initHandles();	// create and position all resize bars & togglers buttons
		initResizable();	// activate resizing on all panes where resizable=true
		sizeContent("all");	// AFTER panes & handles have been initialized, size 'content' divs

		if (o.scrollToBookmarkOnLoad)
			with (self.location) if (hash) replace( hash ); // scrollTo Bookmark

		// search for and bind custom-buttons
		if (o.autoBindCustomButtons) initButtons();

		// bind hotkey function - keyDown - if required
		initHotkeys();
		// track mouse position so we can use it anytime we need it
		initMouseTracking();

		// bind resizeAll() for 'this layout instance' to window.resize event
		if (o.resizeWithWindow && !$Container.data("layoutRole")) // skip if 'nested' inside a pane
			$(window).bind("resize."+ sID, windowResize);

		// bind window.onunload
		$(window).bind("unload."+ sID, unload);

		state.initialized = true;
	};

	var windowResize = function () {
		var delay = Number(options.resizeWithWindowDelay) || 100; // there MUST be some delay!
		if (delay > 0) {
			// resizing uses a delay-loop because the resize event fires repeatly - except in FF, but delay anyway
			clearTimer("winResize"); // if already running
			setTimer("winResize", function(){ clearTimer("winResize"); clearTimer("winResizeRepeater"); resizeAll(); }, delay);
			// ALSO set fixed-delay timer, if not already running
			if (!_c.timers["winResizeRepeater"]) setWindowResizeRepeater();
		}
	};

	var setWindowResizeRepeater = function () {
		var delay = Number(options.resizeWithWindowMaxDelay);
		if (delay > 0)
			setTimer("winResizeRepeater", function(){ setWindowResizeRepeater(); resizeAll(); }, delay);
	};

	var unload = function () {
		var o = options;
		state.cookie = getState(); // save state in case onunload has custom state-management
		if (o.useStateCookie && o.cookie.autoSave) saveCookie();

		_execCallback(null, o.onunload);
	};

	/**
	 *	initMouseTracking / trackMouse / isMouseOver
	 *
	 *	Bound to document.mousemove - updates window.mouseCoords.X/Y
	 *
	 *	TODO: use ui.isOver(y, x, top, left, height, width)
	 */
	var initMouseTracking = function () {
		if (!window.mouseCoords) { // only need 1 mouse tracker!
			window.mouseCoords = { X: 0, Y: 0 }; // init
			$(document).bind("mousemove."+ sID, trackMouse);
		}
	};
	var trackMouse = function (evt) {
		var m = window.mouseCoords;
		m.X = evt.pageX;
		m.Y = evt.pageY;
	};
	var isMouseOver = function (el) {
		var $E	= (typeof el == "string" && $Ps[el]) ? $Ps[el] : $(el);
		if (!$E.length) return false;
		var
			_	= this
		,	d	= $E.offset()
		,	T	= d.top
		,	L	= d.left
		,	R	= L + $E.outerWidth()
		,	B	= T + $E.outerHeight()
		,	m	= window.mouseCoords
		;
		return ((m.X >= L && m.X <= R) && (m.Y >= T && m.Y <= B));
	};


	/**
	 * initContainer
	 *
	 * Validate and initialize container CSS and events
	 *
	 * @callers  create()
	 */
	var initContainer = function () {
			sC.tagName	= $Container.attr("tagName");
		var
			isFullPage	= (sC.tagName == "BODY")
		,	$C		= $Container // alias
		,	props	= "position,margin,padding,border"
		,	CSS		= {}
		;

		// the layoutContainer key is used to store the unique layoutID
		$C
			.data("layoutContainer", sID)		// unique identifier for internal use
			.data("layoutName", options.name)	// add user's layout-name - even if blank!
		;

		// SAVE original container CSS for use in destroy()
		if (!$C.data("layoutCSS")) {
			// handle props like overflow different for BODY & HTML - has 'system default' values
			if (isFullPage) {
				CSS = $.extend( getElemCSS($C, props), {
					height:		$C.css("height")
				,	overflow:	$C.css("overflow")
				,	overflowX:	$C.css("overflowX")
				,	overflowY:	$C.css("overflowY")
				});
				// ALSO SAVE <HTML> CSS
				var $H = $("html");
				$H.data("layoutCSS", {
					height:		"auto" // FF would return a fixed px-size!
				,	overflow:	$H.css("overflow")
				,	overflowX:	$H.css("overflowX")
				,	overflowY:	$H.css("overflowY")
				});
			}
			else // handle props normally for non-body elements
				CSS = getElemCSS($C, props+",top,bottom,left,right,width,height,overflow,overflowX,overflowY");

			$C.data("layoutCSS", CSS);
		}

		try { // format html/body if this is a full page layout
			if (isFullPage) {
				$("html").css({
					height:		"100%"
				,	overflow:	"hidden"
				,	overflowX:	"hidden"
				,	overflowY:	"hidden"
				});
				$("body").css({
					position:	"relative"
				,	height:		"100%"
				,	overflow:	"hidden"
				,	overflowX:	"hidden"
				,	overflowY:	"hidden"
				,	margin:		0
				,	padding:	0		// TODO: test whether body-padding could be handled?
				,	border:		"none"	// a body-border creates problems because it cannot be measured!
				});
			}
			else { // set required CSS for overflow and position
				var
					CSS	= { overflow: "hidden" } // make sure container will not 'scroll'
				,	p	= $C.css("position")
				,	h	= $C.css("height")
				;
				// if this is a NESTED layout, then container/outer-pane ALREADY has position and height
				if (!$C.data("layoutRole")) {
					if (!p || !p.match(/fixed|absolute|relative/))
						CSS.position = "relative"; // container MUST have a 'position'
					if (!h || h=="auto")
						CSS.height = "100%"; // container MUST have a 'height'
				}
				$C.css( CSS );
				if ($C.is(":visible") && $C.innerHeight() < 2)
					alert( lang.errContainerHeight.replace(/CONTAINER/, $C[0].tagName + ($C.selector || '')) );
			}
		} catch (ex) {}

		// set current layout-container dimensions
		$.extend(state.container, getElemDims( $C ));
	};

	/**
	 * initHotkeys
	 *
	 * Bind layout hotkeys - if options enabled
	 *
	 * @callers  create()
	 */
	var initHotkeys = function () {
		// bind keyDown to capture hotkeys, if option enabled for ANY pane
		$.each(_c.borderPanes.split(","), function (i, pane) {
			var o = options[pane];
			if (o.enableCursorHotkey || o.customHotkey) {
				$(document).bind("keydown."+ sID, keyDown); // only need to bind this ONCE
				return false; // BREAK - binding was done
			}
		});
	};

	/**
	 * initOptions
	 *
	 * Build final OPTIONS data
	 *
	 * @callers  create()
	 */
	var initOptions = function () {
		// simplify logic by making sure passed 'opts' var has basic keys
		opts = _transformData( opts );

		// TODO: create a compatibility add-on for new UI widget that will transform old option syntax
		var newOpts = {
			applyDefaultStyles:		"applyDemoStyles"
		};
		renameOpts(opts.defaults);
		$.each(_c.allPanes.split(","), function (i, pane) {
			renameOpts(opts[pane]);
		});

		// update default effects, if case user passed key
		if (opts.effects) {
			$.extend( effects, opts.effects );
			delete opts.effects;
		}
		$.extend( options.cookie, opts.cookie );

		// see if any 'global options' were specified
		var globals = "name,zIndex,scrollToBookmarkOnLoad,resizeWithWindow,resizeWithWindowDelay,resizeWithWindowMaxDelay,"+
			"onresizeall,onresizeall_start,onresizeall_end,onload,onunload,autoBindCustomButtons,useStateCookie";
		$.each(globals.split(","), function (i, key) {
			if (opts[key] !== undefined)
				options[key] = opts[key];
			else if (opts.defaults[key] !== undefined) {
				options[key] = opts.defaults[key];
				delete opts.defaults[key];
			}
		});

		// remove any 'defaults' that MUST be set 'per-pane'
		$.each("paneSelector,resizerCursor,customHotkey".split(","),
			function (i, key) { delete opts.defaults[key]; } // is OK if key does not exist
		);

		// now update options.defaults
		$.extend( true, options.defaults, opts.defaults );

		// merge config for 'center-pane' - border-panes handled in the loop below
		_c.center = $.extend( true, {}, _c.panes, _c.center );
		// update config.zIndex values if zIndex option specified
		var z = options.zIndex;
		if (z === 0 || z > 0) {
			_c.zIndex.pane_normal		= z;
			_c.zIndex.resizer_normal	= z+1;
			_c.zIndex.iframe_mask		= z+1;
		}

		// merge options for 'center-pane' - border-panes handled in the loop below
		$.extend( options.center, opts.center );
		// Most 'default options' do not apply to 'center', so add only those that DO
		var o_Center = $.extend( true, {}, options.defaults, opts.defaults, options.center ); // TEMP data
		$.each("paneClass,contentSelector,applyDemoStyles,showOverflowOnHover,triggerEventsOnLoad".split(","),
			function (i, key) { options.center[key] = o_Center[key]; }
		);

		var o, defs = options.defaults;

		// create a COMPLETE set of options for EACH border-pane
		$.each(_c.borderPanes.split(","), function (i, pane) {

			// apply 'pane-defaults' to CONFIG.[PANE]
			_c[pane] = $.extend( true, {}, _c.panes, _c[pane] );

			// apply 'pane-defaults' +  user-options to OPTIONS.PANE
			o = options[pane] = $.extend( true, {}, options.defaults, options[pane], opts.defaults, opts[pane] );

			// make sure we have base-classes
			if (!o.paneClass)		o.paneClass		= "ui-layout-pane";
			if (!o.resizerClass)	o.resizerClass	= "ui-layout-resizer";
			if (!o.togglerClass)	o.togglerClass	= "ui-layout-toggler";

			// create FINAL fx options for each pane, ie: options.PANE.fxName/fxSpeed/fxSettings[_open|_close]
			$.each(["_open","_close",""], function (i,n) { 
				var
					sName		= "fxName"+n
				,	sSpeed		= "fxSpeed"+n
				,	sSettings	= "fxSettings"+n
				;
				// recalculate fxName according to specificity rules
				o[sName] =
					opts[pane][sName]		// opts.west.fxName_open
				||	opts[pane].fxName		// opts.west.fxName
				||	opts.defaults[sName]	// opts.defaults.fxName_open
				||	opts.defaults.fxName	// opts.defaults.fxName
				||	o[sName]				// options.west.fxName_open
				||	o.fxName				// options.west.fxName
				||	defs[sName]				// options.defaults.fxName_open
				||	defs.fxName				// options.defaults.fxName
				||	"none"
				;
				// validate fxName to be sure is a valid effect
				var fxName = o[sName];
				if (fxName == "none" || !$.effects || !$.effects[fxName] || (!effects[fxName] && !o[sSettings] && !o.fxSettings))
					fxName = o[sName] = "none"; // effect not loaded, OR undefined FX AND fxSettings not passed
				// set vars for effects subkeys to simplify logic
				var
					fx = effects[fxName]	|| {} // effects.slide
				,	fx_all	= fx.all		|| {} // effects.slide.all
				,	fx_pane	= fx[pane]		|| {} // effects.slide.west
				;
				// RECREATE the fxSettings[_open|_close] keys using specificity rules
				o[sSettings] = $.extend(
					{}
				,	fx_all						// effects.slide.all
				,	fx_pane						// effects.slide.west
				,	defs.fxSettings || {}		// options.defaults.fxSettings
				,	defs[sSettings] || {}		// options.defaults.fxSettings_open
				,	o.fxSettings				// options.west.fxSettings
				,	o[sSettings]				// options.west.fxSettings_open
				,	opts.defaults.fxSettings	// opts.defaults.fxSettings
				,	opts.defaults[sSettings] || {} // opts.defaults.fxSettings_open
				,	opts[pane].fxSettings		// opts.west.fxSettings
				,	opts[pane][sSettings] || {}	// opts.west.fxSettings_open
				);
				// recalculate fxSpeed according to specificity rules
				o[sSpeed] =
					opts[pane][sSpeed]		// opts.west.fxSpeed_open
				||	opts[pane].fxSpeed		// opts.west.fxSpeed (pane-default)
				||	opts.defaults[sSpeed]	// opts.defaults.fxSpeed_open
				||	opts.defaults.fxSpeed	// opts.defaults.fxSpeed
				||	o[sSpeed]				// options.west.fxSpeed_open
				||	o[sSettings].duration	// options.west.fxSettings_open.duration
				||	o.fxSpeed				// options.west.fxSpeed
				||	o.fxSettings.duration	// options.west.fxSettings.duration
				||	defs.fxSpeed			// options.defaults.fxSpeed
				||	defs.fxSettings.duration// options.defaults.fxSettings.duration
				||	fx_pane.duration		// effects.slide.west.duration
				||	fx_all.duration			// effects.slide.all.duration
				||	"normal"				// DEFAULT
				;
			});

		});

		function renameOpts (O) {
			for (var key in newOpts) {
				if (O[key] != undefined) {
					O[newOpts[key]] = O[key];
					delete O[key];
				}
			}
		}
	};

	/**
	 * initPanes
	 *
	 * Initialize module objects, styling, size and position for all panes
	 *
	 * @callers  create()
	 */
	var initPanes = function () {
		// NOTE: do north & south FIRST so we can measure their height - do center LAST
		$.each(_c.allPanes.split(","), function (idx, pane) {
			var
				o		= options[pane]
			,	s		= state[pane]
			,	c		= _c[pane]
			,	fx		= s.fx
			,	dir		= c.dir
			,	sel		= o.paneSelector
			,	spacing	= o.spacing_open || 0
			,	isCenter = (pane == "center")
			,	CSS		= {}
			,	$P, $C
			,	size, minSize, maxSize
			;
			$Cs[pane] = false; // init

			if (sel.substr(0,1)==="#") // ID selector
				// NOTE: elements selected 'by ID' DO NOT have to be 'children'
				$P = $Ps[pane] = $Container.find(sel+":first");
			else { // class or other selector
				$P = $Ps[pane] = $Container.children(sel+":first");
				// look for the pane nested inside a 'form' element
				if (!$P.length) $P = $Ps[pane] = $Container.children("form:first").children(sel+":first");
			}

			if (!$P.length) {
				$Ps[pane] = false; // logic
				return true; // SKIP to next
			}

			// SAVE original Pane CSS
			if (!$P.data("layoutCSS")) {
				var props = "position,top,left,bottom,right,width,height,overflow,zIndex,display,backgroundColor,padding,margin,border";
				$P.data("layoutCSS", getElemCSS($P, props));
			}

			// add basic classes & attributes
			$P
				.data("layoutName", options.name)	// add user's layout-name - even if blank!
				.data("layoutRole", "pane")
				.data("layoutEdge", pane)
				.css(c.cssReq).css("zIndex", _c.zIndex.pane_normal)
				.css(o.applyDemoStyles ? c.cssDemo : {}) // demo styles
				.addClass( o.paneClass +" "+ o.paneClass+"-"+pane ) // default = "ui-layout-pane ui-layout-pane-west" - may be a dupe of 'paneSelector'
				.bind("mouseenter."+ sID, addHover )
				.bind("mouseleave."+ sID, removeHover )
			;

			// see if this pane has a 'scrolling-content element'
			if (o.contentSelector) {
				$C = $Cs[pane] = $P.find(o.contentSelector).eq(0); // match 1-element only
				if (!$C.length)
					$Cs[pane] = false;
				else {
					$C.css( _c.content.cssReq );
					if (o.applyDemoStyles) {
						$C.css( _c.content.cssDemo ); // add padding & overflow: auto to content-div
						$P.css( _c.content.cssDemoPane ); // REMOVE padding/scrolling from pane
					}
					s.content = {}; // init content state
					// sizeContent() is called later
				}
			}

			if (!isCenter) {
				// call _parseSize AFTER applying pane classes & styles - but before making visible (if hidden)
				// if o.size is auto or not valid, then MEASURE the pane and use that as it's 'size'
				size	= s.size = _parseSize(pane, o.size);
				minSize	= _parseSize(pane,o.minSize) || 1;
				maxSize	= _parseSize(pane,o.maxSize) || 100000;
				if (size > 0) size = max(min(size, maxSize), minSize);
			}

			// init pane-logic vars
				s.tagName	= $P.attr("tagName");
				s.noRoom	= false; // true = pane 'automatically' hidden due to insufficient room - will unhide automatically
				s.isVisible	= true;  // false = pane is invisible - closed OR hidden - simplify logic
			if (!isCenter) {
				s.isClosed  = false; // true = pane is closed
				s.isSliding = false; // true = pane is currently open by 'sliding' over adjacent panes
				s.isResizing= false; // true = pane is in process of being resized
				s.isHidden	= false; // true = pane is hidden - no spacing, resizer or toggler is visible!
			}

			// set css-position to account for container borders & padding
			switch (pane) {
				case "north": 	CSS.top 	= sC.insetTop;
								CSS.left 	= sC.insetLeft;
								CSS.right	= sC.insetRight;
								break;
				case "south": 	CSS.bottom	= sC.insetBottom;
								CSS.left 	= sC.insetLeft;
								CSS.right 	= sC.insetRight;
								break;
				case "west": 	CSS.left 	= sC.insetLeft; // top, bottom & height set by sizeMidPanes()
								break;
				case "east": 	CSS.right 	= sC.insetRight; // ditto
								break;
				case "center":	// top, left, width & height set by sizeMidPanes()
			}

			if (dir == "horz") // north or south pane
				CSS.height = max(1, cssH(pane, size));
			else if (dir == "vert") // east or west pane
				CSS.width = max(1, cssW(pane, size));
			//else if (isCenter) {}

			$P.css(CSS); // apply size -- top, bottom & height will be set by sizeMidPanes
			if (dir != "horz") sizeMidPanes(pane, true); // true = skipCallback

			// NOW make the pane visible - in case was initially hidden
			$P.css({ visibility: "visible", display: "block" });

			// close or hide the pane if specified in settings
			if (o.initClosed && o.closable)
				close(pane, true, true); // true, true = force, noAnimation
			else if (o.initHidden || o.initClosed)
				hide(pane); // will be completely invisible - no resizer or spacing
			// ELSE setAsOpen() - called later by initHandles()

			// check option for auto-handling of pop-ups & drop-downs
			if (o.showOverflowOnHover)
				$P.hover( allowOverflow, resetOverflow );
		});

		/*
		 *	init the pane-handles NOW in case we have to hide or close the pane below
		 */
		initHandles();

		// now that all panes have been initialized and initially-sized,
		// make sure there is really enough space available for each pane
		$.each(_c.borderPanes.split(","), function (i, pane) {
			if ($Ps[pane] && state[pane].isVisible) { // pane is OPEN
				setSizeLimits(pane);
				makePaneFit(pane); // pane may be Closed, Hidden or Resized by makePaneFit()
			}
		});
		// size center-pane AGAIN in case we 'closed' a border-pane in loop above
		sizeMidPanes("center");

		// trigger onResize callbacks for all panes with triggerEventsOnLoad = true
		$.each(_c.allPanes.split(","), function (i, pane) {
			o = options[pane];
			if ($Ps[pane] && o.triggerEventsOnLoad && state[pane].isVisible) // pane is OPEN
				_execCallback(pane, o.onresize_end || o.onresize); // call onresize
		});
	};

	/**
	 * initHandles
	 *
	 * Initialize module objects, styling, size and position for all resize bars and toggler buttons
	 *
	 * @callers  create()
	 */
	var initHandles = function (panes) {
		if (!panes || panes == "all") panes = _c.borderPanes;

		// create toggler DIVs for each pane, and set object pointers for them, eg: $R.north = north toggler DIV
		$.each(panes.split(","), function (i, pane) {
			var $P		= $Ps[pane];
			$Rs[pane]	= false; // INIT
			$Ts[pane]	= false;
			if (!$P) return; // pane does not exist - skip

			var 
				o		= options[pane]
			,	s		= state[pane]
			,	c		= _c[pane]
			,	rClass	= o.resizerClass
			,	tClass	= o.togglerClass
			,	side	= c.side.toLowerCase()
			,	spacing	= (s.isVisible ? o.spacing_open : o.spacing_closed)
			,	_pane	= "-"+ pane // used for classNames
			,	_state	= (s.isVisible ? "-open" : "-closed") // used for classNames
				// INIT RESIZER BAR
			,	$R		= $Rs[pane] = $("<div></div>")
				// INIT TOGGLER BUTTON
			,	$T		= (o.closable ? $Ts[pane] = $("<div></div>") : false)
			;

			if (s.isVisible && o.resizable)
				; // handled by initResizable
			else if (!s.isVisible && o.slidable)
				$R.attr("title", o.sliderTip).css("cursor", o.sliderCursor);

			$R
				// if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "paneLeft-resizer"
				.attr("id", (o.paneSelector.substr(0,1)=="#" ? o.paneSelector.substr(1) + "-resizer" : ""))
				.data("layoutRole", "resizer")
				.data("layoutEdge", pane)
				.css(_c.resizers.cssReq).css("zIndex", _c.zIndex.resizer_normal)
				.css(o.applyDemoStyles ? _c.resizers.cssDemo : {}) // add demo styles
				.addClass(rClass +" "+ rClass+_pane)
				.appendTo($Container) // append DIV to container
				.hover( addHover, removeHover )
			;

			if ($T) {
				$T
					// if paneSelector is an ID, then create a matching ID for the resizer, eg: "#paneLeft" => "#paneLeft-toggler"
					.attr("id", (o.paneSelector.substr(0,1)=="#" ? o.paneSelector.substr(1) + "-toggler" : ""))
					.data("layoutRole", "toggler")
					.data("layoutEdge", pane)
					.css(_c.togglers.cssReq) // add base/required styles
					.css(o.applyDemoStyles ? _c.togglers.cssDemo : {}) // add demo styles
					.addClass(tClass +" "+ tClass+_pane)
					.appendTo($R) // append SPAN to resizer DIV
					.click(function(evt){ toggle(pane); evt.stopPropagation(); })
					.hover( addHover, removeHover )
				;
				// ADD INNER-SPANS TO TOGGLER
				if (o.togglerContent_open) // ui-layout-open
					$("<span>"+ o.togglerContent_open +"</span>")
						.data("layoutRole", "togglerContent")
						.data("layoutEdge", pane)
						.addClass("content content-open")
						.css("display","none")
						.appendTo( $T )
						.hover( addHover, removeHover )
					;
				if (o.togglerContent_closed) // ui-layout-closed
					$("<span>"+ o.togglerContent_closed +"</span>")
						.data("layoutRole", "togglerContent")
						.data("layoutEdge", pane)
						.addClass("content content-closed")
						.css("display","none")
						.appendTo( $T )
						.hover( addHover, removeHover )
					;
			}

			// ADD CLASSNAMES & SLIDE-BINDINGS - eg: class="resizer resizer-west resizer-open"
			if (s.isVisible)
				setAsOpen(pane);	// onOpen will be called, but NOT onResize
			else {
				setAsClosed(pane);	// onClose will be called
				bindStartSlidingEvent(pane, true); // will enable events IF option is set
			}

		});

		// SET ALL HANDLE DIMENSIONS
		sizeHandles("all");
	};

	/**
	 * initButtons
	 *
	 * Searches for .ui-layout-button-xxx elements and auto-binds them as layout-buttons
	 *
	 * @callers  create()
	 */
	var initButtons = function () {
		var pre	= "ui-layout-button-", name;
		$.each("toggle,open,close,pin,toggle-slide,open-slide".split(","), function (i, action) {
			$.each(_c.borderPanes.split(","), function (ii, pane) {
				$("."+pre+action+"-"+pane).each(function(){
					// if button was previously 'bound', data.layoutName was set, but is blank if layout has no 'name'
					name = $(this).data("layoutName") || $(this).attr("layoutName");
					if (name == undefined || name == options.name) {
						if (action.substr("-slide") > 0)
							bindButton(this, action.split("-")[0], pane, true)
						else
							bindButton(this, action, pane);
					}
				});
			});
		});
	};

	/**
	 * initResizable
	 *
	 * Add resize-bars to all panes that specify it in options
	 *
	 * @dependancies  $.fn.resizable - will skip if not found
	 * @callers  create()
	 */
	var initResizable = function (panes) {
		var
			draggingAvailable = (typeof $.fn.draggable == "function")
		,	$Frames, side // set in start()
		;
		if (!panes || panes == "all") panes = _c.borderPanes;

		$.each(panes.split(","), function (idx, pane) {
			var 
				o	= options[pane]
			,	s	= state[pane]
			,	c	= _c[pane]
			,	side = (c.dir=="horz" ? "top" : "left")
			,	r, live // set in start because may change
			;
			if (!draggingAvailable || !$Ps[pane] || !o.resizable) {
				o.resizable = false;
				return true; // skip to next
			}

			var 
				$P 		= $Ps[pane]
			,	$R		= $Rs[pane]
			,	base	= o.resizerClass
			//	'drag' classes are applied to the ORIGINAL resizer-bar while dragging is in process
			,	resizerClass		= base+"-drag"				// resizer-drag
			,	resizerPaneClass	= base+"-"+pane+"-drag"		// resizer-north-drag
			//	'helper' class is applied to the CLONED resizer-bar while it is being dragged
			,	helperClass			= base+"-dragging"			// resizer-dragging
			,	helperPaneClass		= base+"-"+pane+"-dragging" // resizer-north-dragging
			,	helperLimitClass	= base+"-dragging-limit"	// resizer-drag
			,	helperClassesSet	= false 					// logic var
			;

			if (!s.isClosed)
				$R
					.attr("title", o.resizerTip)
					.css("cursor", o.resizerCursor) // n-resize, s-resize, etc
				;

			$R.draggable({
				containment:	$Container[0] // limit resizing to layout container
			,	axis:			(c.dir=="horz" ? "y" : "x") // limit resizing to horz or vert axis
			,	delay:			100
			,	distance:		1
			//	basic format for helper - style it using class: .ui-draggable-dragging
			,	helper:			"clone"
			,	opacity:		o.resizerDragOpacity
			,	addClasses:		false // avoid ui-state-disabled class when disabled
			//,	iframeFix:		o.draggableIframeFix // TODO: consider using when bug is fixed
			,	zIndex:			_c.zIndex.resizer_drag

			,	start: function (e, ui) {
					// REFRESH options & state pointers in case we used swapPanes
					o = options[pane];
					s = state[pane];
					// re-read options
					live = o.resizeWhileDragging;

					// onresize_start callback - will CANCEL hide if returns false
					// TODO: CONFIRM that dragging can be cancelled like this???
					if (false === _execCallback(pane, o.onresize_start)) return false;

					_c.isLayoutBusy	= true; // used by sizePane() logic during a liveResize
					s.isResizing	= true; // prevent pane from closing while resizing
					clearTimer(pane+"_closeSlider"); // just in case already triggered

					// SET RESIZER LIMITS - used in drag()
					setSizeLimits(pane); // update pane/resizer state
					r = s.resizerPosition;

					$R.addClass( resizerClass +" "+ resizerPaneClass ); // add drag classes
					helperClassesSet = false; // reset logic var - see drag()

					// MASK PANES WITH IFRAMES OR OTHER TROUBLESOME ELEMENTS
					$Frames = $(o.maskIframesOnResize === true ? "iframe" : o.maskIframesOnResize).filter(":visible");
					var id, i=0; // ID incrementer - used when 'resizing' masks during dynamic resizing
					$Frames.each(function() {					
						id = "ui-layout-mask-"+ (++i);
						$(this).data("layoutMaskID", id); // tag iframe with corresponding maskID
						$('<div id="'+ id +'" class="ui-layout-mask ui-layout-mask-'+ pane +'"/>')
							.css({
								background:	"#fff"
							,	opacity:	"0.001"
							,	zIndex:		_c.zIndex.iframe_mask
							,	position:	"absolute"
							,	width:		this.offsetWidth+"px"
							,	height:		this.offsetHeight+"px"
							})
							.css($(this).position()) // top & left -- changed from offset()
							.appendTo(this.parentNode) // put mask-div INSIDE pane to avoid zIndex issues
						;
					});

					// DISABLE TEXT SELECTION - particularly for WebKit browsers, Safari & Chrome
					if (o.noSelectionWhileDragging) $(document).disableSelection(); 
				}

			,	drag: function (e, ui) {
					if (!helperClassesSet) { // can only add classes after clone has been added to the DOM
						//$(".ui-draggable-dragging")
						ui.helper
							.addClass( helperClass +" "+ helperPaneClass ) // add helper classes
							.children().css("visibility","hidden") // hide toggler inside dragged resizer-bar
						;
						helperClassesSet = true;
						// draggable bug!? RE-SET zIndex to prevent E/W resize-bar showing through N/S pane!
						if (s.isSliding) $Ps[pane].css("zIndex", _c.zIndex.pane_sliding);
					}
					// CONTAIN RESIZER-BAR TO RESIZING LIMITS
					var limit = 0;
					if (ui.position[side] < r.min) {
						ui.position[side] = r.min;
						limit = -1;
					}
					else if (ui.position[side] > r.max) {
						ui.position[side] = r.max;
						limit = 1;
					}
					// ADD/REMOVE dragging-limit CLASS
					if (limit) {
						ui.helper.addClass( helperLimitClass ); // at dragging-limit
						window.defaultStatus = "Panel has reached its "+ (limit>0 ? "maximum" : "minimum") +" size";
					}
					else {
						ui.helper.removeClass( helperLimitClass ); // not at dragging-limit
						window.defaultStatus = "";
					}
					// DYNAMICALLY RESIZE PANES IF OPTION ENABLED
					if (live) resizePanes(e, ui, pane);
				}

			,	stop: function (e, ui) {
					// RE-ENABLE TEXT SELECTION
					if (o.noSelectionWhileDragging) $(document).enableSelection(); 
					window.defaultStatus = ""; // clear 'resizing limit' message from statusbar
					$R.removeClass( resizerClass +" "+ resizerPaneClass +" "+ helperLimitClass ); // remove drag classes from Resizer
					s.isResizing = false;
					_c.isLayoutBusy	= false; // set BEFORE resizePanes so other logic can pick it up
					resizePanes(e, ui, pane, true); // true = resizingDone
				}

			});

			/**
			 * resizePanes
			 *
			 * Sub-routine called from stop() and optionally drag()
			 */
			var resizePanes = function (e, ui, pane, resizingDone) {
				var 
					dragPos	= ui.position
				,	c		= _c[pane]
				,	resizerPos, newSize
				,	i = 0 // ID incrementer
				;
				switch (pane) {
					case "north":	resizerPos = dragPos.top; break;
					case "west":	resizerPos = dragPos.left; break;
					case "south":	resizerPos = sC.offsetHeight - dragPos.top  - o.spacing_open; break;
					case "east":	resizerPos = sC.offsetWidth  - dragPos.left - o.spacing_open; break;
				};

				// remove container margin from resizer position to get the pane size
				newSize = resizerPos - sC["inset"+ c.side];
				manualSizePane(pane, newSize);

				if (resizingDone) {
					// Remove OR Resize MASK(S) created in drag.start
					$("div.ui-layout-mask").each(function() { this.parentNode.removeChild(this); });
					//$("div.ui-layout-mask").remove(); // TODO: Is this less efficient?
				}
				else
					$Frames.each(function() {
						$("#"+ $(this).data("layoutMaskID")) // get corresponding mask by ID
							.css($(this).position()) // update top & left
							.css({ // update width & height
								width:	this.offsetWidth +"px"
							,	height:	this.offsetHeight+"px"
							})
						;
					});
			}
		});
	};


	/**
	 *	destroy
	 *
	 *	Destroy this layout and reset all elements
	 */
	var destroy = function () {
		// UNBIND layout events
		$(window).unbind("."+ sID);
		$(document).unbind("."+ sID);

		var
			isFullPage	= (sC.tagName == "BODY")
		//	create list of ALL pane-classes that need to be removed
		,	root	= o.paneClass // default="ui-layout-pane"
		,	_open	= "-open"
		,	_sliding= "-sliding"
		,	_closed	= "-closed"
		,	generic = [ root, root+_open, root+_closed, root+_sliding ] // generic classes
		,	$P, pRoot, pClasses // loop vars
		;
		// loop all panes to remove layout classes, attributes and bindings
		$.each(_c.allPanes.split(","), function (i, pane) {
			$P = $Ps[pane];
			if (!$P) return true; // no pane - SKIP

			// REMOVE pane's resizer and toggler elements
			if (pane != "center") {
				$Ts[pane].remove();
				$Rs[pane].remove();
			}

			pRoot = root+"-"+pane; // eg: "ui-layout-pane-west"
			pClasses = []; // reset
			pClasses.push( pRoot );
			pClasses.push( pRoot+_open );
			pClasses.push( pRoot+_closed );
			pClasses.push( pRoot+_sliding );

			$.merge(pClasses, generic); // ADD generic classes
			$.merge(pClasses, getHoverClasses($P, true)); // ADD hover-classes

			$P
				.removeClass( pClasses.join(" ") ) // remove ALL pane-classes
				.removeData("layoutRole")
				.removeData("layoutEdge")
				.unbind("."+ sID) // remove ALL Layout events
				// TODO: remove these extra unbind commands when jQuery is fixed
				.unbind("mouseenter")
				.unbind("mouseleave")
			;

			// do NOT reset CSS if this pane is STILL the container of a nested layout!
			// the nested layout will reset its 'container' when/if it is destroyed
			if (!$P.data("layoutContainer"))
				$P.css( $P.data("layoutCSS") );
		});

		// reset layout-container
		$Container.removeData("layoutContainer");

		// do NOT reset container CSS if is a 'pane' in an outer-layout - ie, THIS layout is 'nested'
		if (!$Container.data("layoutEdge"))
			$Container.css( $Container.data("layoutCSS") ); // RESET CSS
		// for full-page layouts, must also reset the <HTML> CSS
		if (isFullPage)
			$("html").css( $("html").data("layoutCSS") ); // RESET CSS

		// trigger state-management and onunload callback
		unload();

		var n = options.name; // layout-name
		if (n && window[n]) window[n] = null; // clear window object, if exists
	};


/*
 * ###########################
 *       ACTION METHODS
 * ###########################
 */

	/**
	 * hide / show
	 *
	 * Completely 'hides' a pane, including its spacing - as if it does not exist
	 * The pane is not actually 'removed' from the source, so can use 'show' to un-hide it
	 *
	 * @param String  pane   The pane being hidden, ie: north, south, east, or west
	 */
	var hide = function (pane, noAnimation) {
		var
			o	= options[pane]
		,	s	= state[pane]
		,	$P	= $Ps[pane]
		,	$R	= $Rs[pane]
		;
		if (!$P || s.isHidden) return; // pane does not exist OR is already hidden

		// onhide_start callback - will CANCEL hide if returns false
		if (state.initialized && false === _execCallback(pane, o.onhide_start)) return;

		s.isSliding = false; // just in case

		// now hide the elements
		if ($R) $R.hide(); // hide resizer-bar
		if (!state.initialized || s.isClosed) {
			s.isClosed = true; // to trigger open-animation on show()
			s.isHidden  = true;
			s.isVisible = false;
			$P.hide(); // no animation when loading page
			sizeMidPanes(_c[pane].dir == "horz" ? "all" : "center");
			if (state.initialized || o.triggerEventsOnLoad)
				_execCallback(pane, o.onhide_end || o.onhide);
		}
		else {
			s.isHiding = true; // used by onclose
			close(pane, false, noAnimation); // adjust all panes to fit
		}
	};

	var show = function (pane, openPane, noAnimation, noAlert) {
		var
			o	= options[pane]
		,	s	= state[pane]
		,	$P	= $Ps[pane]
		,	$R	= $Rs[pane]
		;
		if (!$P || !s.isHidden) return; // pane does not exist OR is not hidden

		// onshow_start callback - will CANCEL show if returns false
		if (false === _execCallback(pane, o.onshow_start)) return;

		s.isSliding = false; // just in case
		s.isShowing = true; // used by onopen/onclose
		//s.isHidden  = false; - will be set by open/close - if not cancelled

		// now show the elements
		//if ($R) $R.show(); - will be shown by open/close
		if (openPane === false)
			close(pane, true); // true = force
		else
			open(pane, false, noAnimation, noAlert); // adjust all panes to fit
	};


	var slideOpen = function (evt_or_pane) {
		var
			type = typeof evt_or_pane
		,	pane = (type == "string" ? evt_or_pane : $(this).data("layoutEdge"))
		;
		// prevent event from triggering on NEW resizer binding created below
		if (type == "object") { evt_or_pane.stopImmediatePropagation(); }

		if (state[pane].isClosed)
			open(pane, true); // true = slide - ie, called from here!
		else // skip 'open' if already open!
			bindStopSlidingEvents(pane, true); // BIND trigger events to close sliding-pane
	};

	var slideClosed = function (evt_or_pane) {
		var
			$E	= (typeof evt_or_pane == "string" ? $Ps[evt_or_pane] : $(this))
		,	pane= $E.data("layoutEdge")
		,	o	= options[pane]
		,	s	= state[pane]
		,	$P	= $Ps[pane]
		;
		if (s.isClosed || s.isResizing)
			return; // skip if already closed OR in process of resizing
		else if (o.slideTrigger_close == "click")
			close_NOW(); // close immediately onClick
		else if (o.trackMouseWhenSliding && isMouseOver(pane))
			clearTimer(pane+"_closeSlider"); // browser glitch - mouse is REALLY 'over' the pane
		else // trigger = mouseout - use a delay
			setTimer(pane+"_closeSlider", close_NOW, 300); // .3 sec delay

		// SUBROUTINE for timed close
		function close_NOW (e) {
			if (s.isClosed) // skip 'close' if already closed!
				bindStopSlidingEvents(pane, false); // UNBIND trigger events
			else
				close(pane); // close will handle unbinding
		}
	};


	/**
	 * toggle
	 *
	 * Toggles a pane open/closed by calling either open or close
	 *
	 * @param String  pane   The pane being toggled, ie: north, south, east, or west
	 */
	var toggle = function (pane, slide) {
		if (typeof pane !="string")
			pane = $(this).data("layoutEdge"); // bound to $R.dblclick
		var s = state[pane];
		if (s.isHidden)
			show(pane); // will call 'open' after unhiding it
		else if (s.isClosed)
			open(pane, !!slide);
		else
			close(pane);
	};

	/**
	 * close
	 *
	 * Close the specified pane (animation optional), and resize all other panes as needed
	 *
	 * @param String  pane   The pane being closed, ie: north, south, east, or west
	 */
	var close = function (pane, force, noAnimation, skipCallback) {
		var
			$P		= $Ps[pane]
		,	$R		= $Rs[pane]
		,	$T		= $Ts[pane]
		,	o		= options[pane]
		,	s		= state[pane]
		,	doFX	= !noAnimation && !s.isClosed && (o.fxName_close != "none")
		// 	transfer logic vars to temp vars
		,	isShowing	= s.isShowing
		,	isHiding	= s.isHiding
		,	wasSliding	= s.isSliding
		;
		// now clear the logic vars
		delete s.isShowing;
		delete s.isHiding;

		if (!$P || !o.closable) return; // invalid request // (!o.resizable && !o.closable) ???
		else if (!force && s.isClosed && !isShowing) return; // already closed

		if (_c.isLayoutBusy) { // layout is 'busy' - probably with an animation
			_queue("close", pane, force); // set a callback for this action, if possible
			return; // ABORT 
		}

		// onclose_start callback - will CANCEL hide if returns false
		// SKIP if just 'showing' a hidden pane as 'closed'
		if (state.initialized && !isShowing && false === _execCallback(pane, o.onclose_start)) return;

		// SET flow-control flags
		_c[pane].isMoving = true;
		_c.isLayoutBusy = true;

		s.isClosed = true;
		s.isVisible = false;
		// update isHidden BEFORE sizing panes
		if (isHiding) s.isHidden = true;
		else if (isShowing) s.isHidden = false;

		if (s.isSliding) // pane is being closed, so UNBIND trigger events
			bindStopSlidingEvents(pane, false); // will set isSliding=false
		else if (state.initialized) // resize panes adjacent to this one
			sizeMidPanes(_c[pane].dir == "horz" ? "all" : "center", false); // false = NOT skipCallback

		// if this pane has a resizer bar, move it NOW - before animation
		if (state.initialized) setAsClosed(pane); // during init, setAsClosed will be called LATER by initHandles

		// ANIMATE 'CLOSE' - if no animation, then was ALREADY shown above
		if (doFX) {
			lockPaneForFX(pane, true); // need to set left/top so animation will work
			$P.hide( o.fxName_close, o.fxSettings_close, o.fxSpeed_close, function () {
				lockPaneForFX(pane, false); // undo
				close_2();
			});
		}
		else {
			$P.hide(); // just hide pane NOW
			close_2();
		};

		// SUBROUTINE
		function close_2 () {
			if (s.isClosed) { // make sure pane was not 'reopened' before animation finished!

				bindStartSlidingEvent(pane, true); // will enable if o.slidable = true

				// if opposite-pane was autoClosed, see if it can be autoOpened now
				var altPane = _c.altSide[pane];
				if (state[ altPane ].noRoom) {
					setSizeLimits( altPane );
					makePaneFit( altPane );
				}

				if (!skipCallback && (state.initialized || o.triggerEventsOnLoad)) {
					// onclose callback - UNLESS just 'showing' a hidden pane as 'closed'
					if (!isShowing && !wasSliding) _execCallback(pane, o.onclose_end || o.onclose);
					// onhide OR onshow callback
					if (isShowing)	_execCallback(pane, o.onshow_end || o.onshow);
					if (isHiding)	_execCallback(pane, o.onhide_end || o.onhide);
				}
			}
			// execute internal flow-control callback
			_dequeue(pane);
		}
	};

	var setAsClosed = function (pane) {
		var
			$P		= $Ps[pane]
		,	$R		= $Rs[pane]
		,	$T		= $Ts[pane]
		,	o		= options[pane]
		,	s		= state[pane]
		,	side	= _c[pane].side.toLowerCase()
		,	inset	= "inset"+ _c[pane].side
		,	rClass	= o.resizerClass
		,	tClass	= o.togglerClass
		,	_pane	= "-"+ pane // used for classNames
		,	_open	= "-open"
		,	_sliding= "-sliding"
		,	_closed	= "-closed"
		;
		$R
			.css(side, sC[inset]) // move the resizer
			.removeClass( rClass+_open +" "+ rClass+_pane+_open )
			.removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
			.addClass( rClass+_closed +" "+ rClass+_pane+_closed )
			.unbind("dblclick."+ sID)
		;
		// DISABLE 'resizing' when closed - do this BEFORE bindStartSlidingEvent?
		if (o.resizable && typeof $.fn.draggable == "function")
			$R
				.draggable("disable")
				.removeClass("ui-state-disabled") // do NOT apply disabled styling - not suitable here
				.css("cursor", "default")
				.attr("title","")
			;

		// if pane has a toggler button, adjust that too
		if ($T) {
			$T
				.removeClass( tClass+_open +" "+ tClass+_pane+_open )
				.addClass( tClass+_closed +" "+ tClass+_pane+_closed )
				.attr("title", o.togglerTip_closed) // may be blank
			;
			// toggler-content - if exists
			$T.children(".content-open").hide();
			$T.children(".content-closed").css("display","block");
		}

		// sync any 'pin buttons'
		syncPinBtns(pane, false);

		if (state.initialized) {
			// resize 'length' and position togglers for adjacent panes
			sizeHandles("all");
		}
	};

	/**
	 * open
	 *
	 * Open the specified pane (animation optional), and resize all other panes as needed
	 *
	 * @param String  pane   The pane being opened, ie: north, south, east, or west
	 */
	var open = function (pane, slide, noAnimation, noAlert) {
		var 
			$P		= $Ps[pane]
		,	$R		= $Rs[pane]
		,	$T		= $Ts[pane]
		,	o		= options[pane]
		,	s		= state[pane]
		,	doFX	= !noAnimation && s.isClosed && (o.fxName_open != "none")
		// 	transfer logic var to temp var
		,	isShowing = s.isShowing
		;
		// now clear the logic var
		delete s.isShowing;

		if (!$P || (!o.resizable && !o.closable)) return; // invalid request
		else if (s.isVisible && !s.isSliding) return; // already open

		// pane can ALSO be unhidden by just calling show(), so handle this scenario
		if (s.isHidden && !isShowing) {
			show(pane, true);
			return;
		}

		if (_c.isLayoutBusy) { // layout is 'busy' - probably with an animation
			_queue("open", pane, slide); // set a callback for this action, if possible
			return; // ABORT
		}

		// onopen_start callback - will CANCEL hide if returns false
		if (false === _execCallback(pane, o.onopen_start)) return;

		// make sure there is enough space available to open the pane
		setSizeLimits(pane, slide); // update pane-state
		if (s.minSize > s.maxSize) { // INSUFFICIENT ROOM FOR PANE TO OPEN!
			syncPinBtns(pane, false); // make sure pin-buttons are reset
			if (!noAlert && o.noRoomToOpenTip) alert(o.noRoomToOpenTip);
			return; // ABORT
		}

		// SET flow-control flags
		_c[pane].isMoving = true;
		_c.isLayoutBusy = true;

		if (slide) // START Sliding - will set isSliding=true
			bindStopSlidingEvents(pane, true); // BIND trigger events to close sliding-pane
		else if (s.isSliding) // PIN PANE (stop sliding) - open pane 'normally' instead
			bindStopSlidingEvents(pane, false); // UNBIND trigger events - will set isSliding=false
		else if (o.slidable)
			bindStartSlidingEvent(pane, false); // UNBIND trigger events

		s.noRoom = false; // will be reset by makePaneFit if 'noRoom'
		makePaneFit(pane);

		s.isVisible = true;
		s.isClosed	= false;
		// update isHidden BEFORE sizing panes - WHY??? Old?
		if (isShowing) s.isHidden = false;

		if (doFX) { // ANIMATE
			lockPaneForFX(pane, true); // need to set left/top so animation will work
			$P.show( o.fxName_open, o.fxSettings_open, o.fxSpeed_open, function() {
				lockPaneForFX(pane, false); // undo
				open_2(); // continue
			});
		}
		else {// no animation
			$P.show();	// just show pane and...
			open_2();	// continue
		};

		// SUBROUTINE
		function open_2 () {
			if (s.isVisible) { // make sure pane was not closed or hidden before animation finished!

				// cure iframe display issues
				_fixIframe(pane);

				// NOTE: if isSliding, then other panes are NOT 'resized'
				if (!s.isSliding) // resize all panes adjacent to this one
					sizeMidPanes(_c[pane].dir=="vert" ? "center" : "all", false); // false = NOT skipCallback
				else if (o.slideTrigger_close == "mouseout" && isTimerRunning(pane+"_closeSlider")) {
					if (o.trackMouseWhenSliding && isMouseOver(pane)) // handle Chrome browser glitch...
						clearTimer(pane+"_closeSlider"); // prevent premature close
				}

				// set classes, position handles and execute callbacks...
				setAsOpen(pane);
			}

			// internal flow-control callback
			_dequeue(pane);
		};
	
	};

	var setAsOpen = function (pane, skipCallback) {
		var 
			$P		= $Ps[pane]
		,	$R		= $Rs[pane]
		,	$T		= $Ts[pane]
		,	o		= options[pane]
		,	s		= state[pane]
		,	side	= _c[pane].side.toLowerCase()
		,	inset	= "inset"+ _c[pane].side
		,	rClass	= o.resizerClass
		,	tClass	= o.togglerClass
		,	_pane	= "-"+ pane // used for classNames
		,	_open	= "-open"
		,	_closed	= "-closed"
		,	_sliding= "-sliding"
		;
		$R
			.css(side, sC[inset] + getPaneSize(pane)) // move the resizer
			.removeClass( rClass+_closed +" "+ rClass+_pane+_closed )
			.addClass( rClass+_open +" "+ rClass+_pane+_open )
		;
		if (s.isSliding)
			$R.addClass( rClass+_sliding +" "+ rClass+_pane+_sliding )
		else // in case 'was sliding'
			$R.removeClass( rClass+_sliding +" "+ rClass+_pane+_sliding )

		if (o.resizerDblClickToggle)
			$R.bind("dblclick", toggle );
		removeHover( 0, $R ); // remove hover classes
		if (o.resizable && typeof $.fn.draggable == "function")
			$R
				.draggable("enable")
				.css("cursor", o.resizerCursor)
				.attr("title", o.resizerTip)
			;
		else if (!s.isSliding)
			$R.css("cursor", "default"); // n-resize, s-resize, etc

		// if pane also has a toggler button, adjust that too
		if ($T) {
			$T
				.removeClass( tClass+_closed +" "+ tClass+_pane+_closed )
				.addClass( tClass+_open +" "+ tClass+_pane+_open )
				.attr("title", o.togglerTip_open) // may be blank
			;
			removeHover( 0, $T ); // remove hover classes
			// toggler-content - if exists
			$T.children(".content-closed").hide();
			$T.children(".content-open").css("display","block");
		}

		// sync any 'pin buttons'
		syncPinBtns(pane, !s.isSliding);

		if (state.initialized) {
			// resize resizer & toggler sizes for all panes
			sizeHandles("all");
			// resize content every time pane opens - to be sure
			sizeContent(pane);
		}

		// update pane-state dimensions
		$.extend(s, getElemDims($P));

		if (!skipCallback && (state.initialized || o.triggerEventsOnLoad) && $P.is(":visible")) {
			// onopen callback
			_execCallback(pane, o.onopen_end || o.onopen);
			// onshow callback - TODO: should this be here?
			if (s.isShowing) _execCallback(pane, o.onshow_end || o.onshow);
			// ALSO call onresize because layout-size *may* have changed while pane was closed
			if (state.initialized) _execCallback(pane, o.onresize_end || o.onresize); // if (state.initialized)
		}
	};


	/**
	 * lockPaneForFX
	 *
	 * Must set left/top on East/South panes so animation will work properly
	 *
	 * @param String  pane  The pane to lock, 'east' or 'south' - any other is ignored!
	 * @param Boolean  doLock  true = set left/top, false = remove
	 */
	var lockPaneForFX = function (pane, doLock) {
		var $P = $Ps[pane];
		if (doLock) {
			$P.css({ zIndex: _c.zIndex.pane_animate }); // overlay all elements during animation
			if (pane=="south")
				$P.css({ top: sC.insetTop + sC.innerHeight - $P.outerHeight() });
			else if (pane=="east")
				$P.css({ left: sC.insetLeft + sC.innerWidth - $P.outerWidth() });
		}
		else { // animation DONE - RESET CSS
			$P.css({ zIndex: (state[pane].isSliding ? _c.zIndex.pane_sliding : _c.zIndex.pane_normal) });
			if (pane=="south")
				$P.css({ top: "auto" });
			else if (pane=="east")
				$P.css({ left: "auto" });
			// fix anti-aliasing in IE - only needed for animations that change opacity
			var o = options[pane];
			if (state.browser.msie && o.fxOpacityFix && o.fxName_open != "slide" && $P.css("filter") && $P.css("opacity") == 1)
				$P[0].style.removeAttribute('filter');
		}
	};


	/**
	 * bindStartSlidingEvent
	 *
	 * Toggle sliding functionality of a specific pane on/off by adding removing 'slide open' trigger
	 *
	 * @callers  open(), close()
	 * @param String  pane  The pane to enable/disable, 'north', 'south', etc.
	 * @param Boolean  enable  Enable or Disable sliding?
	 */
	var bindStartSlidingEvent = function (pane, enable) {
		var 
			o		= options[pane]
		,	$R		= $Rs[pane]
		,	trigger	= o.slideTrigger_open
		;
		if (!$R || !o.slidable) return;

		// make sure we have a valid event
		if (trigger != "click" && trigger != "dblclick" && trigger != "mouseover")
			trigger = o.slideTrigger_open = "click";

		$R
			// add or remove trigger event
			[enable ? "bind" : "unbind"](trigger, slideOpen)
			// set the appropriate cursor & title/tip
			.css("cursor", (enable ? o.sliderCursor : "default"))
			.attr("title", (enable ? o.sliderTip : ""))
		;
	};

	/**
	 * bindStopSlidingEvents
	 *
	 * Add or remove 'mouseout' events to 'slide close' when pane is 'sliding' open or closed
	 * Also increases zIndex when pane is sliding open
	 * See bindStartSlidingEvent for code to control 'slide open'
	 *
	 * @callers  slideOpen(), slideClosed()
	 * @param String  pane  The pane to process, 'north', 'south', etc.
	 * @param Boolean  enable  Enable or Disable events?
	 */
	var bindStopSlidingEvents = function (pane, enable) {
		var 
			o		= options[pane]
		,	s		= state[pane]
		,	trigger	= o.slideTrigger_close
		,	action	= (enable ? "bind" : "unbind") // can't make 'unbind' work! - see disabled code below
		,	$P		= $Ps[pane]
		,	$R		= $Rs[pane]
		;

		s.isSliding = enable; // logic
		clearTimer(pane+"_closeSlider"); // just in case

		// raise z-index when sliding
		$P.css({ zIndex: (enable ? _c.zIndex.pane_sliding : _c.zIndex.pane_normal) });
		$R.css({ zIndex: (enable ? _c.zIndex.pane_sliding : _c.zIndex.resizer_normal) });

		// make sure we have a valid event
		if (trigger != "mouseout" && trigger != "click")
			trigger = o.slideTrigger_close = "mouseout";

		// remove 'slideOpen' trigger event from resizer
		if (enable) bindStartSlidingEvent(pane, false);

		// add/remove slide triggers
		$R[action](trigger, slideClosed); // base event on resize
		// need extra events for mouseout
		if (trigger == "mouseout") {
			// also close on pane.mouseout
			$P[action]("mouseout."+ sID, slideClosed);
			// cancel timer when mouse moves between 'pane' and 'resizer'
			$R[action]("mouseover", cancelMouseOut);
			$P[action]("mouseover."+ sID, cancelMouseOut);
		}

		if (!enable)
			clearTimer(pane+"_closeSlider");
		else if (trigger == "click" && !o.resizable) {
			// IF pane is not resizable (which already has a cursor and tip) 
			// then set the a cursor & title/tip on resizer when sliding
			$R.css("cursor", (enable ? o.sliderCursor : "default"));
			$R.attr("title", (enable ? o.togglerTip_open : "")); // use Toggler-tip, eg: "Close Pane"
		}

		// SUBROUTINE for mouseout timer clearing
		function cancelMouseOut (evt) {
			clearTimer(pane+"_closeSlider");
			evt.stopPropagation();
		}
	};


	/**
	 * makePaneFit
	 *
	 * Hides/closes a pane if there is insufficient room - reverses this when there is room again
	 * MUST have already called setSizeLimits() before calling this method
	 */
	var makePaneFit = function (pane, isOpening, skipCallback) {
		var
			o	= options[pane]
		,	s	= state[pane]
		,	c	= _c[pane]
		,	$P	= $Ps[pane]
		,	$R	= $Rs[pane]
		,	isSidePane 	= c.dir=="vert"
		,	hasRoom		= false
		;

		// special handling for center pane
		if (pane == "center" || (isSidePane && s.noVerticalRoom)) {
			// see if there is enough room to display the center-pane
			hasRoom = s.minHeight <= s.maxHeight && (isSidePane || s.minWidth <= s.maxWidth);
			if (hasRoom && s.noRoom) { // previously hidden due to noRoom, so show now
				$P.show();
				if ($R) $R.show();
				s.isVisible = true;
				s.noRoom = false;
				if (isSidePane) s.noVerticalRoom = false;
				_fixIframe(pane);
			}
			else if (!hasRoom && !s.noRoom) { // not currently hidden, so hide now
				$P.hide();
				if ($R) $R.hide();
				s.isVisible = false;
				s.noRoom = true;
			}
		}

		// see if there is enough room to fit the border-pane
		if (pane == "center") {
			// ignore center in this block
		}
		else if (s.minSize <= s.maxSize) { // pane CAN fit
			hasRoom = true;
			if (s.size > s.maxSize) // pane is too big - shrink it
				sizePane(pane, s.maxSize, skipCallback);
			else if (s.size < s.minSize) // pane is too small - enlarge it
				sizePane(pane, s.minSize, skipCallback);
			else if ($R && $P.is(":visible")) {
				// make sure resizer-bar is positioned correctly
				// handles situation where nested layout was 'hidden' when initialized
				var
					side = c.side.toLowerCase()
				,	pos  = s.size + sC["inset"+ c.side]
				;
				if (_cssNum($R, side) != pos) $R.css( side, pos );
			}

			// if was previously hidden due to noRoom, then RESET because NOW there is room
			if (s.noRoom) {
				// s.noRoom state will be set by open or show
				if (s.wasOpen && o.closable) {
					if (o.autoReopen)
						open(pane, false, true, true); // true = noAnimation, true = noAlert
					else // leave the pane closed, so just update state
						s.noRoom = false;
				}
				else
					show(pane, s.wasOpen, true, true); // true = noAnimation, true = noAlert
			}
		}
		else { // !hasRoom - pane CANNOT fit
			if (!s.noRoom) { // pane not set as noRoom yet, so hide or close it now...
				s.noRoom = true; // update state
				s.wasOpen = !s.isClosed && !s.isSliding;
				if (o.closable) // 'close' if possible
					close(pane, true, true); // true = force, true = noAnimation
				else // 'hide' pane if cannot just be closed
					hide(pane, true); // true = noAnimation
			}
		}
	};


	/**
	 * sizePane / manualSizePane
	 *
	 * sizePane is called only by internal methods whenever a pane needs to be resized
	 * manualSizePane is an exposed flow-through method allowing extra code when pane is 'manually resized'
	 *
	 * @param String	pane	The pane being resized
	 * @param Integer	size	The *desired* new size for this pane - will be validated
	 * @param Boolean	skipCallback	Should the onresize callback be run?
	 */
	var manualSizePane = function (pane, size, skipCallback) {
		// ANY call to sizePane will disabled autoResize
		var
			o = options[pane]
		//	if resizing callbacks have been delayed and resizing is now DONE, force resizing to complete...
		,	forceResize = o.resizeWhileDragging && !_c.isLayoutBusy //  && !o.triggerEventsWhileDragging
		;
		o.autoResize = false;
		// flow-through...
		sizePane(pane, size, skipCallback, forceResize);
	}
	var sizePane = function (pane, size, skipCallback, force) {
		var 
			o		= options[pane]
		,	s		= state[pane]
		,	$P		= $Ps[pane]
		,	$R		= $Rs[pane]
		,	side	= _c[pane].side.toLowerCase()
		,	inset	= "inset"+ _c[pane].side
		,	skipResizeWhileDragging = _c.isLayoutBusy && !o.triggerEventsWhileDragging
		,	oldSize
		;
		// calculate 'current' min/max sizes
		setSizeLimits(pane); // update pane-state
		oldSize = s.size;

		size = _parseSize(pane, size); // handle percentages & auto
		size = max(size, _parseSize(pane, o.minSize));
		size = min(size, s.maxSize);
		if (size < s.minSize) { // not enough room for pane!
			makePaneFit(pane, false, skipCallback);	// will hide or close pane
			return;
		}

		// IF newSize is same as oldSize, then nothing to do - abort
		if (!force && size == oldSize) return;
		s.size = size;

		// resize the pane, and make sure its visible
		$P.css( _c[pane].sizeType.toLowerCase(), max(1, cssSize(pane, size)) );

		// update pane-state dimensions
		$.extend(s, getElemDims($P));

		// reposition the resizer-bar
		if ($R && $P.is(":visible")) $R.css( side, size + sC[inset] );

		// resize all the adjacent panes, and adjust their toggler buttons
		// when skipCallback passed, it means the controlling method will handle 'other panes'
		if (!skipCallback) {
			// also no callback if live-resize is in progress and NOT triggerEventsWhileDragging
			if (!s.isSliding) sizeMidPanes(_c[pane].dir=="horz" ? "all" : "center", skipResizeWhileDragging, force);
			sizeHandles("all");
		}

		sizeContent(pane);

		if (!skipCallback && !skipResizeWhileDragging && state.initialized && s.isVisible)
			_execCallback(pane, o.onresize_end || o.onresize);

		// if opposite-pane was autoClosed, see if it can be autoOpened now
		var altPane = _c.altSide[pane];
		if (size < oldSize && state[ altPane ].noRoom) {
			setSizeLimits( altPane );
			makePaneFit( altPane, false, skipCallback );
		}
	};

	/**
	 * sizeMidPanes
	 *
	 * @callers  initPanes(), sizePane(), resizeAll(), open(), close(), hide()
	 */
	var sizeMidPanes = function (panes, skipCallback, force) {
		if (!panes || panes == "all") panes = "east,west,center";

		$.each(panes.split(","), function (i, pane) {
			if (!$Ps[pane]) return; // NO PANE - skip
			var 
				o		= options[pane]
			,	s		= state[pane]
			,	$P		= $Ps[pane]
			,	$R		= $Rs[pane]
			,	isCenter= (pane=="center")
			,	hasRoom	= true
			,	CSS		= {}
			,	d		= calcNewCenterPaneDims()
			;
			// update pane-state dimensions
			$.extend(s, getElemDims($P));

			if (pane == "center") {
				if (!force && s.isVisible && d.width == s.outerWidth && d.height == s.outerHeight)
					return true; // SKIP - pane already the correct size
				// set state for makePaneFit() logic
				$.extend(s, cssMinDims(pane), {
					maxWidth:		d.width
				,	maxHeight:		d.height
				});
				CSS = d;
				// convert OUTER width/height to CSS width/height 
				CSS.width	= cssW(pane, d.width);
				CSS.height	= cssH(pane, d.height);
				hasRoom		= CSS.width > 0 && CSS.height > 0;
			}
			else { // for east and west, set only the height, which is same as center height
				// set state.min/maxWidth/Height for makePaneFit() logic
				$.extend(s, getElemDims($P), cssMinDims(pane))
				if (!force && !s.noVerticalRoom && d.height == s.outerHeight)
					return true; // SKIP - pane already the correct size
				CSS.top			= d.top;
				CSS.bottom		= d.bottom;
				CSS.height		= cssH(pane, d.height);
				s.maxHeight	= max(0, CSS.height);
				hasRoom			= (s.maxHeight > 0);
				if (!hasRoom) s.noVerticalRoom = true; // makePaneFit() logic
			}

			if (hasRoom) {
				$P.css(CSS); // apply the CSS to pane
				if (pane == "center") $.extend(s, getElemDims($P)); // set new dimensions
				if (s.noRoom) makePaneFit(pane); // will re-open/show auto-closed/hidden pane
				if (state.initialized) sizeContent(pane);
			}
			else if (!s.noRoom && s.isVisible) // no room for pane
				makePaneFit(pane); // will hide or close pane

			/*
			 * Extra CSS for IE6 or IE7 in Quirks-mode - add 'width' to NORTH/SOUTH panes
			 * Normally these panes have only 'left' & 'right' positions so pane auto-sizes
			 * ALSO required when pane is an IFRAME because will NOT default to 'full width'
			 */
			if (pane == "center") { // finished processing midPanes
				var b = state.browser;
				var fix = b.isIE6 || (b.msie && !b.boxModel);
				if ($Ps.north && (fix || state.north.tagName=="IFRAME")) 
					$Ps.north.css("width", cssW($Ps.north, sC.innerWidth));
				if ($Ps.south && (fix || state.south.tagName=="IFRAME"))
					$Ps.south.css("width", cssW($Ps.south, sC.innerWidth));
			}

			// resizeAll passes skipCallback because it triggers callbacks after ALL panes are resized
			if (!skipCallback && state.initialized && s.isVisible)
				_execCallback(pane, o.onresize_end || o.onresize);
		});
	};


	/**
	 * resizeAll
	 *
	 * @callers  window.onresize(), callbacks or custom code
	 */
	var resizeAll = function () {
		var
			oldW	= sC.innerWidth
		,	oldH	= sC.innerHeight
		;
		$.extend( state.container, getElemDims( $Container ) ); // UPDATE container dimensions
		if (!sC.outerHeight) return; // cannot size layout when 'container' is hidden or collapsed

		// onresizeall_start will CANCEL resizing if returns false
		// state.container has already been set, so user can access this info for calcuations
		if (false === _execCallback(null, options.onresizeall_start)) return false;

		var
			// see if container is now 'smaller' than before
			shrunkH	= (sC.innerHeight < oldH)
		,	shrunkW	= (sC.innerWidth < oldW)
		,	o, s, dir
		;
		// NOTE special order for sizing: S-N-E-W
		$.each(["south","north","east","west"], function (i, pane) {
			if (!$Ps[pane]) return; // no pane - SKIP
			s	= state[pane];
			o	= options[pane];
			dir	= _c[pane].dir;

			if (o.autoResize && s.size != o.size) // resize pane to original size set in options
				sizePane(pane, o.size, true); // true - skipCallback
			else {
				setSizeLimits(pane);
				makePaneFit(pane, false, true); // true - skipCallback
			}
		});

		sizeMidPanes("all", true); // true - skipCallback
		sizeHandles("all"); // reposition the toggler elements

		// trigger all individual pane callbacks AFTER layout has finished resizing
		o = options; // reuse alias
		$.each(_c.allPanes.split(","), function (i, pane) {
			if (state[pane].isVisible) // undefined for non-existent panes
				_execCallback(pane, o[pane].onresize_end || o[pane].onresize); // callback - if exists
		});

		_execCallback(null, o.onresizeall_end || o.onresizeall); // onresizeall callback, if exists
	};


	/**
	 * sizeContent
	 *
	 * IF pane has a content-div, then resize all elements inside pane to fit pane-height
	 */
	var sizeContent = function (panes) {
		if (!panes || panes == "all") panes = _c.allPanes;
		$.each(panes.split(","), function (idx, pane) {
			var 
				$P	= $Ps[pane]
			,	$C	= $Cs[pane]
			,	o	= options[pane]
			,	s	= state[pane]
			,	m	= s.content
			;
			if ($P && $C && $P.is(":visible")) { // if No Content OR Pane not visible, then skip
				var eC = $C[0];
				function setOffsets () {
					$.swap( $C[0], { height: "auto", display: "block", visibility: "hidden" }, function(){
						m.above = eC.offsetTop;
						m.below = $P.innerHeight() - eC.offsetTop - eC.offsetHeight;
					});
				};
				// defer remeasuring offsets while live-resizing
				if (o.resizeContentWhileDragging || !s.isResizing || m.above == undefined) 
					// let pane size-to-fit (invisibly), then measure the Content offset from top & bottom
					$.swap( $P[0], { position: "relative", height: "auto", visibility: "hidden" }, setOffsets );
				// resize the Content element to fit actual pane-size -  will autoHide if not enough room
				setOuterHeight($C, $P.innerHeight() - m.above - m.below, true); // true=autoHide
			}
		});
	};


	/**
	 * sizeHandles
	 *
	 * Called every time a pane is opened, closed, or resized to slide the togglers to 'center' and adjust their length if necessary
	 *
	 * @callers  initHandles(), open(), close(), resizeAll()
	 */
	var sizeHandles = function (panes) {
		if (!panes || panes == "all") panes = _c.borderPanes;

		$.each(panes.split(","), function (i, pane) {
			var 
				o	= options[pane]
			,	s	= state[pane]
			,	$P	= $Ps[pane]
			,	$R	= $Rs[pane]
			,	$T	= $Ts[pane]
			,	$TC
			;
			if (!$P || !$R) return;

			var
				dir			= _c[pane].dir
			,	_state		= (s.isClosed ? "_closed" : "_open")
			,	spacing		= o["spacing"+ _state]
			,	togAlign	= o["togglerAlign"+ _state]
			,	togLen		= o["togglerLength"+ _state]
			,	paneLen
			,	offset
			,	CSS = {}
			;

			if (spacing == 0) {
				$R.hide();
				return;
			}
			else if (!s.noRoom && !s.isHidden) // skip if resizer was hidden for any reason
				$R.show(); // in case was previously hidden

			// Resizer Bar is ALWAYS same width/height of pane it is attached to
			if (dir == "horz") { // north/south
				paneLen = $P.outerWidth(); // s.outerWidth || 
				s.resizerLength = paneLen;
				$R.css({
					width:	max(1, cssW($R, paneLen)) // account for borders & padding
				,	height:	max(0, cssH($R, spacing)) // ditto
				,	left:	_cssNum($P, "left")
				});
			}
			else { // east/west
				paneLen = $P.outerHeight(); // s.outerHeight || 
				s.resizerLength = paneLen;
				$R.css({
					height:	max(1, cssH($R, paneLen)) // account for borders & padding
				,	width:	max(0, cssW($R, spacing)) // ditto
				,	top:	sC.insetTop + getPaneSize("north", true) // TODO: what if no North pane?
				//,	top:	_cssNum($Ps["center"], "top")
				});
			}

			// remove hover classes
			removeHover( o, $R );

			if ($T) {
				if (togLen == 0 || (s.isSliding && o.hideTogglerOnSlide)) {
					$T.hide(); // always HIDE the toggler when 'sliding'
					return;
				}
				else
					$T.show(); // in case was previously hidden

				if (!(togLen > 0) || togLen == "100%" || togLen > paneLen) {
					togLen = paneLen;
					offset = 0;
				}
				else { // calculate 'offset' based on options.PANE.togglerAlign_open/closed
					if (typeof togAlign == "string") {
						switch (togAlign) {
							case "top":
							case "left":	offset = 0;
											break;
							case "bottom":
							case "right":	offset = paneLen - togLen;
											break;
							case "middle":
							case "center":
							default:		offset = Math.floor((paneLen - togLen) / 2); // 'default' catches typos
						}
					}
					else { // togAlign = number
						var x = parseInt(togAlign); //
						if (togAlign >= 0) offset = x;
						else offset = paneLen - togLen + x; // NOTE: x is negative!
					}
				}

				if (dir == "horz") { // north/south
					var width = cssW($T, togLen);
					$T.css({
						width:	max(0, width)  // account for borders & padding
					,	height:	max(1, cssH($T, spacing)) // ditto
					,	left:	offset // TODO: VERIFY that toggler  positions correctly for ALL values
					,	top:	0
					});
					// CENTER the toggler content SPAN
					$T.children(".content").each(function(){
						$TC = $(this);
						$TC.css("marginLeft", Math.floor((width-$TC.outerWidth())/2)); // could be negative
					});
				}
				else { // east/west
					var height = cssH($T, togLen);
					$T.css({
						height:	max(0, height)  // account for borders & padding
					,	width:	max(1, cssW($T, spacing)) // ditto
					,	top:	offset // POSITION the toggler
					,	left:	0
					});
					// CENTER the toggler content SPAN
					$T.children(".content").each(function(){
						$TC = $(this);
						$TC.css("marginTop", Math.floor((height-$TC.outerHeight())/2)); // could be negative
					});
				}

				// remove ALL hover classes
				removeHover( 0, $T );
			}

			// DONE measuring and sizing this resizer/toggler, so can be 'hidden' now
			if (!state.initialized && o.initHidden) {
				$R.hide();
				if ($T) $T.hide();
			}
		});
	};


	/**
	 *	swapPanes
	 *
	 *	Move a pane from source-side (eg, west) to target-side (eg, east)
	 *	If pane exists on target-side, move that to source-side, ie, 'swap' the panes
	 */
	var swapPanes = function (pane1, pane2) {
		var
			oPane1	= copy( pane1 )
		,	oPane2	= copy( pane2 )
		,	sizes	= {}
		;
		sizes[pane1] = oPane1 ? oPane1.state.size : 0;
		sizes[pane2] = oPane2 ? oPane2.state.size : 0;

		// clear pointers & state
		$Ps[pane1] = false; 
		$Ps[pane2] = false;
		state[pane1] = {};
		state[pane2] = {};
		
		// transfer element pointers and data to NEW Layout keys
		move( oPane1, pane2 );
		move( oPane2, pane1 );

		// cleanup objects
		oPane1 = oPane2 = sizes = null;

		// pane1 does not exist anymore
		if (!$Ps[pane1] && $Rs[pane1]) {
			$Rs[pane1].remove();
			$Rs[pane1] = $Ts[pane1] = false;
		}

		// pane2 does not exist anymore
		if (!$Ps[pane2] && $Rs[pane2]) {
			$Rs[pane2].remove();
			$Rs[pane2] = $Ts[pane2] = false;
		}

		// make panes 'visible' again
		if ($Ps[pane1]) $Ps[pane1].css(_c.visible);
		if ($Ps[pane2]) $Ps[pane2].css(_c.visible);

		// fix any size discrepancies caused by swap
		resizeAll();

		return;

		function copy (n) { // n = pane
			var
				$P	= $Ps[n]
			,	$C	= $Cs[n]
			;
			return !$P ? false : {
				pane:		n
			,	P:			$P ? $P[0] : false
			,	C:			$C ? $C[0] : false
			,	state:		$.extend({}, state[n])
			,	options:	$.extend({}, options[n])
			}
		};

		function move (oPane, pane) {
			if (!oPane) return;
			var
				P		= oPane.P
			,	C		= oPane.C
			,	oldPane = oPane.pane
			,	c		= _c[pane]
			,	side	= c.side.toLowerCase()
			,	inset	= "inset"+ c.side
			//	save pane-options that should be retained
			,	s		= $.extend({}, state[pane])
			,	o		= options[pane]
			//	RETAIN side-specific FX Settings - more below
			,	fx		= { resizerCursor: o.resizerCursor }
			,	re, size, pos
			;
			$.each("fxName,fxSpeed,fxSettings".split(","), function (i, k) {
				fx[k] = o[k];
				fx[k +"_open"]  = o[k +"_open"];
				fx[k +"_close"] = o[k +"_close"];
			});

			// update object pointers and attributes
			$Ps[pane] = $(P)
				.data("layoutEdge", pane)
				.css(_c.hidden)
				.css(c.cssReq)
			;
			$Cs[pane] = C ? $(C) : false;

			// set options and state
			options[pane]	= $.extend({}, oPane.options, fx);
			state[pane]		= $.extend({}, oPane.state);

			// change classNames on the pane, eg: ui-layout-pane-east ==> ui-layout-pane-west
			re = new RegExp("pane-"+ oldPane, "g");
			P.className = P.className.replace(re, "pane-"+ pane);

			if (!$Rs[pane]) {
				initHandles(pane); // create the required resizer & toggler
				initResizable(pane);
			}

			// if moving to different orientation, then keep 'target' pane size
			if (c.dir != _c[oldPane].dir) {
				size = sizes[pane] || 0;
				setSizeLimits(pane); // update pane-state
				size = max(size, state[pane].minSize);
				// use manualSizePane to disable autoResize - not useful after panes are swapped
				manualSizePane(pane, size, true); // true = skipCallback
			}
			else // move the resizer here
				$Rs[pane].css(side, sC[inset] + (state[pane].isVisible ? getPaneSize(pane) : 0));


			// ADD CLASSNAMES & SLIDE-BINDINGS
			if (oPane.state.isVisible && !s.isVisible)
				setAsOpen(pane, true); // true = skipCallback
			else {
				setAsClosed(pane, true); // true = skipCallback
				bindStartSlidingEvent(pane, true); // will enable events IF option is set
			}

			// DESTROY the object
			oPane = null;
		};
	};


	/**
	 * keyDown
	 *
	 * Capture keys when enableCursorHotkey - toggle pane if hotkey pressed
	 *
	 * @callers  document.keydown()
	 */
	function keyDown (evt) {
		if (!evt) return true;
		var code = evt.keyCode;
		if (code < 33) return true; // ignore special keys: ENTER, TAB, etc

		var
			PANE = {
				38: "north" // Up Cursor	- $.ui.keyCode.UP
			,	40: "south" // Down Cursor	- $.ui.keyCode.DOWN
			,	37: "west"  // Left Cursor	- $.ui.keyCode.LEFT
			,	39: "east"  // Right Cursor	- $.ui.keyCode.RIGHT
			}
		,	ALT		= evt.altKey // no worky!
		,	SHIFT	= evt.shiftKey
		,	CTRL	= evt.ctrlKey
		,	CURSOR	= (CTRL && code >= 37 && code <= 40)
		,	o, k, m, pane
		;

		if (CURSOR && options[PANE[code]].enableCursorHotkey) // valid cursor-hotkey
			pane = PANE[code];
		else if (CTRL || SHIFT) // check to see if this matches a custom-hotkey
			$.each(_c.borderPanes.split(","), function (i, p) { // loop each pane to check its hotkey
				o = options[p];
				k = o.customHotkey;
				m = o.customHotkeyModifier; // if missing or invalid, treated as "CTRL+SHIFT"
				if ((SHIFT && m=="SHIFT") || (CTRL && m=="CTRL") || (CTRL && SHIFT)) { // Modifier matches
					if (k && code == (isNaN(k) || k <= 9 ? k.toUpperCase().charCodeAt(0) : k)) { // Key matches
						pane = p;
						return false; // BREAK
					}
				}
			});

		// validate pane
		if (!pane || !$Ps[pane] || !options[pane].closable || state[pane].isHidden)
			return true;

		toggle(pane);

		evt.stopPropagation();
		evt.returnValue = false; // CANCEL key
		return false;
	};


/*
 * ######################################
 *      UTILITY METHODS
 *   called externally or by initButtons
 * ######################################
 */

	/**
	* allowOverflow / resetOverflow
	*
	* Change/reset a pane's overflow setting & zIndex to allow popups/drop-downs to work
	*
	* @param element   elem 	Optional - can also be 'bound' to a click, mouseOver, or other event
	*/
	function allowOverflow (el) {
		if (this && this.tagName) el = this; // BOUND to element
		var $P;
		if (typeof el == "string")
			$P = $Ps[el];
		else if ($(el).data("layoutRole"))
			$P = $(el);
		else
			$(el).parents.each(function(){
				if ($(this).data("layoutRole")) {
					$P = $(this);
					return false; // BREAK
				}
			});
		if (!$P || !$P.length) return; // INVALID

		var
			pane	= $P.data("layoutEdge")
		,	s		= state[pane]
		;

		// if pane is already raised, then reset it before doing it again!
		// this would happen if allowOverflow is attached to BOTH the pane and an element 
		if (s.cssSaved)
			resetOverflow(pane); // reset previous CSS before continuing

		// if pane is raised by sliding or resizing, or it's closed, then abort
		if (s.isSliding || s.isResizing || s.isClosed) {
			s.cssSaved = false;
			return;
		}

		var
			newCSS	= { zIndex: (_c.zIndex.pane_normal + 1) }
		,	curCSS	= {}
		,	of		= $P.css("overflow")
		,	ofX		= $P.css("overflowX")
		,	ofY		= $P.css("overflowY")
		;
		// determine which, if any, overflow settings need to be changed
		if (of != "visible") {
			curCSS.overflow = of;
			newCSS.overflow = "visible";
		}
		if (ofX && ofX != "visible" && ofX != "auto") {
			curCSS.overflowX = ofX;
			newCSS.overflowX = "visible";
		}
		if (ofY && ofY != "visible" && ofY != "auto") {
			curCSS.overflowY = ofX;
			newCSS.overflowY = "visible";
		}

		// save the current overflow settings - even if blank!
		s.cssSaved = curCSS;

		// apply new CSS to raise zIndex and, if necessary, make overflow 'visible'
		$P.css( newCSS );

		// make sure the zIndex of all other panes is normal
		$.each(_c.allPanes.split(","), function(i, p) {
			if (p != pane) resetOverflow(p);
		});

	};

	function resetOverflow (el) {
		if (this && this.tagName) el = this; // BOUND to element
		var $P;
		if (typeof el == "string")
			$P = $Ps[el];
		else if ($(el).data("layoutRole"))
			$P = $(el);
		else
			$(el).parents.each(function(){
				if ($(this).data("layoutRole")) {
					$P = $(this);
					return false; // BREAK
				}
			});
		if (!$P || !$P.length) return; // INVALID

		var
			pane	= $P.data("layoutEdge")
		,	s		= state[pane]
		,	CSS		= s.cssSaved || {}
		;
		// reset the zIndex
		if (!s.isSliding && !s.isResizing)
			$P.css("zIndex", _c.zIndex.pane_normal);

		// reset Overflow - if necessary
		$P.css( CSS );

		// clear var
		s.cssSaved = false;
	};


	/**
	* getBtn
	*
	* Helper function to validate params received by addButton utilities
	*
	* Two classes are added to the element, based on the buttonClass...
	* The type of button is appended to create the 2nd className:
	*  - ui-layout-button-pin
	*  - ui-layout-pane-button-toggle
	*  - ui-layout-pane-button-open
	*  - ui-layout-pane-button-close
	*
	* @param String   selector 	jQuery selector for button, eg: ".ui-layout-north .toggle-button"
	* @param String   pane 		Name of the pane the button is for: 'north', 'south', etc.
	* @returns  If both params valid, the element matching 'selector' in a jQuery wrapper - otherwise 'false'
	*/
	function getBtn(selector, pane, action) {
		var $E	= $(selector);
		if (!$E.length) // element not found
			alert(lang.errButton + lang.selector +": "+ selector);
		else if (_c.borderPanes.indexOf(pane) == -1) // invalid 'pane' sepecified
			alert(lang.errButton + lang.Pane.toLowerCase() +": "+ pane);
		else { // VALID
			var btn = options[pane].buttonClass +"-"+ action;
			$E
				.addClass( btn +" "+ btn +"-"+ pane )
				.data("layoutName", options.name) // add layout identifier - even if blank!
			;
			return $E;
		}
		return false;  // INVALID
	};


	/**
	* bindButton
	*
	* NEW syntax for binding layout-buttons - will eventually replace addToggleBtn, addOpenBtn, etc.
	*
	*/
	function bindButton (selector, action, pane) {
		switch (action.toLowerCase()) {
			case "toggle":			addToggleBtn(selector, pane);		break;	
			case "open":			addOpenBtn(selector, pane);			break;
			case "close":			addCloseBtn(selector, pane);		break;
			case "pin":				addPinBtn(selector, pane);			break;
			case "toggle-slide":	addToggleBtn(selector, pane, true);	break;	
			case "open-slide":		addOpenBtn(selector, pane, true);	break;
		}
	};

	/**
	* addToggleBtn
	*
	* Add a custom Toggler button for a pane
	*
	* @param String   selector 	jQuery selector for button, eg: ".ui-layout-north .toggle-button"
	* @param String   pane 		Name of the pane the button is for: 'north', 'south', etc.
	*/
	function addToggleBtn (selector, pane, slide) {
		var $E = getBtn(selector, pane, "toggle");
		if ($E)
			$E.click(function (evt) {
				toggle(pane, !!slide);
				evt.stopPropagation();
			});
	};

	/**
	* addOpenBtn
	*
	* Add a custom Open button for a pane
	*
	* @param String   selector 	jQuery selector for button, eg: ".ui-layout-north .open-button"
	* @param String   pane 		Name of the pane the button is for: 'north', 'south', etc.
	*/
	function addOpenBtn (selector, pane, slide) {
		var $E = getBtn(selector, pane, "open");
		if ($E)
			$E
				.attr("title", lang.Open)
				.click(function (evt) {
					open(pane, !!slide);
					evt.stopPropagation();
				})
			;
	};

	/**
	* addCloseBtn
	*
	* Add a custom Close button for a pane
	*
	* @param String   selector 	jQuery selector for button, eg: ".ui-layout-north .close-button"
	* @param String   pane 		Name of the pane the button is for: 'north', 'south', etc.
	*/
	function addCloseBtn (selector, pane) {
		var $E = getBtn(selector, pane, "close");
		if ($E)
			$E
				.attr("title", lang.Close)
				.click(function (evt) {
					close(pane);
					evt.stopPropagation();
				})
			;
	};

	/**
	* addPinBtn
	*
	* Add a custom Pin button for a pane
	*
	* Four classes are added to the element, based on the paneClass for the associated pane...
	* Assuming the default paneClass and the pin is 'up', these classes are added for a west-pane pin:
	*  - ui-layout-pane-pin
	*  - ui-layout-pane-west-pin
	*  - ui-layout-pane-pin-up
	*  - ui-layout-pane-west-pin-up
	*
	* @param String   selector 	jQuery selector for button, eg: ".ui-layout-north .ui-layout-pin"
	* @param String   pane 		Name of the pane the pin is for: 'north', 'south', etc.
	*/
	function addPinBtn (selector, pane) {
		var $E = getBtn(selector, pane, "pin");
		if ($E) {
			var s = state[pane];
			$E.click(function (evt) {
				setPinState($(this), pane, (s.isSliding || s.isClosed));
				if (s.isSliding || s.isClosed) open( pane ); // change from sliding to open
				else close( pane ); // slide-closed
				evt.stopPropagation();
			});
			// add up/down pin attributes and classes
			setPinState ($E, pane, (!s.isClosed && !s.isSliding));
			// add this pin to the pane data so we can 'sync it' automatically
			// PANE.pins key is an array so we can store multiple pins for each pane
			_c[pane].pins.push( selector ); // just save the selector string
		}
	};

	/**
	* syncPinBtns
	*
	* INTERNAL function to sync 'pin buttons' when pane is opened or closed
	* Unpinned means the pane is 'sliding' - ie, over-top of the adjacent panes
	*
	* @callers  open(), close()
	* @params  pane   These are the params returned to callbacks by layout()
	* @params  doPin  True means set the pin 'down', False means 'up'
	*/
	function syncPinBtns (pane, doPin) {
		$.each(_c[pane].pins, function (i, selector) {
			setPinState($(selector), pane, doPin);
		});
	};

	/**
	* setPinState
	*
	* Change the class of the pin button to make it look 'up' or 'down'
	*
	* @callers  addPinBtn(), syncPinBtns()
	* @param Element  $Pin		The pin-span element in a jQuery wrapper
	* @param Boolean  doPin		True = set the pin 'down', False = set it 'up'
	* @param String   pinClass	The root classname for pins - will add '-up' or '-down' suffix
	*/
	function setPinState ($Pin, pane, doPin) {
		var updown = $Pin.attr("pin");
		if (updown && doPin == (updown=="down")) return; // already in correct state
		var
			pin		= options[pane].buttonClass +"-pin"
		,	side	= pin +"-"+ pane
		,	UP		= pin +"-up "+	side +"-up"
		,	DN		= pin +"-down "+side +"-down"
		;
		$Pin
			.attr("pin", doPin ? "down" : "up") // logic
			.attr("title", doPin ? lang.Unpin : lang.Pin)
			.removeClass( doPin ? UP : DN ) 
			.addClass( doPin ? DN : UP ) 
		;
	};


	/*
	 *	LAYOUT STATE MANAGEMENT
	 *
	 *	@example .layout({ cookie: { name: "myLayout", keys: "west.isClosed,east.isClosed" } })
	 *	@example .layout({ cookie__name: "myLayout", cookie__keys: "west.isClosed,east.isClosed" })
	 *	@example myLayout.getState( "west.isClosed,north.size,south.isHidden" );
	 *	@example myLayout.saveCookie( "west.isClosed,north.size,south.isHidden", {expires: 7} );
	 *	@example myLayout.deleteCookie();
	 *	@example myLayout.loadCookie();
	 *	@example var hSaved = myLayout.state.cookie;
	 */

	function isCookiesEnabled () {
		// TODO: is the cookieEnabled property common enough to be useful???
		return (navigator.cookieEnabled != 0);
	};
	
	/*
	 * getCookie
	 *
	 * Read & return data from the cookie - as JSON
	 */
	function getCookie (opts) {
		var
			o		= $.extend( {}, options.cookie, opts || {} )
		,	name	= o.name || options.name || "Layout"
		,	c		= document.cookie
		,	cs		= c ? c.split(';') : []
		,	pair	// loop var
		;
		for (var i=0, n=cs.length; i < n; i++) {
			pair = $.trim(cs[i]).split('='); // name=value pair
			if (pair[0] == name) // found the layout cookie
				// convert cookie string back to a hash
				return decodeJSON( decodeURIComponent(pair[1]) );
		}
		return "";
	};

	/*
	 * saveCookie
	 *
	 * Get the current layout state and save it to a cookie
	 */
	function saveCookie (keys, opts) {
		var
			o		= $.extend( {}, options.cookie, opts || {} )
		,	name	= o.name || options.name || "Layout"
		,	params	= ''
		,	date	= ''
		,	clear	= false
		;
		if (o.expires.toUTCString)
			date = o.expires;
		else if (typeof o.expires == 'number') {
			date = new Date();
			if (o.expires > 0)
				date.setDate(date.getDate() + o.expires);
			else {
				date.setYear(1970);
				clear = true;
			}
		}
		if (date)		params += ';expires='+ date.toUTCString();
		if (o.path)		params += ';path='+ o.path;
		if (o.domain)	params += ';domain='+ o.domain;
		if (o.secure)	params += ';secure';

		if (clear) {
			state.cookie = {}; // clear data
			document.cookie = name +'='+ params; // expire the cookie
		}
		else {
			state.cookie = getState(keys || o.keys); // read current panes-state
			document.cookie = name +'='+ encodeURIComponent( encodeJSON(state.cookie) ) + params; // write cookie
		}

		return $.extend({}, state.cookie); // return COPY of state.cookie
	};

	/*
	 * deleteCookie
	 *
	 * Remove the state cookie
	 */
	function deleteCookie () {
		saveCookie('', { expires: -1 });
	};

	/*
	 * loadCookie
	 *
	 * Get data from the cookie and USE IT to loadState
	 */
	function loadCookie (opts) {
		var o = getCookie(opts); // READ the cookie
		if (o) {
			state.cookie = $.extend({}, o); // SET state.cookie
			loadState(o);	// LOAD the retrieved state
		}
		return o;
	};

	/*
	 * loadState
	 *
	 * Update layout options from the cookie, if one exists
	 */
	function loadState (opts) {
		$.extend( true, options, opts ); // update layout options
	};

	/*
	 * getState
	 *
	 * Get the *current layout state* and return it as a hash
	 */
	function getState (keys) {
		var
			data	= {}
		,	alt		= { isClosed: 'initClosed', isHidden: 'initHidden' }
		,	pair, pane, key, val
		;
		if (!keys) keys = options.cookie.keys; // if called by user
		if ($.isArray(keys)) keys = keys.join(",");
		// convert keys to an array and change delimiters from '__' to '.'
		keys = keys.replace(/__/g, ".").split(',');
		// loop keys and create a data hash
		for (var i=0,n=keys.length; i < n; i++) {
			pair = keys[i].split(".");
			pane = pair[0];
			key  = pair[1];
			if (_c.allPanes.indexOf(pane) < 0) continue; // bad pane!
			val = state[ pane ][ key ];
			if (val == undefined) continue;
			if (key=="isClosed" && state[pane]["isSliding"])
				val = true; // if sliding, then *really* isClosed
			( data[pane] || (data[pane]={}) )[ alt[key] ? alt[key] : key ] = val;
		}
		return data;
	};

	/*
	 * encodeJSON
	 *
	 * Stringify a JSON hash so can save in a cookie or db-field
	 */
	function encodeJSON (JSON) {
		return parse( JSON );
		function parse (h) {
			var D=[], i=0, k, v, t; // k = key, v = value
			for (k in h) {
				v = h[k];
				t = typeof v;
				if (t == 'string')		// STRING - add quotes
					v = '"'+ v +'"';
				else if (t == 'object')	// SUB-KEY - recurse into it
					v = parse(v);
				D[i++] = '"'+ k +'":'+ v;
			}
			return "{"+ D.join(",") +"}";
		};
	};

	/*
	 * decodeJSON
	 *
	 * Convert stringified JSON back to a hash object
	 */
	function decodeJSON (str) {
		try { return window["eval"]("("+ str +")") || {}; }
		catch (e) { return {}; }
	};


/*
 * #####################
 * CREATE/RETURN LAYOUT
 * #####################
 */

	// validate the container
	if (!$(this).length) {
		alert( lang.errContainerMissing );
		return {};
	};

	// init global vars
	var 
		$Container = $(this) // Container element
	,	$Ps	= {} // Panes x5	- set in initPanes()
	,	$Cs	= {} // Content x5	- set in initPanes()
	,	$Rs	= {} // Resizers x4	- set in initHandles()
	,	$Ts	= {} // Togglers x4	- set in initHandles()
	//	aliases for code brevity
	,	sC	= state.container // alias for easy access to 'container dimensions'
	,	sID	= state.id // alias for unique layout ID/namespace - eg: "layout435"
	;

	// return object pointers to expose data & option Properties, and primary action Methods
	var Instance = {
		options:		options			// property - options hash
	,	state:			state			// property - dimensions hash
	,	container:		$Container		// property - object pointers for layout container
	,	panes:			$Ps				// property - object pointers for ALL Panes: panes.north, panes.center
	,	resizers:		$Rs				// property - object pointers for ALL Resizers, eg: resizers.north
	,	togglers:		$Ts				// property - object pointers for ALL Togglers, eg: togglers.north
	,	toggle:			toggle			// method - pass a 'pane' ("north", "west", etc)
	,	open:			open			// method - ditto
	,	close:			close			// method - ditto
	,	hide:			hide			// method - ditto
	,	show:			show			// method - ditto
	,	resizeContent:	sizeContent		// method - ditto - DEPRICATED - "resize" is inconsistent
	,	sizeContent:	sizeContent		// method - pass a 'pane'
	,	sizePane:		manualSizePane	// method - pass a 'pane' AND an 'outer-size' in pixels or percent, or 'auto'
	,	swapPanes:		swapPanes		// method - pass TWO 'panes' - will swap them
	,	resizeAll:		resizeAll		// method - no parameters
	,	destroy:		destroy			// method - no parameters
	,	setSizeLimits:	setSizeLimits	// method - pass a 'pane' - update state min/max data
	,	bindButton:		bindButton		// utility - pass element selector, 'action' and 'pane' (E, "toggle", "west")
	,	addToggleBtn:	addToggleBtn	// utility - pass element selector and 'pane' (E, "west")
	,	addOpenBtn:		addOpenBtn		// utility - ditto
	,	addCloseBtn:	addCloseBtn		// utility - ditto
	,	addPinBtn:		addPinBtn		// utility - ditto
	,	allowOverflow:	allowOverflow	// utility - pass calling element (this)
	,	resetOverflow:	resetOverflow	// utility - ditto
	,	encodeJSON:		encodeJSON		// method - pass a JSON object
	,	decodeJSON:		decodeJSON		// method - pass a string of encoded JSON
	,	getState:		getState		// method - returns hash of current layout-state
	,	getCookie:		getCookie		// method - update options from cookie - returns hash of cookie data
	,	saveCookie:		saveCookie		// method - optionally pass keys-list and cookie-options (hash)
	,	deleteCookie:	deleteCookie	// method
	,	loadCookie:		loadCookie		// method - update options from cookie - returns hash of cookie data
	,	loadState:		loadState		// method - pass a hash of state to use to update options
	,	cssWidth:		cssW			// utility - pass element and target outerWidth
	,	cssHeight:		cssH			// utility - ditto
	,	isMouseOver:	isMouseOver		// utility - pass any element OR 'pane' - returns true or false
	};

	// create the border layout NOW
	create();

	// return the Instance object
	return Instance;

}
})( jQuery );;$.fn.childrenResize = function() {
    var currentWidth = $(this).width();
	resizeAll($(this), currentWidth);
	return this;
};

function resizeAll(element, width) {
  element.width = width;
  if (typeof element.children == 'function') {
    element.children().each(function(i, item){
	   resizeAll(item, width);
    });
  }
  else 
   for(var i=0; i < element.children.length; ++i) {
      resizeAll(element.children[i], width);
    }
};;(function($){
/**
 * jqGrid English Translation
 * Tony Tomov tony@trirand.com
 * http://trirand.com/blog/ 
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
**/
$.jgrid = {
	defaults : {
		recordtext: "View {0} - {1} of {2}",
		emptyrecords: "No records to view",
		loadtext: "Loading...",
		pgtext : "Page {0} of {1}",
        altRows: true,
		scrollOffset: 0,
		width: 962,
		height: '100%',
		loadui: 'disable'
	},
	search : {
		caption: "Search...",
		Find: "Find",
		Reset: "Reset",
		odata : ['equal', 'not equal', 'less', 'less or equal','greater','greater or equal', 'begins with','does not begin with','is in','is not in','ends with','does not end with','contains','does not contain'],
		groupOps: [	{ op: "AND", text: "all" },	{ op: "OR",  text: "any" }	],
		matchText: " match",
		rulesText: " rules"
	},
	edit : {
		addCaption: "Add Record",
		editCaption: "Edit Record",
		bSubmit: "Submit",
		bCancel: "Cancel",
		bClose: "Close",
		saveData: "Data has been changed! Save changes?",
		bYes : "Yes",
		bNo : "No",
		bExit : "Cancel",
		msg: {
			required:"Field is required",
			number:"Please, enter valid number",
			minValue:"value must be greater than or equal to ",
			maxValue:"value must be less than or equal to",
			email: "is not a valid e-mail",
			integer: "Please, enter valid integer value",
			date: "Please, enter valid date value",
			url: "is not a valid URL. Prefix required ('http://' or 'https://')",
			nodefined : " is not defined!",
			novalue : " return value is required!",
			customarray : "Custom function should return array!",
			customfcheck : "Custom function should be present in case of custom checking!"
			
		}
	},
	view : {
		caption: "View Record",
		bClose: "Close"
	},
	del : {
		caption: "Delete",
		msg: "Delete selected record(s)?",
		bSubmit: "Delete",
		bCancel: "Cancel"
	},
	nav : {
		edittext: "",
		edittitle: "Edit selected row",
		addtext:"",
		addtitle: "Add new row",
		deltext: "",
		deltitle: "Delete selected row",
		searchtext: "",
		searchtitle: "Find records",
		refreshtext: "",
		refreshtitle: "Reload Grid",
		alertcap: "Warning",
		alerttext: "Please, select row",
		viewtext: "",
		viewtitle: "View selected row"
	},
	col : {
		caption: "Select columns",
		bSubmit: "Ok",
		bCancel: "Cancel"
	},
	errors : {
		errcap : "Error",
		nourl : "No url is set",
		norecords: "No records to process",
		model : "Length of colNames <> colModel!"
	},
	formatter : {
		integer : {thousandsSeparator: " ", defaultValue: '0'},
		number : {decimalSeparator:".", thousandsSeparator: " ", decimalPlaces: 2, defaultValue: '0.00'},
		currency : {decimalSeparator:".", thousandsSeparator: " ", decimalPlaces: 2, prefix: "", suffix:"", defaultValue: '0.00'},
		date : {
			dayNames:   [
				"Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat",
				"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
			],
			monthNames: [
				"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
				"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
			],
			AmPm : ["am","pm","AM","PM"],
			S: function (j) {return j < 11 || j > 13 ? ['st', 'nd', 'rd', 'th'][Math.min((j - 1) % 10, 3)] : 'th'},
			srcformat: 'Y-m-d',
			newformat: 'd/m/Y',
			masks : {
				ISO8601Long:"Y-m-d H:i:s",
				ISO8601Short:"Y-m-d",
				ShortDate: "n/j/Y",
				LongDate: "l, F d, Y",
				FullDateTime: "l, F d, Y g:i:s A",
				MonthDay: "F d",
				ShortTime: "g:i A",
				LongTime: "g:i:s A",
				SortableDateTime: "Y-m-d\\TH:i:s",
				UniversalSortableDateTime: "Y-m-d H:i:sO",
				YearMonth: "F, Y"
			},
			reformatAfterEdit : false
		},
		baseLinkUrl: '',
		showAction: '',
		target: '',
		checkbox : {disabled:true},
		idName : 'id'
	}
};
})(jQuery);
;;(function($) {
    /*
    * jqGrid  3.7.2 - jQuery Grid
    * Copyright (c) 2008, Tony Tomov, tony@trirand.com
    * Dual licensed under the MIT and GPL licenses
    * http://www.opensource.org/licenses/mit-license.php
    * http://www.gnu.org/licenses/gpl-2.0.html
    * Date: 2010-07-12 
    */
    $.jgrid = $.jgrid || {};
    $.extend($.jgrid, {
        htmlDecode: function(value) {
            if (value == '&nbsp;' || value == '&#160;' || (value.length == 1 && value.charCodeAt(0) == 160)) { return ""; }
            return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
        },
        htmlEncode: function(value) {
            return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/\"/g, "&quot;");
        },
        format: function(format) { //jqgformat
            var args = $.makeArray(arguments).slice(1);
            if (format === undefined) { format = ""; }
            return format.replace(/\{(\d+)\}/g, function(m, i) {
                return args[i];
            });
        },
        getCellIndex: function(cell) {
            var c = $(cell);
            if (c.is('tr')) { return -1; }
            c = (!c.is('td') && !c.is('th') ? c.closest("td,th") : c)[0];
            if ($.browser.msie) { return $.inArray(c, c.parentNode.cells); }
            return c.cellIndex;
        },
        stripHtml: function(v) {
            v = v + "";
            var regexp = /<("[^"]*"|'[^']*'|[^'">])*>/gi;
            if (v) {
                v = v.replace(regexp, "");
                return (v && v !== '&nbsp;' && v !== '&#160;') ? v.replace(/\"/g, "'") : "";
            } else {
                return v;
            }
        },
        stringToDoc: function(xmlString) {
            var xmlDoc;
            if (typeof xmlString !== 'string') { return xmlString; }
            try {
                var parser = new DOMParser();
                xmlDoc = parser.parseFromString(xmlString, "text/xml");
            }
            catch (e) {
                xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
                xmlDoc.async = false;
                xmlDoc.loadXML(xmlString);
            }
            return (xmlDoc && xmlDoc.documentElement && xmlDoc.documentElement.tagName != 'parsererror') ? xmlDoc : null;
        },
        parse: function(jsonString) {
            var js = jsonString;
            if (js.substr(0, 9) == "while(1);") { js = js.substr(9); }
            if (js.substr(0, 2) == "/*") { js = js.substr(2, js.length - 4); }
            if (!js) { js = "{}"; }
            return ($.jgrid.useJSON === true && typeof (JSON) === 'object' && typeof (JSON.parse) === 'function') ?
		    JSON.parse(js) :
		    eval('(' + js + ')');
        },
        parseDate: function(format, date) {
            var tsp = { m: 1, d: 1, y: 1970, h: 0, i: 0, s: 0 }, k, hl, dM, ft;
            if (date && date !== null && date !== undefined) {
                date = $.trim(date);
                date = date.split(/[\\\/:_;.\t\T\s-]/);
                format = format.split(/[\\\/:_;.\t\T\s-]/);
                var dfmt = $.jgrid.formatter.date.monthNames;
                var afmt = $.jgrid.formatter.date.AmPm;
                var h12to24 = function(ampm, h) {
                    if (ampm === 0) { if (h == 12) { h = 0; } }
                    else { if (h != 12) { h += 12; } }
                    return h;
                };
                for (k = 0, hl = format.length; k < hl; k++) {
                    if (format[k] == 'M') {
                        dM = $.inArray(date[k], dfmt);
                        if (dM !== -1 && dM < 12) { date[k] = dM + 1; }
                    }
                    if (format[k] == 'F') {
                        dM = $.inArray(date[k], dfmt);
                        if (dM !== -1 && dM > 11) { date[k] = dM + 1 - 12; }
                    }
                    if (format[k] == 'a') {
                        dM = $.inArray(date[k], afmt);
                        if (dM !== -1 && dM < 2 && date[k] == afmt[dM]) {
                            date[k] = dM;
                            tsp.h = h12to24(date[k], tsp.h);
                        }
                    }
                    if (format[k] == 'A') {
                        dM = $.inArray(date[k], afmt);
                        if (dM !== -1 && dM > 1 && date[k] == afmt[dM]) {
                            date[k] = dM - 2;
                            tsp.h = h12to24(date[k], tsp.h);
                        }
                    }
                    if (date[k] !== undefined) {
                        tsp[format[k].toLowerCase()] = parseInt(date[k], 10);
                    }
                }
                tsp.m = parseInt(tsp.m, 10) - 1;
                var ty = tsp.y;
                if (ty >= 70 && ty <= 99) { tsp.y = 1900 + tsp.y; }
                else if (ty >= 0 && ty <= 69) { tsp.y = 2000 + tsp.y; }
            }
            return new Date(tsp.y, tsp.m, tsp.d, tsp.h, tsp.i, tsp.s, 0);
        },
        jqID: function(sid) {
            sid = sid + "";
            return sid.replace(/([\.\:\[\]])/g, "\\$1");
        },
        getAccessor: function(obj, expr) {
            var ret, p, prm, i;
            if (typeof expr === 'function') { return expr(obj); }
            ret = obj[expr];
            if (ret === undefined) {
                try {
                    if (typeof expr === 'string') {
                        prm = expr.split('.');
                    }
                    if (prm !== undefined) { i = prm.length; }
                    if (i) {
                        ret = obj;
                        while (ret && i--) {
                            p = prm.shift();
                            ret = ret[p];
                        }
                    }
                } catch (e) { }
            }
            return ret;
        },
        ajaxOptions: {},
        from: function(source, initalQuery) {
            // Original Author Hugo Bonacci
            // License MIT http://jlinq.codeplex.com/license
            var queryObject = function(d, q) {
                if (typeof (d) == "string") {
                    d = $.data(d);
                }
                var self = this,
		_data = d,
		_usecase = true,
		_trim = false,
		_query = q,
		_stripNum = /[\$,%]/g,
		_lastCommand = null,
		_lastField = null,
		_negate = false,
		_queuedOperator = "",
		_sorting = [],
		_useProperties = true;
                if (typeof (d) == "object" && d.push) {
                    if (d.length > 0) {
                        if (typeof (d[0]) != "object") {
                            _useProperties = false;
                        } else {
                            _useProperties = true;
                        }
                    }
                } else {
                    throw "data provides is not an array";
                }
                this._hasData = function() {
                    return _data === null ? false : _data.length === 0 ? false : true;
                };
                this._getStr = function(s) {
                    var phrase = [];
                    if (_trim) {
                        phrase.push("jQuery.trim(");
                    }
                    phrase.push("String(" + s + ")");
                    if (_trim) {
                        phrase.push(")");
                    }
                    if (!_usecase) {
                        phrase.push(".toLowerCase()");
                    }
                    return phrase.join("");
                };
                this._strComp = function(val) {
                    if (typeof (val) == "string") {
                        return ".toString()";
                    } else {
                        return "";
                    }
                };
                this._group = function(f, u) {
                    return ({ field: f.toString(), unique: u, items: [] });
                };
                this._toStr = function(phrase) {
                    if (_trim) {
                        phrase = $.trim(phrase);
                    }
                    if (!_usecase) {
                        phrase = phrase.toLowerCase();
                    }
                    phrase = phrase.toString().replace(new RegExp('\\"', "g"), '\\"');
                    return phrase;
                };
                this._funcLoop = function(func) {
                    var results = [];
                    $.each(_data, function(i, v) {
                        results.push(func(v));
                    });
                    return results;
                };
                this._append = function(s) {
                    if (_query === null) {
                        _query = "";
                    } else {
                        _query += _queuedOperator == "" ? " && " : _queuedOperator;
                    }
                    if (_negate) {
                        _query += "!";
                    }
                    _query += "(" + s + ")";
                    _negate = false;
                    _queuedOperator = "";
                };
                this._setCommand = function(f, c) {
                    _lastCommand = f;
                    _lastField = c;
                };
                this._resetNegate = function() {
                    _negate = false;
                };
                this._repeatCommand = function(f, v) {
                    if (_lastCommand === null) {
                        return self;
                    }
                    if (f != null && v != null) {
                        return _lastCommand(f, v);
                    }
                    if (_lastField === null) {
                        return _lastCommand(f);
                    }
                    if (!_useProperties) {
                        return _lastCommand(f);
                    }
                    return _lastCommand(_lastField, f);
                };
                this._equals = function(a, b) {
                    return (self._compare(a, b, 1) === 0);
                };
                this._compare = function(a, b, d) {
                    if (d === undefined) { d = 1; }
                    if (a === undefined) { a = null; }
                    if (b === undefined) { b = null; }
                    if (a === null && b === null) {
                        return 0;
                    }
                    if (a === null && b !== null) {
                        return 1;
                    }
                    if (a !== null && b === null) {
                        return -1;
                    }
                    if (!_usecase) {
                        a = a.toLowerCase();
                        b = b.toLowerCase();
                    }
                    if (a < b) { return -d; }
                    if (a > b) { return d; }
                    return 0;
                };
                this._performSort = function() {
                    if (_sorting.length === 0) { return; }
                    _data = self._doSort(_data, 0);
                };
                this._doSort = function(d, q) {
                    var by = _sorting[q].by,
			dir = _sorting[q].dir,
			type = _sorting[q].type,
			dfmt = _sorting[q].datefmt;
                    if (q == _sorting.length - 1) {
                        return self._getOrder(d, by, dir, type, dfmt);
                    }
                    q++;
                    var values = self._getGroup(d, by, dir, type, dfmt);
                    var results = [];
                    for (var i = 0; i < values.length; i++) {
                        var sorted = self._doSort(values[i].items, q);
                        for (var j = 0; j < sorted.length; j++) {
                            results.push(sorted[j]);
                        }
                    }
                    return results;
                };
                this._getOrder = function(data, by, dir, type, dfmt) {
                    var sortData = [], _sortData = [], newDir = dir == "a" ? 1 : -1, i, ab, j,
			findSortKey;

                    if (type === undefined) { type = "text"; }
                    if (type == 'float' || type == 'number' || type == 'currency' || type == 'numeric') {
                        findSortKey = function($cell) {
                            var key = parseFloat(String($cell).replace(_stripNum, ''));
                            return isNaN(key) ? 0.00 : key;
                        };
                    } else if (type == 'int' || type == 'integer') {
                        findSortKey = function($cell) {
                            return $cell ? parseFloat(String($cell).replace(_stripNum, '')) : 0;
                        };
                    } else if (type == 'date' || type == 'datetime') {
                        findSortKey = function($cell) {
                            return $.jgrid.parseDate(dfmt, $cell).getTime();
                        };
                    } else {
                        findSortKey = function($cell) {
                            if (!$cell) { $cell = ""; }
                            return $.trim($cell.toUpperCase());
                        };
                    }
                    $.each(data, function(i, v) {
                        ab = $.jgrid.getAccessor(v, by);
                        if (ab === undefined) { ab = ""; }
                        ab = findSortKey(ab);
                        _sortData.push({ 'vSort': ab, 'index': i });
                    });

                    _sortData.sort(function(a, b) {
                        a = a.vSort;
                        b = b.vSort;
                        return self._compare(a, b, newDir);
                    });
                    j = 0;
                    var nrec = data.length;
                    // overhead, but we do not change the original data.
                    while (j < nrec) {
                        i = _sortData[j].index;
                        sortData.push(data[i]);
                        j++;
                    }
                    return sortData;
                };
                this._getGroup = function(data, by, dir, type, dfmt) {
                    var results = [],
			group = null,
			last = null, val;
                    $.each(self._getOrder(data, by, dir, type, dfmt), function(i, v) {
                        val = $.jgrid.getAccessor(v, by);
                        if (val === undefined) { val = ""; }
                        if (!self._equals(last, val)) {
                            last = val;
                            if (group != null) {
                                results.push(group);
                            }
                            group = self._group(by, val);
                        }
                        group.items.push(v);
                    });
                    if (group != null) {
                        results.push(group);
                    }
                    return results;
                };
                this.ignoreCase = function() {
                    _usecase = false;
                    return self;
                };
                this.useCase = function() {
                    _usecase = true;
                    return self;
                };
                this.trim = function() {
                    _trim = true;
                    return self;
                };
                this.noTrim = function() {
                    _trim = false;
                    return self;
                };
                this.combine = function(f) {
                    var q = $.from(_data);
                    if (!_usecase) { q.ignoreCase(); }
                    if (_trim) { q.trim(); }
                    result = f(q).showQuery();
                    self._append(result);
                    return self;
                };
                this.execute = function() {
                    var match = _query, results = [];
                    if (match === null) {
                        return self;
                    }
                    $.each(_data, function(i) {
                        if (eval(match)) { results.push(this); }
                    });
                    _data = results;
                    return self;
                };
                this.data = function() {
                    return _data;
                };
                this.select = function(f) {
                    self._performSort();
                    if (!self._hasData()) { return []; }
                    self.execute();
                    if ($.isFunction(f)) {
                        var results = [];
                        $.each(_data, function(i, v) {
                            results.push(f(v));
                        });
                        return results;
                    }
                    return _data;
                };
                this.hasMatch = function(f) {
                    if (!self._hasData()) { return false; }
                    self.execute();
                    return _data.length > 0;
                };
                this.showQuery = function(cmd) {
                    var queryString = _query;
                    if (queryString === null) { queryString = "no query found"; }
                    if ($.isFunction(cmd)) {
                        cmd(queryString); return self;
                    }
                    return queryString;
                };
                this.andNot = function(f, v, x) {
                    _negate = !_negate;
                    return self.and(f, v, x);
                };
                this.orNot = function(f, v, x) {
                    _negate = !_negate;
                    return self.or(f, v, x);
                };
                this.not = function(f, v, x) {
                    return self.andNot(f, v, x);
                };
                this.and = function(f, v, x) {
                    _queuedOperator = " && ";
                    if (f === undefined) {
                        return self;
                    }
                    return self._repeatCommand(f, v, x);
                };
                this.or = function(f, v, x) {
                    _queuedOperator = " || ";
                    if (f === undefined) { return self; }
                    return self._repeatCommand(f, v, x);
                };
                this.isNot = function(f) {
                    _negate = !_negate;
                    return self.is(f);
                };
                this.is = function(f) {
                    self._append('this.' + f);
                    self._resetNegate();
                    return self;
                };
                this._compareValues = function(func, f, v, how, t) {
                    var fld;
                    if (_useProperties) {
                        fld = 'this.' + f;
                    } else {
                        fld = 'this';
                    }
                    if (v === undefined) { v = null; }
                    var val = v === null ? f : v,
			swst = t.stype === undefined ? "text" : t.stype;
                    switch (swst) {
                        case 'int':
                        case 'integer':
                            val = isNaN(Number(val)) ? '0' : val; // To be fixed with more inteligent code
                            fld = 'parseInt(' + fld + ',10)';
                            val = 'parseInt(' + val + ',10)';
                            break;
                        case 'float':
                        case 'number':
                        case 'numeric':
                            val = String(val).replace(_stripNum, '');
                            val = isNaN(Number(val)) ? '0' : val; // To be fixed with more inteligent code
                            fld = 'parseFloat(' + fld + ')';
                            val = 'parseFloat(' + val + ')';
                            break;
                        case 'date':
                        case 'datetime':
                            val = String($.jgrid.parseDate(t.newfmt || 'Y-m-d', val).getTime());
                            fld = 'jQuery.jgrid.parseDate("' + t.srcfmt + '",' + fld + ').getTime()';
                            break;
                        default:
                            fld = self._getStr(fld);
                            val = self._getStr('"' + self._toStr(val) + '"');
                    }
                    self._append(fld + ' ' + how + ' ' + val);
                    self._setCommand(func, f);
                    self._resetNegate();
                    return self;
                };
                this.equals = function(f, v, t) {
                    return self._compareValues(self.equals, f, v, "==", t);
                };
                this.greater = function(f, v, t) {
                    return self._compareValues(self.greater, f, v, ">", t);
                };
                this.less = function(f, v, t) {
                    return self._compareValues(self.less, f, v, "<", t);
                };
                this.greaterOrEquals = function(f, v, t) {
                    return self._compareValues(self.greaterOrEquals, f, v, ">=", t);
                };
                this.lessOrEquals = function(f, v, t) {
                    return self._compareValues(self.lessOrEquals, f, v, "<=", t);
                };
                this.startsWith = function(f, v) {
                    var val = (v === undefined || v === null) ? f : v,
			length = _trim ? $.trim(val.toString()).length : val.toString().length;
                    if (_useProperties) {
                        self._append(self._getStr('this.' + f) + '.substr(0,' + length + ') == ' + self._getStr('"' + self._toStr(v) + '"'));
                    } else {
                        length = _trim ? $.trim(v.toString()).length : v.toString().length;
                        self._append(self._getStr('this') + '.substr(0,' + length + ') == ' + self._getStr('"' + self._toStr(f) + '"'));
                    }
                    self._setCommand(self.startsWith, f);
                    self._resetNegate();
                    return self;
                };
                this.endsWith = function(f, v) {
                    var val = (v === undefined || v === null) ? f : v,
			length = _trim ? $.trim(val.toString()).length : val.toString().length;
                    if (_useProperties) {
                        self._append(self._getStr('this.' + f) + '.substr(' + self._getStr('this.' + f) + '.length-' + length + ',' + length + ') == "' + self._toStr(v) + '"');
                    } else {
                        self._append(self._getStr('this') + '.substr(' + self._getStr('this') + '.length-"' + self._toStr(f) + '".length,"' + self._toStr(f) + '".length) == "' + self._toStr(f) + '"');
                    }
                    self._setCommand(self.endsWith, f); self._resetNegate();
                    return self;
                };
                this.contains = function(f, v) {
                    if (_useProperties) {
                        self._append(self._getStr('this.' + f) + '.indexOf("' + self._toStr(v) + '",0) > -1');
                    } else {
                        self._append(self._getStr('this') + '.indexOf("' + self._toStr(f) + '",0) > -1');
                    }
                    self._setCommand(self.contains, f);
                    self._resetNegate();
                    return self;
                };
                this.where = function(cmd) {
                    if ($.isFunction(cmd)) {
                        self.execute();
                        var results = [];
                        $.each(_data, function(i, v) {
                            if (cmd(v)) {
                                results.push(v);
                            }
                        });
                        return $.from(results, self.showQuery());
                    }
                    self._append(cmd);
                    self._setCommand(this.where, null);
                    self._resetNegate();
                    return self;
                };
                this.groupBy = function(by, dir, type, datefmt) {
                    if (!self._hasData()) {
                        return null;
                    }
                    return self._getGroup(_data, by, dir, type, datefmt);
                };
                this.distinct = function(by, dir) {
                    var groups = self.groupBy(by, dir);
                    var results = [];
                    $.each(groups, function(i, v) {
                        results.push(v.unique);
                    });
                    return results;
                };
                this.orderBy = function(by, dir, stype, dfmt) {
                    dir = dir === undefined || dir === null ? "a" : $.trim(dir.toString().toLowerCase());
                    if (stype === null || stype === undefined) { stype = "text"; }
                    if (dfmt === null || dfmt === undefined) { dfmt = "Y-m-d"; }
                    if (dir == "desc" || dir == "descending") { dir = "d"; }
                    if (dir == "asc" || dir == "ascending") { dir = "a"; }
                    _sorting.push({ by: by, dir: dir, type: stype, datefmt: dfmt });
                    return self;
                };
                return self;
            };
            return new queryObject(source, null);
        },
        extend: function(methods) {
            $.extend($.fn.jqGrid, methods);
            if (!this.no_legacy_api) {
                $.fn.extend(methods);
            }
        }
    });

    $.fn.jqGrid = function(pin) {
        if (typeof pin == 'string') {
            var fn = $.fn.jqGrid[pin];
            if (!fn) {
                throw ("jqGrid - No such method: " + pin);
            }
            var args = $.makeArray(arguments).slice(1);
            return fn.apply(this, args);
        }
        return this.each(function() {
            if (this.grid) { return; }

            var shrinkToFit = true;
            if (pin.shrinkToFit != undefined) {
                shrinkToFit = pin.shrinkToFit;
            }
            var p = $.extend(true, {
                url: "",
                height: 150,
                page: 1,
                rowNum: 20,
                rowTotal: null,
                records: 0,
                pager: "",
                pgbuttons: true,
                pginput: true,
                colModel: [],
                rowList: [],
                colNames: [],
                sortorder: "asc",
                sortname: "",
                datatype: "xml",
                mtype: "GET",
                altRows: false,
                selarrrow: [],
                savedRow: [],
                shrinkToFit: shrinkToFit,
                xmlReader: {},
                jsonReader: {},
                subGrid: false,
                subGridModel: [],
                reccount: 0,
                lastpage: 0,
                lastsort: 0,
                selrow: null,
                beforeSelectRow: null,
                onSelectRow: null,
                onSortCol: null,
                ondblClickRow: null,
                onRightClickRow: null,
                onPaging: null,
                onSelectAll: null,
                loadComplete: null,
                gridComplete: null,
                loadError: null,
                loadBeforeSend: null,
                afterInsertRow: null,
                beforeRequest: null,
                onHeaderClick: null,
                viewrecords: false,
                loadonce: false,
                multiselect: false,
                multikey: false,
                editurl: null,
                search: false,
                caption: "",
                hidegrid: true,
                hiddengrid: false,
                postData: {},
                userData: {},
                treeGrid: false,
                treeGridModel: 'nested',
                treeReader: {},
                treeANode: -1,
                ExpandColumn: null,
                tree_root_level: 0,
                prmNames: { page: "page", rows: "rows", sort: "sidx", order: "sord", search: "_search", nd: "nd", id: "id", oper: "oper", editoper: "edit", addoper: "add", deloper: "del", subgridid: "id", npage: null, totalrows: "totalrows" },
                forceFit: false,
                gridstate: "visible",
                cellEdit: false,
                cellsubmit: "remote",
                nv: 0,
                loadui: "enable",
                toolbar: [false, ""],
                scroll: false,
                multiboxonly: false,
                deselectAfterSort: true,
                scrollrows: false,
                autowidth: false,
                scrollOffset: 18,
                cellLayout: 17,
                subGridWidth: 20,
                multiselectWidth: 20,
                gridview: false,
                rownumWidth: 25,
                rownumbers: false,
                pagerpos: 'center',
                recordpos: 'right',
                footerrow: false,
                userDataOnFooter: false,
                hoverrows: true,
                altclass: 'ui-priority-secondary',
                viewsortcols: [false, 'vertical', true],
                resizeclass: '',
                autoencode: false,
                remapColumns: [],
                ajaxGridOptions: {},
                direction: "ltr",
                toppager: false,
                headertitles: false,
                scrollTimeout: 200,
                data: [],
                _index: {}
            }, $.jgrid.defaults, pin || {});
            var grid = {
                headers: [],
                cols: [],
                footers: [],
                dragStart: function(i, x, y) {
                    this.resizing = { idx: i, startX: x.clientX, sOL: y[0] };
                    this.hDiv.style.cursor = "col-resize";
                    this.curGbox = $("#rs_m" + p.id, "#gbox_" + p.id);
                    this.curGbox.css({ display: "block", left: y[0], top: y[1], height: y[2] });
                    if ($.isFunction(p.resizeStart)) { p.resizeStart.call(this, x, i); }
                    document.onselectstart = function() { return false; };
                },
                dragMove: function(x) {
                    if (this.resizing) {
                        var diff = x.clientX - this.resizing.startX,
					h = this.headers[this.resizing.idx],
					newWidth = p.direction === "ltr" ? h.width + diff : h.width - diff, hn, nWn;
                        if (newWidth > 33) {
                            this.curGbox.css({ left: this.resizing.sOL + diff });
                            if (p.forceFit === true) {
                                hn = this.headers[this.resizing.idx + p.nv];
                                nWn = p.direction === "ltr" ? hn.width - diff : hn.width + diff;
                                if (nWn > 33) {
                                    h.newWidth = newWidth;
                                    hn.newWidth = nWn;
                                }
                            } else {
                                this.newWidth = p.direction === "ltr" ? p.tblwidth + diff : p.tblwidth - diff;
                                h.newWidth = newWidth;
                            }
                        }
                    }
                },
                dragEnd: function() {
                    this.hDiv.style.cursor = "default";
                    if (this.resizing) {
                        var idx = this.resizing.idx,
					nw = this.headers[idx].newWidth || this.headers[idx].width;
                        nw = parseInt(nw, 10);
                        this.resizing = false;
                        $("#rs_m" + p.id).css("display", "none");
                        p.colModel[idx].width = nw;
                        this.headers[idx].width = nw;
                        this.headers[idx].el.style.width = nw + "px";
                        if (this.cols.length > 0) { this.cols[idx].style.width = nw + "px"; }
                        if (this.footers.length > 0) { this.footers[idx].style.width = nw + "px"; }
                        if (p.forceFit === true) {
                            nw = this.headers[idx + p.nv].newWidth || this.headers[idx + p.nv].width;
                            this.headers[idx + p.nv].width = nw;
                            this.headers[idx + p.nv].el.style.width = nw + "px";
                            if (this.cols.length > 0) { this.cols[idx + p.nv].style.width = nw + "px"; }
                            if (this.footers.length > 0) { this.footers[idx + p.nv].style.width = nw + "px"; }
                            p.colModel[idx + p.nv].width = nw;
                        } else {
                            p.tblwidth = this.newWidth || p.tblwidth;
                            $('table:first', this.bDiv).css("width", p.tblwidth + "px");
                            $('table:first', this.hDiv).css("width", p.tblwidth + "px");
                            this.hDiv.scrollLeft = this.bDiv.scrollLeft;
                            if (p.footerrow) {
                                $('table:first', this.sDiv).css("width", p.tblwidth + "px");
                                this.sDiv.scrollLeft = this.bDiv.scrollLeft;
                            }
                        }
                        if ($.isFunction(p.resizeStop)) { p.resizeStop.call(this, nw, idx); }
                    }
                    this.curGbox = null;
                    document.onselectstart = function() { return true; };
                },
                populateVisible: function() {
                    if (grid.timer) { clearTimeout(grid.timer); }
                    grid.timer = null;

                    var dh = $(grid.bDiv).height();
                    if (!dh) { return; }
                    var table = $("table:first", grid.bDiv);
                    var rows = $("> tbody > tr:visible:first", table);
                    var rh = rows.outerHeight() || grid.prevRowHeight;
                    if (!rh) { return; }
                    grid.prevRowHeight = rh;
                    var rn = p.rowNum;
                    var scrollTop = grid.scrollTop = grid.bDiv.scrollTop;
                    var ttop = Math.round(table.position().top) - scrollTop;
                    var tbot = ttop + table.height();
                    var div = rh * rn;
                    var page, npage, empty;
                    // tbot < dh &&
                    if (tbot < dh && ttop <= 0 &&
					(p.lastpage === undefined || parseInt((tbot + scrollTop + div - 1) / div, 10) <= p.lastpage)) {
                        npage = parseInt((dh - tbot + div - 1) / div, 10);
                        if (tbot >= 0 || npage < 2 || p.scroll === true) {
                            page = Math.round((tbot + scrollTop) / div) + 1;
                            ttop = -1;
                        } else {
                            ttop = 1;
                        }
                    }
                    if (ttop > 0) {
                        page = parseInt(scrollTop / div, 10) + 1;
                        npage = parseInt((scrollTop + dh) / div, 10) + 2 - page;
                        empty = true;
                    }
                    if (npage) {
                        if (p.lastpage && page > p.lastpage || p.lastpage == 1) {
                            return;
                        }
                        if (grid.hDiv.loading) {
                            grid.timer = setTimeout(grid.populateVisible, p.scrollTimeout);
                        } else {
                            p.page = page;
                            if (empty) {
                                grid.selectionPreserver(table[0]);
                                grid.emptyRows(grid.bDiv, false);
                            }
                            grid.populate(npage);
                        }
                    }
                },
                scrollGrid: function() {
                    if (p.scroll) {
                        var scrollTop = grid.bDiv.scrollTop;
                        if (grid.scrollTop === undefined) { grid.scrollTop = 0; }
                        if (scrollTop != grid.scrollTop) {
                            grid.scrollTop = scrollTop;
                            if (grid.timer) { clearTimeout(grid.timer); }
                            grid.timer = setTimeout(grid.populateVisible, p.scrollTimeout);
                        }
                    }
                    grid.hDiv.scrollLeft = grid.bDiv.scrollLeft;
                    if (p.footerrow) {
                        grid.sDiv.scrollLeft = grid.bDiv.scrollLeft;
                    }
                },
                selectionPreserver: function(ts) {
                    var p = ts.p;
                    var sr = p.selrow, sra = p.selarrrow ? $.makeArray(p.selarrrow) : null;
                    var left = ts.grid.bDiv.scrollLeft;
                    var complete = p.gridComplete;
                    p.gridComplete = function() {
                        p.selrow = null;
                        p.selarrrow = [];
                        if (p.multiselect && sra && sra.length > 0) {
                            for (var i = 0; i < sra.length; i++) {
                                if (sra[i] != sr) {
                                    $(ts).jqGrid("setSelection", sra[i], false);
                                }
                            }
                        }
                        if (sr) {
                            $(ts).jqGrid("setSelection", sr, false);
                        }
                        ts.grid.bDiv.scrollLeft = left;
                        p.gridComplete = complete;
                        if (p.gridComplete) {
                            complete();
                        }
                    };
                }
            };
            this.p = p;
            var i, dir, ts;
            if (this.p.colNames.length === 0) {
                for (i = 0; i < this.p.colModel.length; i++) {
                    this.p.colNames[i] = this.p.colModel[i].label || this.p.colModel[i].name;
                }
            }
            if (this.p.colNames.length !== this.p.colModel.length) {
                alert($.jgrid.errors.model);
                return;
            }
            var gv = $("<div class='ui-jqgrid-view'></div>"), ii,
		isMSIE = $.browser.msie ? true : false,
		isSafari = ($.browser.webkit || $.browser.safari) && (parseInt($.browser.version) < 536 || (parseInt($.browser.version) == 536 && ($.browser.version.indexOf('536.') < 0 || parseInt($.browser.version.substr(4)) < 5)));
            ts = this;
            ts.p.direction = $.trim(ts.p.direction.toLowerCase());
            if ($.inArray(ts.p.direction, ["ltr", "rtl"]) == -1) { ts.p.direction = "ltr"; }
            dir = ts.p.direction;

            $(gv).insertBefore(this);
            $(this).appendTo(gv).removeClass("scroll");
            var eg = $("<div class='ui-jqgrid ui-widget ui-widget-content ui-corner-all'></div>");
            $(eg).insertBefore(gv).attr({ "id": "gbox_" + this.id, "dir": dir });
            $(gv).appendTo(eg).attr("id", "gview_" + this.id);
            if (isMSIE && $.browser.version <= 6) {
                ii = '<iframe style="display:block;position:absolute;z-index:-1;filter:Alpha(Opacity=\'0\');" src="javascript:false;"></iframe>';
            } else { ii = ""; }
            $("<div class='ui-widget-overlay jqgrid-overlay' id='lui_" + this.id + "'></div>").append(ii).insertBefore(gv);
            $("<div class='loading ui-state-default ui-state-active' id='load_" + this.id + "'>" + this.p.loadtext + "</div>").insertBefore(gv);
            $(this).attr({ cellSpacing: "0", cellPadding: "0", border: "0", "role": "grid", "aria-multiselectable": !!this.p.multiselect, "aria-labelledby": "gbox_" + this.id });
            var sortkeys = ["shiftKey", "altKey", "ctrlKey"],
		intNum = function(val, defval) {
		    val = parseInt(val, 10);
		    if (isNaN(val)) { return defval ? defval : 0; }
		    else { return val; }
		},
		formatCol = function(pos, rowInd, tv) {
		    var cm = ts.p.colModel[pos],
			ral = cm.align, result = "style=\"", clas = cm.classes, nm = cm.name;
		    if (ral) { result += "text-align:" + ral + ";"; }
		    if (cm.hidden === true) { result += "display:none;"; }
		    if (rowInd === 0) {
		        result += "width: " + grid.headers[pos].width + "px;";
		    }
		    result += "\"" + (clas !== undefined ? (" class=\"" + clas + "\"") : "") + ((cm.title && tv) ? (" title=\"" + $.jgrid.stripHtml(tv) + "\"") : "");
		    result += " aria-describedby=\"" + ts.p.id + "_" + nm + "\"";
		    return result;
		},
		cellVal = function(val) {
		    return val === undefined || val === null || val === "" ? "&#160;" : (ts.p.autoencode ? $.jgrid.htmlEncode(val) : val + "");
		},
		formatter = function(rowId, cellval, colpos, rwdat, _act) {
		    var cm = ts.p.colModel[colpos], v;
		    if (typeof cm.formatter !== 'undefined') {
		        var opts = { rowId: rowId, colModel: cm, gid: ts.p.id };
		        if ($.isFunction(cm.formatter)) {
		            v = cm.formatter.call(ts, cellval, opts, rwdat, _act);
		        } else if ($.fmatter) {
		            v = $.fn.fmatter(cm.formatter, cellval, opts, rwdat, _act);
		        } else {
		            v = cellVal(cellval);
		        }
		    } else {
		        v = cellVal(cellval);
		    }
		    return v;
		},
		addCell = function(rowId, cell, pos, irow, srvr) {
		    var v, prp;
		    v = formatter(rowId, cell, pos, srvr, 'add');
		    prp = formatCol(pos, irow, v);
		    return "<td role=\"gridcell\" " + prp + ">" + v + "</td>";
		},
		addMulti = function(rowid, pos, irow) {
		    var v = "<input role=\"checkbox\" type=\"checkbox\"" + " id=\"jqg_" + rowid + "\" class=\"cbox\" name=\"jqg_" + rowid + "\"/>",
			prp = formatCol(pos, irow, '');
		    return "<td role=\"gridcell\" aria-describedby=\"" + ts.p.id + "_cb\" " + prp + ">" + v + "</td>";
		},
		addRowNum = function(pos, irow, pG, rN) {
		    var v = (parseInt(pG, 10) - 1) * parseInt(rN, 10) + 1 + irow,
			prp = formatCol(pos, irow, '');
		    return "<td role=\"gridcell\" aria-describedby=\"" + ts.p.id + "_rn\" class=\"ui-state-default jqgrid-rownum\" " + prp + ">" + v + "</td>";
		},
		reader = function(datatype) {
		    var field, f = [], j = 0, i;
		    for (i = 0; i < ts.p.colModel.length; i++) {
		        field = ts.p.colModel[i];
		        if (field.name !== 'cb' && field.name !== 'subgrid' && field.name !== 'rn') {
		            f[j] = (datatype == "xml") ? field.xmlmap || field.name : field.jsonmap || field.name;
		            j++;
		        }
		    }
		    return f;
		},
		orderedCols = function(offset) {
		    var order = ts.p.remapColumns;
		    if (!order || !order.length) {
		        order = $.map(ts.p.colModel, function(v, i) { return i; });
		    }
		    if (offset) {
		        order = $.map(order, function(v) { return v < offset ? null : v - offset; });
		    }
		    return order;
		},
		emptyRows = function(parent, scroll) {
		    if (ts.p.deepempty) { $("tbody:first tr", parent).remove(); }
		    else { $("tbody:first", parent).empty(); }
		    if (scroll && ts.p.scroll) {
		        $(">div:first", parent).css({ height: "auto" }).children("div:first").css({ height: 0, display: "none" });
		        parent.scrollTop = 0;
		    }
		},
		addXmlData = function(xml, t, rcnt, more, adjust) {
		    var startReq = new Date(),
			locdata = (ts.p.datatype != "local" && ts.p.loadonce) || ts.p.datatype == "xmlstring",
			xmlid;
		    if (locdata) {
		        ts.p.data = [];
		        ts.p._index = {};
		        ts.p.localReader.id = xmlid = "_id_";
		    }
		    ts.p.reccount = 0;
		    if ($.isXMLDoc(xml)) {
		        if (ts.p.treeANode === -1 && !ts.p.scroll) {
		            emptyRows(t, false);
		            rcnt = 0;
		        } else { rcnt = rcnt > 0 ? rcnt : 0; }
		    } else { return; }
		    var i, fpos, ir = 0, v, row, gi = 0, si = 0, ni = 0, idn, getId, f = [], F, rd = {}, rl = ts.rows.length, xmlr, rid, rowData = [], ari = 0, cn = (ts.p.altRows === true) ? " " + ts.p.altclass : "", cn1;
		    if (!ts.p.xmlReader.repeatitems) { f = reader("xml"); }
		    if (ts.p.keyIndex === false) {
		        idn = ts.p.xmlReader.id;
		    } else {
		        idn = ts.p.keyIndex;
		    }
		    if (f.length > 0 && !isNaN(idn)) {
		        if (ts.p.remapColumns && ts.p.remapColumns.length) {
		            idn = $.inArray(idn, ts.p.remapColumns);
		        }
		        idn = f[idn];
		    }
		    if ((idn + "").indexOf("[") === -1) {
		        if (f.length) {
		            getId = function(trow, k) { return $(idn, trow).text() || k; };
		        } else {
		            getId = function(trow, k) { return $(ts.p.xmlReader.cell, trow).eq(idn).text() || k; };
		        }
		    }
		    else {
		        getId = function(trow, k) { return trow.getAttribute(idn.replace(/[\[\]]/g, "")) || k; };
		    }
		    ts.p.userData = {};
		    $(ts.p.xmlReader.page, xml).each(function() { ts.p.page = this.textContent || this.text || 0; });
		    $(ts.p.xmlReader.total, xml).each(function() { ts.p.lastpage = this.textContent || this.text; if (ts.p.lastpage === undefined) { ts.p.lastpage = 1; } });
		    $(ts.p.xmlReader.records, xml).each(function() { ts.p.records = this.textContent || this.text || 0; });
		    $(ts.p.xmlReader.userdata, xml).each(function() { ts.p.userData[this.getAttribute("name")] = this.textContent || this.text; });
		    var gxml = $(ts.p.xmlReader.root + " " + ts.p.xmlReader.row, xml), gl = gxml.length, j = 0;
		    if (gxml && gl) {
		        var rn = parseInt(ts.p.rowNum, 10), br = ts.p.scroll ? (parseInt(ts.p.page, 10) - 1) * rn + 1 : 1, altr;
		        if (adjust) { rn *= adjust + 1; }
		        var afterInsRow = $.isFunction(ts.p.afterInsertRow);
		        while (j < gl) {
		            xmlr = gxml[j];
		            rid = getId(xmlr, br + j);
		            altr = rcnt === 0 ? 0 : rcnt + 1;
		            cn1 = (altr + j) % 2 == 1 ? cn : '';
		            rowData[ari++] = "<tr id=\"" + rid + "\" role=\"row\" class =\"ui-widget-content jqgrow ui-row-" + ts.p.direction + "" + cn1 + "\">";
		            if (ts.p.rownumbers === true) {
		                rowData[ari++] = addRowNum(0, j, ts.p.page, ts.p.rowNum);
		                ni = 1;
		            }
		            if (ts.p.multiselect === true) {
		                rowData[ari++] = addMulti(rid, ni, j);
		                gi = 1;
		            }
		            if (ts.p.subGrid === true) {
		                rowData[ari++] = $(ts).jqGrid("addSubGridCell", gi + ni, j + rcnt);
		                si = 1;
		            }
		            if (ts.p.xmlReader.repeatitems) {
		                if (!F) { F = orderedCols(gi + si + ni); }
		                var cells = $(ts.p.xmlReader.cell, xmlr);
		                $.each(F, function(k) {
		                    var cell = cells[this];
		                    if (!cell) { return false; }
		                    v = cell.textContent || cell.text;
		                    rd[ts.p.colModel[k + gi + si + ni].name] = v;
		                    rowData[ari++] = addCell(rid, v, k + gi + si + ni, j + rcnt, xmlr);
		                });
		            } else {
		                for (i = 0; i < f.length; i++) {
		                    v = $(f[i], xmlr).text();
		                    rd[ts.p.colModel[i + gi + si + ni].name] = v;
		                    rowData[ari++] = addCell(rid, v, i + gi + si + ni, j + rcnt, xmlr);
		                }
		            }
		            rowData[ari++] = "</tr>";
		            if (locdata) {
		                rd[xmlid] = rid;
		                ts.p.data.push(rd);
		            }
		            if (ts.p.gridview === false) {
		                if (ts.p.treeGrid === true) {
		                    fpos = ts.p.treeANode >= -1 ? ts.p.treeANode : 0;
		                    row = $(rowData.join(''))[0]; // speed overhead
		                    if (rl === 0) { $("tbody:first", t).append(row); } else { $(ts.rows[j + fpos + rcnt]).after(row); }
		                    try { $(ts).jqGrid("setTreeNode", rd, row); } catch (e) { }
		                } else {
		                    $("tbody:first", t).append(rowData.join(''));
		                }
		                if (ts.p.subGrid === true) {
		                    try { $(ts).jqGrid("addSubGrid", ts.rows[ts.rows.length - 1], gi + ni); } catch (_) { }
		                }
		                if (afterInsRow) { ts.p.afterInsertRow.call(ts, rid, rd, xmlr); }
		                rowData = []; ari = 0;
		            }
		            rd = {};
		            ir++;
		            j++;
		            if (ir == rn) { break; }
		        }
		    }
		    if (ts.p.gridview === true) {
		        $("tbody:first", t).append(rowData.join(''));
		    }
		    ts.p.totaltime = new Date() - startReq;
		    if (ir > 0) { ts.grid.cols = ts.rows[0].cells; if (ts.p.records === 0) { ts.p.records = gl; } }
		    rowData = null;
		    if (!ts.p.treeGrid && !ts.p.scroll) { ts.grid.bDiv.scrollTop = 0; }
		    ts.p.reccount = ir;
		    ts.p.treeANode = -1;
		    if (ts.p.userDataOnFooter) { $(ts).jqGrid("footerData", "set", ts.p.userData, true); }
		    if (locdata) {
		        ts.p.records = gl;
		        ts.p.lastpage = Math.ceil(gl / rn);
		    }
		    if (!more) { updatepager(false, true); }
		    if (locdata) {
		        while (ir < gl) {
		            xmlr = gxml[ir];
		            rid = getId(xmlr, ir);
		            if (ts.p.xmlReader.repeatitems) {
		                if (!F) { F = orderedCols(gi + si + ni); }
		                var cells = $(ts.p.xmlReader.cell, xmlr);
		                $.each(F, function(k) {
		                    var cell = cells[this];
		                    if (!cell) { return false; }
		                    v = cell.textContent || cell.text;
		                    rd[ts.p.colModel[k + gi + si + ni].name] = v;
		                });
		            } else {
		                for (i = 0; i < f.length; i++) {
		                    v = $(f[i], xmlr).text();
		                    rd[ts.p.colModel[i + gi + si + ni].name] = v;
		                }
		            }
		            rd[xmlid] = rid;
		            ts.p.data.push(rd);
		            rd = {};
		            ir++;
		        }
		        refreshIndex();
		    }
		},
		addJSONData = function(data, t, rcnt, more, adjust) {
		    var startReq = new Date();
		    if (data) {
		        if (ts.p.treeANode === -1 && !ts.p.scroll) {
		            emptyRows(t, false);
		            rcnt = 0;
		        } else { rcnt = rcnt > 0 ? rcnt : 0; }
		    } else { return; }

		    var dReader = ts.p.datatype == "local" ? ts.p.localReader : ts.p.jsonReader, locid,
			locdata = (ts.p.datatype != "local" && ts.p.loadonce) || ts.p.datatype == "jsonstring";
		    if (locdata) { ts.p.data = []; ts.p._index = {}; locid = ts.p.localReader.id = "_id_"; }
		    ts.p.reccount = 0;

		    var ir = 0, v, i, j, row, f = [], F, cur, gi = 0, si = 0, ni = 0, len, drows, idn, rd = {}, fpos, rl = ts.rows.length, idr, rowData = [], ari = 0, cn = (ts.p.altRows === true) ? " " + ts.p.altclass : "", cn1, lp;
		    ts.p.page = $.jgrid.getAccessor(data, dReader.page) || 0;
		    lp = $.jgrid.getAccessor(data, dReader.total);
		    ts.p.lastpage = lp === undefined ? 1 : lp;
		    ts.p.records = $.jgrid.getAccessor(data, dReader.records) || 0;
		    ts.p.userData = $.jgrid.getAccessor(data, dReader.userdata) || {};
		    if (!dReader.repeatitems) {
		        F = f = reader("json");
		    }
		    if (ts.p.keyIndex === false) {
		        idn = dReader.id;
		    } else {
		        idn = ts.p.keyIndex;
		    }
		    if (f.length > 0 && !isNaN(idn)) {
		        if (ts.p.remapColumns && ts.p.remapColumns.length) {
		            idn = $.inArray(idn, ts.p.remapColumns);
		        }
		        idn = f[idn];
		    }
		    drows = $.jgrid.getAccessor(data, dReader.root);
		    if (drows) {
		        len = drows.length; i = 0;
		        var rn = parseInt(ts.p.rowNum, 10), br = ts.p.scroll ? (parseInt(ts.p.page, 10) - 1) * rn + 1 : 1, altr;
		        if (adjust) { rn *= adjust + 1; }
		        var afterInsRow = $.isFunction(ts.p.afterInsertRow);
		        while (i < len) {
		            cur = drows[i];
		            idr = $.jgrid.getAccessor(cur, idn);
		            if (idr === undefined) {
		                idr = br + i;
		                if (f.length === 0) {
		                    if (dReader.cell) {
		                        var ccur = cur[dReader.cell];
		                        idr = ccur[idn] || idr;
		                        ccur = null;
		                    }
		                }
		            }
		            altr = rcnt === 0 ? 0 : rcnt + 1;
		            cn1 = (altr + i) % 2 == 1 ? cn : '';
		            rowData[ari++] = "<tr id=\"" + idr + "\" role=\"row\" class= \"ui-widget-content jqgrow ui-row-" + ts.p.direction + "" + cn1 + "\">";
		            if (ts.p.rownumbers === true) {
		                rowData[ari++] = addRowNum(0, i, ts.p.page, ts.p.rowNum);
		                ni = 1;
		            }
		            if (ts.p.multiselect) {
		                rowData[ari++] = addMulti(idr, ni, i);
		                gi = 1;
		            }
		            if (ts.p.subGrid) {
		                rowData[ari++] = $(ts).jqGrid("addSubGridCell", gi + ni, i + rcnt);
		                si = 1;
		            }
		            if (dReader.repeatitems) {
		                if (dReader.cell) { cur = $.jgrid.getAccessor(cur, dReader.cell); }
		                if (!F) { F = orderedCols(gi + si + ni); }
		            }
		            for (j = 0; j < F.length; j++) {
		                v = $.jgrid.getAccessor(cur, F[j]);
		                rowData[ari++] = addCell(idr, v, j + gi + si + ni, i + rcnt, cur);
		                rd[ts.p.colModel[j + gi + si + ni].name] = v;
		            }
		            rowData[ari++] = "</tr>";
		            if (locdata) { rd[locid] = idr; ts.p.data.push(rd); }
		            if (ts.p.gridview === false) {
		                if (ts.p.treeGrid === true) {
		                    fpos = ts.p.treeANode >= -1 ? ts.p.treeANode : 0;
		                    row = $(rowData.join(''))[0];
		                    if (rl === 0) { $("tbody:first", t).append(row); } else { $(ts.rows[i + fpos + rcnt]).after(row); }
		                    try { $(ts).jqGrid("setTreeNode", rd, row); } catch (e) { }
		                } else {
		                    $("tbody:first", t).append(rowData.join(''));
		                }
		                if (ts.p.subGrid === true) {
		                    try { $(ts).jqGrid("addSubGrid", ts.rows[ts.rows.length - 1], gi + ni); } catch (_) { }
		                }
		                if (afterInsRow) { ts.p.afterInsertRow.call(ts, idr, rd, cur); }
		                rowData = []; ari = 0;
		            }
		            rd = {};
		            ir++;
		            i++;
		            if (ir == rn) { break; }
		        }
		        if (ts.p.gridview === true) {
		            $("tbody:first", t).append(rowData.join(''));
		        }
		        ts.p.totaltime = new Date() - startReq;
		        if (ir > 0) { ts.grid.cols = ts.rows[0].cells; if (ts.p.records === 0) { ts.p.records = len; } }
		    }
		    if (!ts.p.treeGrid && !ts.p.scroll) { ts.grid.bDiv.scrollTop = 0; }
		    ts.p.reccount = ir;
		    ts.p.treeANode = -1;
		    if (ts.p.userDataOnFooter) { $(ts).jqGrid("footerData", "set", ts.p.userData, true); }
		    if (locdata) {
		        ts.p.records = len;
		        ts.p.lastpage = Math.ceil(len / rn);
		    }
		    if (!more) { updatepager(false, true); }
		    if (locdata) {
		        while (ir < len) {
		            cur = drows[ir];
		            idr = $.jgrid.getAccessor(cur, idn);
		            if (idr === undefined) {
		                idr = br + ir;
		                if (f.length === 0) {
		                    if (dReader.cell) {
		                        var ccur = cur[dReader.cell];
		                        idr = ccur[idn] || idr;
		                        ccur = null;
		                    }
		                }
		            }
		            if (cur) {
		                if (dReader.repeatitems) {
		                    if (dReader.cell) { cur = $.jgrid.getAccessor(cur, dReader.cell); }
		                    if (!F) { F = orderedCols(gi + si + ni); }
		                }

		                for (j = 0; j < F.length; j++) {
		                    v = $.jgrid.getAccessor(cur, F[j]);
		                    rd[ts.p.colModel[j + gi + si + ni].name] = v;
		                }
		                rd[locid] = idr;
		                ts.p.data.push(rd);
		                rd = {};
		            }
		            ir++;
		        }
		        refreshIndex();
		    }
		},
		addLocalData = function() {
		    var st, fndsort = false, cmtypes = [], srcformat, sortype, newformat;
		    if (!ts.p.data.length) {
		        return;
		    }
		    $.each(ts.p.colModel, function(i, v) {
		        sorttype = this.sorttype || "text";
		        if (sorttype == "date" || sorttype == "datetime") {
		            if (this.formatter && typeof (this.formatter) === 'string' && this.formatter == 'date') {
		                if (this.formatoptions && this.formatoptions.srcformat) {
		                    srcformat = this.formatoptions.srcformat;
		                } else {
		                    srcformat = $.jgrid.formatter.date.srcformat;
		                }
		                if (this.formatoptions && this.formatoptions.newformat) {
		                    newformat = this.formatoptions.newformat;
		                } else {
		                    newformat = $.jgrid.formatter.date.newformat;
		                }
		            } else {
		                srcformat = newformat = this.datefmt || "Y-m-d";
		            }
		            cmtypes[this.name] = { "stype": sorttype, "srcfmt": srcformat, "newfmt": newformat };
		        } else {
		            cmtypes[this.name] = { "stype": sorttype, "srcfmt": '', "newfmt": '' };
		        }
		        if (!fndsort && (this.index == ts.p.sortname || this.name == ts.p.sortname)) {
		            st = this.name; // ???
		            fndsort = true;
		        }
		    });
		    if (ts.p.treeGrid) {
		        $(ts).jqGrid("SortTree", ts.p.sortname, ts.p.sortorder, cmtypes[st].stype, cmtypes[st].srcfmt);
		        return;
		    }
		    var compareFnMap = {
		        'eq': function(queryObj) { return queryObj.equals; },
		        'ne': function(queryObj) { return queryObj.not().equals; },
		        'lt': function(queryObj) { return queryObj.less; },
		        'le': function(queryObj) { return queryObj.lessOrEquals; },
		        'gt': function(queryObj) { return queryObj.greater; },
		        'ge': function(queryObj) { return queryObj.greaterOrEquals; },
		        'cn': function(queryObj) { return queryObj.contains; },
		        'nc': function(queryObj) { return queryObj.not().contains; },
		        'bw': function(queryObj) { return queryObj.startsWith; },
		        'bn': function(queryObj) { return queryObj.not().startsWith; },
		        'en': function(queryObj) { return queryObj.not().endsWith; },
		        'ew': function(queryObj) { return queryObj.endsWith; },
		        'ni': function(queryObj) { return queryObj.not().equals; },
		        'in': function(queryObj) { return queryObj.equals; }

		    },
			query = $.jgrid.from(ts.p.data);
		    if (ts.p.search === true) {
		        var srules = ts.p.postData.filters;
		        if (srules) {
		            if (typeof srules == "string") { srules = $.jgrid.parse(srules); }
		            for (var i = 0, l = srules.rules.length, rule; i < l; i++) {
		                var rule = srules.rules[i], opr = srules.groupOp;
		                if (compareFnMap[rule.op] && rule.field && rule.data && opr) {
		                    if (opr.toUpperCase() == "OR") {
		                        query = compareFnMap[rule.op](query)(rule.field, rule.data, cmtypes[rule.field]).or();
		                    } else {
		                        query = compareFnMap[rule.op](query)(rule.field, rule.data, cmtypes[rule.field]);
		                    }
		                }
		            }
		        } else {
		            try {
		                query = compareFnMap[ts.p.postData.searchOper](query)(ts.p.postData.searchField, ts.p.postData.searchString, cmtypes[ts.p.postData.searchField]);
		            } catch (se) { }
		        }
		    }

		    if (st && ts.p.sortorder) {
		        if (ts.p.sortorder.toUpperCase() == "DESC") {
		            query.orderBy(st, "d", cmtypes[st].stype, cmtypes[st].srcfmt);
		        } else {
		            query.orderBy(st, "a", cmtypes[st].stype, cmtypes[st].srcfmt);
		        }
		    }
		    var queryResults = query.select(),
			recordsperpage = parseInt(ts.p.rowNum, 10),
			total = queryResults.length,
			page = parseInt(ts.p.page, 10),
			totalpages = Math.ceil(total / recordsperpage),
			retresult = {};
		    queryResults = queryResults.slice((page - 1) * recordsperpage, page * recordsperpage);
		    query = null;
		    cmtypes = null;
		    retresult[ts.p.localReader.total] = totalpages;
		    retresult[ts.p.localReader.page] = page;
		    retresult[ts.p.localReader.records] = total;
		    retresult[ts.p.localReader.root] = queryResults;
		    queryResults = null;
		    return retresult;
		},
		updatepager = function(rn, dnd) {
		    var cp, last, base, from, to, tot, fmt, pgboxes = "";
		    base = parseInt(ts.p.page, 10) - 1;
		    if (base < 0) { base = 0; }
		    base = base * parseInt(ts.p.rowNum, 10);
		    to = base + ts.p.reccount;
		    if (ts.p.scroll) {
		        var rows = $("tbody:first > tr", ts.grid.bDiv);
		        base = to - rows.length;
		        ts.p.reccount = rows.length;
		        var rh = rows.outerHeight();
		        if (rh) {
		            var top = base * rh;
		            var height = parseInt(ts.p.records, 10) * rh;
		            $(">div:first", ts.grid.bDiv).css({ height: height }).children("div:first").css({ height: top, display: top ? "" : "none" });
		        }
		        ts.grid.bDiv.scrollLeft = ts.grid.hDiv.scrollLeft;
		    }
		    pgboxes = ts.p.pager ? ts.p.pager : "";
		    pgboxes += ts.p.toppager ? (pgboxes ? "," + ts.p.toppager : ts.p.toppager) : "";
		    if (pgboxes) {
		        fmt = $.jgrid.formatter.integer || {};
		        cp = intNum(ts.p.page);
		        last = intNum(ts.p.lastpage);
		        $(".selbox", pgboxes).attr("disabled", false);
		        if (ts.p.pginput === true) {
		            $('.ui-pg-input', pgboxes).val(ts.p.page);
		            $('#sp_1', pgboxes).html($.fmatter ? $.fmatter.util.NumberFormat(ts.p.lastpage, fmt) : ts.p.lastpage);

		        }
		        if (ts.p.viewrecords) {
		            if (ts.p.reccount === 0) {
		                $(".ui-paging-info", pgboxes).html(ts.p.emptyrecords);
		            } else {
		                from = base + 1;
		                tot = ts.p.records;
		                if ($.fmatter) {
		                    from = $.fmatter.util.NumberFormat(from, fmt);
		                    to = $.fmatter.util.NumberFormat(to, fmt);
		                    tot = $.fmatter.util.NumberFormat(tot, fmt);
		                }
		                $(".ui-paging-info", pgboxes).html($.jgrid.format(ts.p.recordtext, from, to, tot));
		            }
		        }
		        if (ts.p.pgbuttons === true) {
		            if (cp <= 0) { cp = last = 0; }
		            if (cp == 1 || cp === 0) {
		                $("#first, #prev", ts.p.pager).addClass('ui-state-disabled').removeClass('ui-state-hover');
		                if (ts.p.toppager) { $("#first_t, #prev_t", ts.p.toppager).addClass('ui-state-disabled').removeClass('ui-state-hover'); }
		            } else {
		                $("#first, #prev", ts.p.pager).removeClass('ui-state-disabled');
		                if (ts.p.toppager) { $("#first_t, #prev_t", ts.p.toppager).removeClass('ui-state-disabled'); }
		            }
		            if (cp == last || cp === 0) {
		                $("#next, #last", ts.p.pager).addClass('ui-state-disabled').removeClass('ui-state-hover');
		                if (ts.p.toppager) { $("#next_t, #last_t", ts.p.toppager).addClass('ui-state-disabled').removeClass('ui-state-hover'); }
		            } else {
		                $("#next, #last", ts.p.pager).removeClass('ui-state-disabled');
		                if (ts.p.toppager) { $("#next_t, #last_t", ts.p.toppager).removeClass('ui-state-disabled'); }
		            }
		        }
		    }
		    if (rn === true && ts.p.rownumbers === true) {
		        $("td.jqgrid-rownum", ts.rows).each(function(i) {
		            $(this).html(base + 1 + i);
		        });
		    }
		    if (dnd && ts.p.jqgdnd) { $(ts).jqGrid('gridDnD', 'updateDnD'); }
		    if ($.isFunction(ts.p.gridComplete)) { ts.p.gridComplete.call(ts); }
		},
		populate = function(npage) {
		    if (!ts.grid.hDiv.loading) {
		        var pvis = ts.p.scroll && npage === false;
		        var prm = {}, dt, dstr, pN = ts.p.prmNames;
		        if (ts.p.page <= 0) { ts.p.page = 1; }
		        if (pN.search !== null) { prm[pN.search] = ts.p.search; } if (pN.nd !== null) { prm[pN.nd] = new Date().getTime(); }
		        if (pN.rows !== null) { prm[pN.rows] = ts.p.rowNum; } if (pN.page !== null) { prm[pN.page] = ts.p.page; }
		        if (pN.sort !== null) { prm[pN.sort] = ts.p.sortname; } if (pN.order !== null) { prm[pN.order] = ts.p.sortorder; }
		        if (ts.p.rowTotal !== null && pN.totalrows !== null) { prm[pN.totalrows] = ts.p.rowTotal; }
		        var lc = ts.p.loadComplete;
		        var lcf = $.isFunction(lc);
		        if (!lcf) { lc = null; }
		        var adjust = 0;
		        npage = npage || 1;
		        if (npage > 1) {
		            if (pN.npage !== null) {
		                prm[pN.npage] = npage;
		                adjust = npage - 1;
		                npage = 1;
		            } else {
		                lc = function(req) {
		                    ts.p.page++;
		                    ts.grid.hDiv.loading = false;
		                    if (lcf) {
		                        ts.p.loadComplete.call(ts, req);
		                    }
		                    populate(npage - 1);
		                };
		            }
		        } else if (pN.npage !== null) {
		            delete ts.p.postData[pN.npage];
		        }
		        $.extend(ts.p.postData, prm);
		        var rcnt = !ts.p.scroll ? 0 : ts.rows.length - 1;
		        if ($.isFunction(ts.p.datatype)) { ts.p.datatype.call(ts, ts.p.postData, "load_" + ts.p.id); return; }
		        else if ($.isFunction(ts.p.beforeRequest)) { ts.p.beforeRequest.call(ts); }
		        dt = ts.p.datatype.toLowerCase();
		        switch (dt) {
		            case "json":
		            case "jsonp":
		            case "xml":
		            case "script":
		                $.ajax($.extend({
		                    url: ts.p.url,
		                    type: ts.p.mtype,
		                    dataType: dt,
		                    data: $.isFunction(ts.p.serializeGridData) ? ts.p.serializeGridData.call(ts, ts.p.postData) : ts.p.postData,
		                    success: function(req, st) {
		                        if (dt === "xml") { addXmlData(req, ts.grid.bDiv, rcnt, npage > 1, adjust); }
		                        else { addJSONData(req, ts.grid.bDiv, rcnt, npage > 1, adjust); }
		                        if (lc) { lc.call(ts, req); }
		                        if (pvis) { ts.grid.populateVisible(); }
		                        if (ts.p.loadonce || ts.p.treeGrid) { ts.p.datatype = "local"; }
		                        req = null;
		                        endReq();
		                    },
		                    error: function(xhr, st, err) {
		                        if ($.isFunction(ts.p.loadError)) { ts.p.loadError.call(ts, xhr, st, err); }
		                        endReq();
		                        xhr = null;
		                    },
		                    beforeSend: function(xhr) {
		                        beginReq();
		                        if ($.isFunction(ts.p.loadBeforeSend)) { ts.p.loadBeforeSend.call(ts, xhr); }
		                    }
		                }, $.jgrid.ajaxOptions, ts.p.ajaxGridOptions));
		                break;
		            case "xmlstring":
		                beginReq();
		                dstr = $.jgrid.stringToDoc(ts.p.datastr);
		                if (lcf) { ts.p.loadComplete.call(ts, dstr); }
		                addXmlData(dstr, ts.grid.bDiv);
		                ts.p.datatype = "local";
		                ts.p.datastr = null;
		                endReq();
		                break;
		            case "jsonstring":
		                beginReq();
		                if (typeof ts.p.datastr == 'string') { dstr = $.jgrid.parse(ts.p.datastr); }
		                else { dstr = ts.p.datastr; }
		                if (lcf) { ts.p.loadComplete.call(ts, dstr); }
		                addJSONData(dstr, ts.grid.bDiv);
		                ts.p.datatype = "local";
		                ts.p.datastr = null;
		                endReq();
		                break;
		            case "local":
		            case "clientside":
		                beginReq();
		                ts.p.datatype = "local";
		                var req = addLocalData();
		                addJSONData(req, ts.grid.bDiv, rcnt, npage > 1, adjust);
		                if (lc) { lc.call(ts, req); }
		                if (pvis) { ts.grid.populateVisible(); }
		                endReq();
		                break;
		        }
		    }
		},
		refreshIndex = function() {
		    var datalen = ts.p.data.length, idname, i, val,
			ni = ts.p.rownumbers === true ? 1 : 0,
			gi = ts.p.multiselect === true ? 1 : 0,
			si = ts.p.subGrid === true ? 1 : 0;

		    if (ts.p.keyIndex === false || ts.p.loadonce === true) {
		        idname = ts.p.localReader.id;
		    } else {
		        idname = ts.p.colModel[ts.p.keyIndex + gi + si + ni].name;
		    }
		    for (i = 0; i < datalen; i++) {
		        val = $.jgrid.getAccessor(ts.p.data[i], idname);
		        ts.p._index[val] = i;
		    }
		},
		beginReq = function() {
		    ts.grid.hDiv.loading = true;
		    if (ts.p.hiddengrid) { return; }
		    switch (ts.p.loadui) {
		        case "disable":
		            break;
		        case "enable":
		            $("#load_" + ts.p.id).show();
		            break;
		        case "block":
		            $("#lui_" + ts.p.id).show();
		            $("#load_" + ts.p.id).show();
		            break;
		    }
		},
		endReq = function() {
		    ts.grid.hDiv.loading = false;
		    switch (ts.p.loadui) {
		        case "disable":
		            break;
		        case "enable":
		            $("#load_" + ts.p.id).hide();
		            break;
		        case "block":
		            $("#lui_" + ts.p.id).hide();
		            $("#load_" + ts.p.id).hide();
		            break;
		    }
		},
		setPager = function(pgid, tp) {
		    var sep = "<td class='ui-pg-button ui-state-disabled' style='width:4px;'><span class='ui-separator'></span></td>",
			pginp = "",
			pgl = "<table cellspacing='0' cellpadding='0' border='0' style='table-layout:auto;' class='ui-pg-table'><tbody><tr>",
			str = "", pgcnt, lft, cent, rgt, twd, tdw, i,
			clearVals = function(onpaging) {
			    var ret;
			    if ($.isFunction(ts.p.onPaging)) { ret = ts.p.onPaging.call(ts, onpaging); }
			    ts.p.selrow = null;
			    if (ts.p.multiselect) { ts.p.selarrrow = []; $('#cb_' + $.jgrid.jqID(ts.p.id), ts.grid.hDiv).attr("checked", false); }
			    ts.p.savedRow = [];
			    if (ret == 'stop') { return false; }
			    return true;
			};
		    pgid = pgid.substr(1);
		    pgcnt = "pg_" + pgid;
		    lft = pgid + "_left"; cent = pgid + "_center"; rgt = pgid + "_right";
		    $("#" + pgid)
			.append("<div id='" + pgcnt + "' class='ui-pager-control' role='group'><table cellspacing='0' cellpadding='0' border='0' class='ui-pg-table' style='width:100%;table-layout:fixed;' role='row'><tbody><tr><td id='" + lft + "' align='left'></td><td id='" + cent + "' align='center' style='white-space:pre;'></td><td id='" + rgt + "' align='right'></td></tr></tbody></table></div>")
			.attr("dir", "ltr"); //explicit setting
		    if (ts.p.rowList.length > 0) {
		        str = "<td dir='" + dir + "'>";
		        str += "<select class='ui-pg-selbox' role='listbox'>";
		        for (i = 0; i < ts.p.rowList.length; i++) {
		            str += "<option role='option' value='" + ts.p.rowList[i] + "'" + ((ts.p.rowNum == ts.p.rowList[i]) ? ' selected' : '') + ">" + ts.p.rowList[i] + "</option>";
		        }
		        str += "</select></td>";
		    }
		    if (dir == "rtl") { pgl += str; }
		    if (ts.p.pginput === true) { pginp = "<td dir='" + dir + "'>" + $.jgrid.format(ts.p.pgtext || "", "<input class='ui-pg-input' type='text' size='2' maxlength='7' value='0' role='textbox'/>", "<span id='sp_1'></span>") + "</td>"; }
		    if (ts.p.pgbuttons === true) {
		        var po = ["first" + tp, "prev" + tp, "next" + tp, "last" + tp]; if (dir == "rtl") { po.reverse(); }
		        pgl += "<td id='" + po[0] + "' class='ui-pg-button ui-corner-all'><span class='ui-icon ui-icon-seek-first'></span></td>";
		        pgl += "<td id='" + po[1] + "' class='ui-pg-button ui-corner-all'><span class='ui-icon ui-icon-seek-prev'></span></td>";
		        pgl += pginp != "" ? sep + pginp + sep : "";
		        pgl += "<td id='" + po[2] + "' class='ui-pg-button ui-corner-all'><span class='ui-icon ui-icon-seek-next'></span></td>";
		        pgl += "<td id='" + po[3] + "' class='ui-pg-button ui-corner-all'><span class='ui-icon ui-icon-seek-end'></span></td>";
		    } else if (pginp != "") { pgl += pginp; }
		    if (dir == "ltr") { pgl += str; }
		    pgl += "</tr></tbody></table>";
		    if (ts.p.viewrecords === true) { $("td#" + pgid + "_" + ts.p.recordpos, "#" + pgcnt).append("<div dir='" + dir + "' style='text-align:" + ts.p.recordpos + "' class='ui-paging-info'></div>"); }
		    $("td#" + pgid + "_" + ts.p.pagerpos, "#" + pgcnt).append(pgl);
		    tdw = $(".ui-jqgrid").css("font-size") || "11px";
		    $(document.body).append("<div id='testpg' class='ui-jqgrid ui-widget ui-widget-content' style='font-size:" + tdw + ";visibility:hidden;' ></div>");
		    twd = $(pgl).clone().appendTo("#testpg").width();
		    $("#testpg").remove();
		    if (twd > 0) {
		        if (pginp != "") { twd += 50; } //should be param
		        $("td#" + pgid + "_" + ts.p.pagerpos, "#" + pgcnt).width(twd);
		    }
		    ts.p._nvtd = [];
		    ts.p._nvtd[0] = twd ? Math.floor((ts.p.width - twd) / 2) : Math.floor(ts.p.width / 3);
		    ts.p._nvtd[1] = 0;
		    pgl = null;
		    $('.ui-pg-selbox', "#" + pgcnt).bind('change', function() {
		        ts.p.page = Math.round(ts.p.rowNum * (ts.p.page - 1) / this.value - 0.5) + 1;
		        ts.p.rowNum = this.value;
		        if (tp) { $('.ui-pg-selbox', ts.p.pager).val(this.value); }
		        else if (ts.p.toppager) { $('.ui-pg-selbox', ts.p.toppager).val(this.value); }
		        if (!clearVals('records')) { return false; }
		        populate();
		        return false;
		    });
		    if (ts.p.pgbuttons === true) {
		        $(".ui-pg-button", "#" + pgcnt).hover(function(e) {
		            if ($(this).hasClass('ui-state-disabled')) {
		                this.style.cursor = 'default';
		            } else {
		                $(this).addClass('ui-state-hover');
		                this.style.cursor = 'pointer';
		            }
		        }, function(e) {
		            if ($(this).hasClass('ui-state-disabled')) {
		            } else {
		                $(this).removeClass('ui-state-hover');
		                this.style.cursor = "default";
		            }
		        });
		        $("#first" + tp + ", #prev" + tp + ", #next" + tp + ", #last" + tp, "#" + pgid).click(function(e) {
		            var cp = intNum(ts.p.page, 1),
				last = intNum(ts.p.lastpage, 1), selclick = false,
				fp = true, pp = true, np = true, lp = true;
		            if (last === 0 || last === 1) { fp = false; pp = false; np = false; lp = false; }
		            else if (last > 1 && cp >= 1) {
		                if (cp === 1) { fp = false; pp = false; }
		                else if (cp > 1 && cp < last) { }
		                else if (cp === last) { np = false; lp = false; }
		            } else if (last > 1 && cp === 0) { np = false; lp = false; cp = last - 1; }
		            if (this.id === 'first' + tp && fp) { ts.p.page = 1; selclick = true; }
		            if (this.id === 'prev' + tp && pp) { ts.p.page = (cp - 1); selclick = true; }
		            if (this.id === 'next' + tp && np) { ts.p.page = (cp + 1); selclick = true; }
		            if (this.id === 'last' + tp && lp) { ts.p.page = last; selclick = true; }
		            if (selclick) {
		                if (!clearVals(this.id)) { return false; }
		                populate();
		            }
		            return false;
		        });
		    }
		    if (ts.p.pginput === true) {
		        $('input.ui-pg-input', "#" + pgcnt).keypress(function(e) {
		            var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;
		            if (key == 13) {
		                ts.p.page = ($(this).val() > 0) ? $(this).val() : ts.p.page;
		                if (!clearVals('user')) { return false; }
		                populate();
		                return false;
		            }
		            return this;
		        });
		    }
		},
		sortData = function(index, idxcol, reload, sor) {
		    if (!ts.p.colModel[idxcol].sortable) { return; }
		    var so;
		    if (ts.p.savedRow.length > 0) { return; }
		    if (!reload) {
		        if (ts.p.lastsort == idxcol) {
		            if (ts.p.sortorder == 'asc') {
		                ts.p.sortorder = 'desc';
		            } else if (ts.p.sortorder == 'desc') { ts.p.sortorder = 'asc'; }
		        } else { ts.p.sortorder = ts.p.colModel[idxcol].firstsortorder || 'asc'; }
		        ts.p.page = 1;
		    }
		    if (sor) {
		        if (ts.p.lastsort == idxcol && ts.p.sortorder == sor && !reload) { return; }
		        else { ts.p.sortorder = sor; }
		    }
		    var thd = $("thead:first", ts.grid.hDiv).get(0);
		    $("tr th:eq(" + ts.p.lastsort + ") span.ui-grid-ico-sort", thd).addClass('ui-state-disabled');
		    $("tr th:eq(" + ts.p.lastsort + ")", thd).attr("aria-selected", "false");
		    $("tr th:eq(" + idxcol + ") span.ui-icon-" + ts.p.sortorder, thd).removeClass('ui-state-disabled');
		    $("tr th:eq(" + idxcol + ")", thd).attr("aria-selected", "true");
		    if (!ts.p.viewsortcols[0]) {
		        if (ts.p.lastsort != idxcol) {
		            $("tr th:eq(" + ts.p.lastsort + ") span.s-ico", thd).hide();
		            $("tr th:eq(" + idxcol + ") span.s-ico", thd).show();
		        }
		    }
		    index = index.substring(5);
		    ts.p.sortname = ts.p.colModel[idxcol].index || index;
		    so = ts.p.sortorder;
		    if ($.isFunction(ts.p.onSortCol)) { if (ts.p.onSortCol.call(ts, index, idxcol, so) == 'stop') { ts.p.lastsort = idxcol; return; } }
		    if (ts.p.datatype == "local") {
		        if (ts.p.deselectAfterSort) { $(ts).jqGrid("resetSelection"); }
		    } else {
		        ts.p.selrow = null;
		        if (ts.p.multiselect) { $("#cb_" + $.jgrid.jqID(ts.p.id), ts.grid.hDiv).attr("checked", false); }
		        ts.p.selarrrow = [];
		        ts.p.savedRow = [];
		    }
		    if (ts.p.scroll) {
		        var sscroll = ts.grid.bDiv.scrollLeft;
		        emptyRows(ts.grid.bDiv, true);
		        ts.grid.hDiv.scrollLeft = sscroll;
		    }
		    if (ts.p.subGrid && ts.p.datatype == 'local') {
		        $("td.sgexpanded", "#" + ts.p.id).each(function() {
		            $(this).trigger("click");
		        });
		    }
		    populate();
		    ts.p.lastsort = idxcol;
		    if (ts.p.sortname != index && idxcol) { ts.p.lastsort = idxcol; }
		},
		setColWidth = function() {
		    var initwidth = 0, brd = ts.p.cellLayout, vc = 0, lvc, scw = ts.p.scrollOffset, cw, hs = false, aw, tw = 0, gw = 0,
			cl = 0, cr;
		    if (isSafari) { brd = 0; }
		    $.each(ts.p.colModel, function(i) {
		        if (typeof this.hidden === 'undefined') { this.hidden = false; }
		        if (this.hidden === false) {
		            initwidth += intNum(this.width, 0);
		            if (this.fixed) {
		                tw += this.width;
		                gw += this.width + brd;
		            } else {
		                vc++;
		            }
		            cl++;
		        }
		    });
		    if (isNaN(ts.p.width)) { ts.p.width = grid.width = initwidth; }
		    else { grid.width = ts.p.width; }
		    ts.p.tblwidth = initwidth;
		    if (ts.p.shrinkToFit === false && ts.p.forceFit === true) { ts.p.forceFit = false; }
		    if (ts.p.shrinkToFit === true && vc > 0) {
		        aw = grid.width - brd * vc - gw;
		        if (isNaN(ts.p.height)) {
		        } else {
		            aw -= scw;
		            hs = true;
		        }
		        initwidth = 0;
		        $.each(ts.p.colModel, function(i) {
		            if (this.hidden === false && !this.fixed) {
		                cw = Math.round(aw * this.width / (ts.p.tblwidth - tw));
		                this.width = cw;
		                initwidth += cw;
		                lvc = i;
		            }
		        });
		        cr = 0;
		        if (hs) {
		            if (grid.width - gw - (initwidth + brd * vc) !== scw) {
		                cr = grid.width - gw - (initwidth + brd * vc) - scw;
		            }
		        } else if (!hs && Math.abs(grid.width - gw - (initwidth + brd * vc)) !== 1) {
		            cr = grid.width - gw - (initwidth + brd * vc);
		        }
		        ts.p.colModel[lvc].width += cr;
		        ts.p.tblwidth = initwidth + cr + tw + cl * brd;
		        if (ts.p.tblwidth != ts.p.width) {
		            ts.p.colModel[lvc].width -= (ts.p.tblwidth - parseInt(ts.p.width, 10));
		            ts.p.tblwidth = ts.p.width;
		        }
		    }
		},
		nextVisible = function(iCol) {
		    var ret = iCol, j = iCol, i;
		    for (i = iCol + 1; i < ts.p.colModel.length; i++) {
		        if (ts.p.colModel[i].hidden !== true) {
		            j = i; break;
		        }
		    }
		    return j - ret;
		},
		getOffset = function(iCol) {
		    var i, ret = {}, brd1 = isSafari ? 0 : ts.p.cellLayout;
		    ret[0] = ret[1] = ret[2] = 0;
		    for (i = 0; i <= iCol; i++) {
		        if (ts.p.colModel[i].hidden === false) {
		            ret[0] += ts.p.colModel[i].width + brd1;
		        }
		    }
		    if (ts.p.direction == "rtl") { ret[0] = ts.p.width - ret[0]; }
		    ret[0] = ret[0] - ts.grid.bDiv.scrollLeft;
		    if ($(ts.grid.cDiv).is(":visible")) { ret[1] += $(ts.grid.cDiv).height() + parseInt($(ts.grid.cDiv).css("padding-top"), 10) + parseInt($(ts.grid.cDiv).css("padding-bottom"), 10); }
		    if (ts.p.toolbar[0] === true && (ts.p.toolbar[1] == 'top' || ts.p.toolbar[1] == 'both')) { ret[1] += $(ts.grid.uDiv).height() + parseInt($(ts.grid.uDiv).css("border-top-width"), 10) + parseInt($(ts.grid.uDiv).css("border-bottom-width"), 10); }
		    if (ts.p.toppager) { ret[1] += $(ts.grid.topDiv).height() + parseInt($(ts.grid.topDiv).css("border-bottom-width"), 10); }
		    ret[2] += $(ts.grid.bDiv).height() + $(ts.grid.hDiv).height();
		    return ret;
		};
            this.p.id = this.id;
            if ($.inArray(ts.p.multikey, sortkeys) == -1) { ts.p.multikey = false; }
            ts.p.keyIndex = false;
            for (i = 0; i < ts.p.colModel.length; i++) {
                if (ts.p.colModel[i].key === true) {
                    ts.p.keyIndex = i;
                    break;
                }
            }
            ts.p.sortorder = ts.p.sortorder.toLowerCase();
            if (this.p.treeGrid === true) {
                try { $(this).jqGrid("setTreeGrid"); } catch (_) { }
                if (ts.p.datatype != "local") { ts.p.localReader = { id: "_id_" }; }
            }
            if (this.p.subGrid) {
                try { $(ts).jqGrid("setSubGrid"); } catch (_) { }
            }
            if (this.p.multiselect) {
                this.p.colNames.unshift("<input role='checkbox' id='cb_" + this.p.id + "' class='cbox' type='checkbox'/>");
                this.p.colModel.unshift({ name: 'cb', width: isSafari ? ts.p.multiselectWidth + ts.p.cellLayout : ts.p.multiselectWidth, sortable: false, resizable: false, hidedlg: true, search: false, align: 'center', fixed: true });
            }
            if (this.p.rownumbers) {
                this.p.colNames.unshift("");
                this.p.colModel.unshift({ name: 'rn', width: ts.p.rownumWidth, sortable: false, resizable: false, hidedlg: true, search: false, align: 'center', fixed: true });
            }
            ts.p.xmlReader = $.extend(true, {
                root: "rows",
                row: "row",
                page: "rows>page",
                total: "rows>total",
                records: "rows>records",
                repeatitems: true,
                cell: "cell",
                id: "[id]",
                userdata: "userdata",
                subgrid: { root: "rows", row: "row", repeatitems: true, cell: "cell" }
            }, ts.p.xmlReader);
            ts.p.jsonReader = $.extend(true, {
                root: "rows",
                page: "page",
                total: "total",
                records: "records",
                repeatitems: true,
                cell: "cell",
                id: "id",
                userdata: "userdata",
                subgrid: { root: "rows", repeatitems: true, cell: "cell" }
            }, ts.p.jsonReader);
            ts.p.localReader = $.extend(true, {
                root: "rows",
                page: "page",
                total: "total",
                records: "records",
                repeatitems: false,
                cell: "cell",
                id: "id",
                userdata: "userdata",
                subgrid: { root: "rows", repeatitems: true, cell: "cell" }
            }, ts.p.localReader);
            if (ts.p.scroll) {
                ts.p.pgbuttons = false; ts.p.pginput = false; ts.p.rowList = [];
            }
            if (ts.p.data.length) { refreshIndex(); }
            var thead = "<thead><tr class='ui-jqgrid-labels' role='rowheader'>",
		tdc, idn, w, res, sort,
		td, ptr, tbody, imgs, iac = "", idc = "";
            if (ts.p.shrinkToFit === true && ts.p.forceFit === true) {
                for (i = ts.p.colModel.length - 1; i >= 0; i--) {
                    if (!ts.p.colModel[i].hidden) {
                        ts.p.colModel[i].resizable = false;
                        break;
                    }
                }
            }
            if (ts.p.viewsortcols[1] == 'horizontal') { iac = " ui-i-asc"; idc = " ui-i-desc"; }
            tdc = isMSIE ? "class='ui-th-div-ie'" : "";
            imgs = "<span class='s-ico' style='display:none'><span sort='asc' class='ui-grid-ico-sort ui-icon-asc" + iac + " ui-state-disabled ui-icon ui-icon-triangle-1-n ui-sort-" + dir + "'></span>";
            imgs += "<span sort='desc' class='ui-grid-ico-sort ui-icon-desc" + idc + " ui-state-disabled ui-icon ui-icon-triangle-1-s ui-sort-" + dir + "'></span></span>";
            for (i = 0; i < this.p.colNames.length; i++) {
                var tooltip = ts.p.headertitles ? (" title=\"" + $.jgrid.stripHtml(ts.p.colNames[i]) + "\"") : "";
                thead += "<th id='" + ts.p.id + "_" + ts.p.colModel[i].name + "' role='columnheader' class='ui-state-default ui-th-column ui-th-" + dir + "'" + tooltip + ">";
                idn = ts.p.colModel[i].index || ts.p.colModel[i].name;
                thead += "<div id='jqgh_" + ts.p.colModel[i].name + "' " + tdc + ">" + ts.p.colNames[i];
                if (!ts.p.colModel[i].width) { ts.p.colModel[i].width = 150; }
                else { ts.p.colModel[i].width = parseInt(ts.p.colModel[i].width, 10); }
                if (typeof (ts.p.colModel[i].title) !== "boolean") { ts.p.colModel[i].title = true; }
                if (idn == ts.p.sortname) {
                    ts.p.lastsort = i;
                }
                thead += imgs + "</div></th>";
            }
            thead += "</tr></thead>";
            $(this).append(thead);
            $("thead tr:first th", this).hover(function() { $(this).addClass('ui-state-hover'); }, function() { $(this).removeClass('ui-state-hover'); });
            if (this.p.multiselect) {
                var emp = [], chk;
                $('#cb_' + $.jgrid.jqID(ts.p.id), this).bind('click', function() {
                    if (this.checked) {
                        $("[id^=jqg_]", ts.rows).attr("checked", true);
                        $(ts.rows).each(function(i) {
                            if (!$(this).hasClass("subgrid")) {
                                $(this).addClass("ui-state-highlight").attr("aria-selected", "true");
                                ts.p.selarrrow[i] = ts.p.selrow = this.id;
                            }
                        });
                        chk = true;
                        emp = [];
                    }
                    else {
                        $("[id^=jqg_]", ts.rows).attr("checked", false);
                        $(ts.rows).each(function(i) {
                            if (!$(this).hasClass("subgrid")) {
                                $(this).removeClass("ui-state-highlight").attr("aria-selected", "false");
                                emp[i] = this.id;
                            }
                        });
                        ts.p.selarrrow = []; ts.p.selrow = null;
                        chk = false;
                    }
                    if ($.isFunction(ts.p.onSelectAll)) { ts.p.onSelectAll.call(ts, chk ? ts.p.selarrrow : emp, chk); }
                });
            }

            if (ts.p.autowidth === true) {
                var pw = $(eg).innerWidth();
                ts.p.width = pw > 0 ? pw : 'nw';
            }
            setColWidth();
            $(eg).css("width", grid.width + "px").append("<div class='ui-jqgrid-resize-mark' id='rs_m" + ts.p.id + "'>&#160;</div>");
            $(gv).css("width", grid.width + "px");
            thead = $("thead:first", ts).get(0);
            var tfoot = "";
            if (ts.p.footerrow) { tfoot += "<table role='grid' style='width:" + ts.p.tblwidth + "px' class='ui-jqgrid-ftable' cellspacing='0' cellpadding='0' border='0'><tbody><tr role='row' class='ui-widget-content footrow footrow-" + dir + "'>"; }
            var thr = $("tr:first", thead);
            ts.p.disableClick = false;
            $("th", thr).each(function(j) {
                w = ts.p.colModel[j].width;
                if (typeof ts.p.colModel[j].resizable === 'undefined') { ts.p.colModel[j].resizable = true; }
                if (ts.p.colModel[j].resizable) {
                    res = document.createElement("span");
                    $(res).html("&#160;").addClass('ui-jqgrid-resize ui-jqgrid-resize-' + dir);
                    if (!$.browser.opera) { $(res).css("cursor", "col-resize"); }
                    $(this).addClass(ts.p.resizeclass);
                } else {
                    res = "";
                }
                $(this).css("width", w + "px").prepend(res);
                if (ts.p.colModel[j].hidden) { $(this).css("display", "none"); }
                grid.headers[j] = { width: w, el: this };
                sort = ts.p.colModel[j].sortable;
                if (typeof sort !== 'boolean') { ts.p.colModel[j].sortable = true; sort = true; }
                var nm = ts.p.colModel[j].name;
                if (sort) {
                    if (!(nm == 'cb' || nm == 'subgrid' || nm == 'rn')) {
                        if (ts.p.viewsortcols[2]) {
                            $("div", this).addClass('ui-jqgrid-sortable');
                        }
                    }
                    if (ts.p.viewsortcols[0]) { $("div span.s-ico", this).show(); if (j == ts.p.lastsort) { $("div span.ui-icon-" + ts.p.sortorder, this).removeClass("ui-state-disabled"); } }
                    else if (j == ts.p.lastsort) { $("div span.s-ico", this).show(); $("div span.ui-icon-" + ts.p.sortorder, this).removeClass("ui-state-disabled"); }
                }
                if (ts.p.footerrow) { tfoot += "<td role='gridcell' " + formatCol(j, 0, '') + ">&#160;</td>"; }
            }).mousedown(function(e) {
                if ($(e.target).closest("th>span.ui-jqgrid-resize").length != 1) { return; }
                var ci = $.jgrid.getCellIndex(this);
                if (ts.p.forceFit === true) { ts.p.nv = nextVisible(ci); }
                grid.dragStart(ci, e, getOffset(ci));
                return false;
            }).click(function(e) {
                if (ts.p.disableClick) {
                    ts.p.disableClick = false;
                    return false;
                }
                var s = "th>div.ui-jqgrid-sortable", r, d;
                if (!ts.p.viewsortcols[2]) { s = "th>div>span>span.ui-grid-ico-sort"; }
                var t = $(e.target).closest(s);
                if (t.length != 1) { return; }
                var ci = $.jgrid.getCellIndex(this);
                if (!ts.p.viewsortcols[2]) { r = true; d = t.attr("sort"); }
                sortData($('div', this)[0].id, ci, r, d);
                return false;
            });
            if (ts.p.sortable && $.fn.sortable) {
                try {
                    $(ts).jqGrid("sortableColumns", thr);
                } catch (e) { }
            }
            if (ts.p.footerrow) { tfoot += "</tr></tbody></table>"; }

            tbody = document.createElement("tbody");
            this.appendChild(tbody);
            $(this).addClass('ui-jqgrid-btable');
            var hTable = $("<table class='ui-jqgrid-htable' style='width:" + ts.p.tblwidth + "px' role='grid' aria-labelledby='gbox_" + this.id + "' cellspacing='0' cellpadding='0' border='0'></table>").append(thead),
		hg = (ts.p.caption && ts.p.hiddengrid === true) ? true : false,
		hb = $("<div class='ui-jqgrid-hbox" + (dir == "rtl" ? "-rtl" : "") + "'></div>");
            grid.hDiv = document.createElement("div");
            $(grid.hDiv)
			.css({ width: grid.width + "px" })
			.addClass("ui-state-default ui-jqgrid-hdiv")
			.append(hb);
            $(hb).append(hTable);
            if (hg) { $(grid.hDiv).hide(); }
            if (ts.p.pager) {
                if (typeof ts.p.pager == "string") { if (ts.p.pager.substr(0, 1) != "#") { ts.p.pager = "#" + ts.p.pager; } }
                else { ts.p.pager = "#" + $(ts.p.pager).attr("id"); }
                $(ts.p.pager).css({ width: grid.width + "px" }).appendTo(eg).addClass('ui-state-default ui-jqgrid-pager ui-corner-bottom');
                if (hg) { $(ts.p.pager).hide(); }
                setPager(ts.p.pager, '');
            }
            if (ts.p.cellEdit === false && ts.p.hoverrows === true) {
                $(ts).bind('mouseover', function(e) {
                    ptr = $(e.target).closest("tr.jqgrow");
                    if ($(ptr).attr("class") !== "subgrid") {
                        $(ptr).addClass("ui-state-hover");
                    }
                    return false;
                }).bind('mouseout', function(e) {
                    ptr = $(e.target).closest("tr.jqgrow");
                    $(ptr).removeClass("ui-state-hover");
                    return false;
                });
            }
            var ri, ci;
            $(ts).before(grid.hDiv).click(function(e) {
                td = e.target;
                var scb = $(td).hasClass("cbox");
                ptr = $(td, ts.rows).closest("tr.jqgrow");
                if ($(ptr).length === 0) {
                    return this;
                }
                var cSel = true;
                if ($.isFunction(ts.p.beforeSelectRow)) { cSel = ts.p.beforeSelectRow.call(ts, ptr[0].id, e); }
                if (td.tagName == 'A' || ((td.tagName == 'INPUT' || td.tagName == 'TEXTAREA' || td.tagName == 'OPTION' || td.tagName == 'SELECT') && !scb)) { return this; }
                if (cSel === true) {
                    if (ts.p.cellEdit === true) {
                        if (ts.p.multiselect && scb) {
                            $(ts).jqGrid("setSelection", ptr[0].id, true);
                        } else {
                            ri = ptr[0].rowIndex;
                            ci = $.jgrid.getCellIndex(td);
                            try { $(ts).jqGrid("editCell", ri, ci, true); } catch (_) { }
                        }
                    } else if (!ts.p.multikey) {
                        if (ts.p.multiselect && ts.p.multiboxonly) {
                            if (scb) { $(ts).jqGrid("setSelection", ptr[0].id, true); }
                            else {
                                $(ts.p.selarrrow).each(function(i, n) {
                                    var ind = ts.rows.namedItem(n);
                                    $(ind).removeClass("ui-state-highlight");
                                    $("#jqg_" + $.jgrid.jqID(n), ind).attr("checked", false);
                                });
                                ts.p.selarrrow = [];
                                $("#cb_" + $.jgrid.jqID(ts.p.id), ts.grid.hDiv).attr("checked", false);
                                $(ts).jqGrid("setSelection", ptr[0].id, true);
                            }
                        } else {
                            $(ts).jqGrid("setSelection", ptr[0].id, true);
                        }
                    } else {
                        if (e[ts.p.multikey]) {
                            $(ts).jqGrid("setSelection", ptr[0].id, true);
                        } else if (ts.p.multiselect && scb) {
                            scb = $("[id^=jqg_]", ptr).attr("checked");
                            $("[id^=jqg_]", ptr).attr("checked", !scb);
                        }
                    }
                    if ($.isFunction(ts.p.onCellSelect)) {
                        ri = ptr[0].id;
                        ci = $.jgrid.getCellIndex(td);
                        ts.p.onCellSelect.call(ts, ri, ci, $(td).html(), e);
                    }
                    e.stopPropagation();
                } else {
                    return this;
                }
            }).bind('reloadGrid', function(e, opts) {
                if (ts.p.treeGrid === true) {
                    ts.p.datatype = ts.p.treedatatype;
                    if (ts.p.postData.hasOwnProperty('nodeid') && ts.p.postData.nodeid == '') {
                        ts.p.data = []; _index = {};
                    }
                }
                if (opts && opts.current) {
                    ts.grid.selectionPreserver(ts);
                }
                if (ts.p.datatype == "local") { $(ts).jqGrid("resetSelection"); }
                else if (!ts.p.treeGrid) {
                    ts.p.selrow = null;
                    if (ts.p.multiselect) { ts.p.selarrrow = []; $('#cb_' + $.jgrid.jqID(ts.p.id), ts.grid.hDiv).attr("checked", false); }
                    ts.p.savedRow = [];
                }
                if (ts.p.scroll) { emptyRows(ts.grid.bDiv, true); }
                if (opts && opts.page) {
                    var page = opts.page;
                    if (page > ts.p.lastpage) { page = ts.p.lastpage; }
                    if (page < 1) { page = 1; }
                    ts.p.page = page;
                    if (ts.grid.prevRowHeight) {
                        ts.grid.bDiv.scrollTop = (page - 1) * ts.grid.prevRowHeight * ts.p.rowNum;
                    } else {
                        ts.grid.bDiv.scrollTop = 0;
                    }
                }
                if (ts.grid.prevRowHeight && ts.p.scroll) {
                    delete ts.p.lastpage;
                    ts.grid.populateVisible();
                } else {
                    ts.grid.populate();
                }
                return false;
            });
            if ($.isFunction(this.p.ondblClickRow)) {
                $(this).dblclick(function(e) {
                    td = e.target;
                    ptr = $(td, ts.rows).closest("tr.jqgrow");
                    if ($(ptr).length === 0) { return false; }
                    ri = ptr[0].rowIndex;
                    ci = $.jgrid.getCellIndex(td);
                    ts.p.ondblClickRow.call(ts, $(ptr).attr("id"), ri, ci, e);
                    return false;
                });
            }
            if ($.isFunction(this.p.onRightClickRow)) {
                $(this).bind('contextmenu', function(e) {
                    td = e.target;
                    ptr = $(td, ts.rows).closest("tr.jqgrow");
                    if ($(ptr).length === 0) { return false; }
                    if (!ts.p.multiselect) { $(ts).jqGrid("setSelection", ptr[0].id, true); }
                    ri = ptr[0].rowIndex;
                    ci = $.jgrid.getCellIndex(td);
                    ts.p.onRightClickRow.call(ts, $(ptr).attr("id"), ri, ci, e);
                    return false;
                });
            }
            grid.bDiv = document.createElement("div");
            $(grid.bDiv)
			.append($('<div style="position:relative;' + (isMSIE && $.browser.version < 8 ? "height:0.01%;" : "") + '"></div>').append('<div></div>').append(this))
			.addClass("ui-jqgrid-bdiv")
			.css({ height: ts.p.height + (isNaN(ts.p.height) ? "" : "px"), width: (grid.width) + "px" })
			.scroll(grid.scrollGrid);
            $("table:first", grid.bDiv).css({ width: ts.p.tblwidth + "px" });
            if (isMSIE) {
                if ($("tbody", this).size() == 2) { $("tbody:first", this).remove(); }
                if (ts.p.multikey) { $(grid.bDiv).bind("selectstart", function() { return false; }); }
            } else {
                if (ts.p.multikey) { $(grid.bDiv).bind("mousedown", function() { return false; }); }
            }
            if (hg) { $(grid.bDiv).hide(); }
            grid.cDiv = document.createElement("div");
            var arf = ts.p.hidegrid === true ? $("<a role='link' href='javascript:void(0)'/>").addClass('ui-jqgrid-titlebar-close HeaderButton').hover(
			function() { arf.addClass('ui-state-hover'); },
			function() { arf.removeClass('ui-state-hover'); })
		.append("<span class='ui-icon ui-icon-circle-triangle-n'></span>").css((dir == "rtl" ? "left" : "right"), "0px") : "";
            $(grid.cDiv).append(arf).append("<span class='ui-jqgrid-title" + (dir == "rtl" ? "-rtl" : "") + "'>" + ts.p.caption + "</span>")
		.addClass("ui-jqgrid-titlebar ui-widget-header ui-corner-top ui-helper-clearfix");
            $(grid.cDiv).insertBefore(grid.hDiv);
            if (ts.p.toolbar[0]) {
                grid.uDiv = document.createElement("div");
                if (ts.p.toolbar[1] == "top") { $(grid.uDiv).insertBefore(grid.hDiv); }
                else if (ts.p.toolbar[1] == "bottom") { $(grid.uDiv).insertAfter(grid.hDiv); }
                if (ts.p.toolbar[1] == "both") {
                    grid.ubDiv = document.createElement("div");
                    $(grid.uDiv).insertBefore(grid.hDiv).addClass("ui-userdata ui-state-default").attr("id", "t_" + this.id);
                    $(grid.ubDiv).insertAfter(grid.hDiv).addClass("ui-userdata ui-state-default").attr("id", "tb_" + this.id);
                    if (hg) { $(grid.ubDiv).hide(); }
                } else {
                    $(grid.uDiv).width(grid.width).addClass("ui-userdata ui-state-default").attr("id", "t_" + this.id);
                }
                if (hg) { $(grid.uDiv).hide(); }
            }
            if (ts.p.toppager) {
                ts.p.toppager = ts.p.id + "_toppager";
                grid.topDiv = $("<div id='" + ts.p.toppager + "'></div>")[0];
                ts.p.toppager = "#" + ts.p.toppager;
                $(grid.topDiv).insertBefore(grid.hDiv).addClass('ui-state-default ui-jqgrid-toppager').width(grid.width);
                setPager(ts.p.toppager, '_t');
            }
            if (ts.p.footerrow) {
                grid.sDiv = $("<div class='ui-jqgrid-sdiv'></div>")[0];
                hb = $("<div class='ui-jqgrid-hbox" + (dir == "rtl" ? "-rtl" : "") + "'></div>");
                $(grid.sDiv).append(hb).insertAfter(grid.hDiv).width(grid.width);
                $(hb).append(tfoot);
                grid.footers = $(".ui-jqgrid-ftable", grid.sDiv)[0].rows[0].cells;
                if (ts.p.rownumbers) { grid.footers[0].className = 'ui-state-default jqgrid-rownum'; }
                if (hg) { $(grid.sDiv).hide(); }
            }
            if (ts.p.caption) {
                var tdt = ts.p.datatype;
                if (ts.p.hidegrid === true) {
                    $(".ui-jqgrid-titlebar-close", grid.cDiv).click(function(e) {
                        var onHdCl = $.isFunction(ts.p.onHeaderClick);
                        if (ts.p.gridstate == 'visible') {
                            $(".ui-jqgrid-bdiv, .ui-jqgrid-hdiv", "#gview_" + ts.p.id).slideUp("fast");
                            if (ts.p.pager) { $(ts.p.pager).slideUp("fast"); }
                            if (ts.p.toppager) { $(ts.p.toppager).slideUp("fast"); }
                            if (ts.p.toolbar[0] === true) {
                                if (ts.p.toolbar[1] == 'both') {
                                    $(grid.ubDiv).slideUp("fast");
                                }
                                $(grid.uDiv).slideUp("fast");
                            }
                            if (ts.p.footerrow) { $(".ui-jqgrid-sdiv", "#gbox_" + ts.p.id).slideUp("fast"); }
                            $("span", this).removeClass("ui-icon-circle-triangle-n").addClass("ui-icon-circle-triangle-s");
                            ts.p.gridstate = 'hidden';
                            if ($("#gbox_" + ts.p.id).hasClass("ui-resizable")) { $(".ui-resizable-handle", "#gbox_" + ts.p.id).hide(); }
                            if (onHdCl) { if (!hg) { ts.p.onHeaderClick.call(ts, ts.p.gridstate, e); } }
                        } else if (ts.p.gridstate == 'hidden') {
                            $(".ui-jqgrid-hdiv, .ui-jqgrid-bdiv", "#gview_" + ts.p.id).slideDown("fast");
                            if (ts.p.pager) { $(ts.p.pager).slideDown("fast"); }
                            if (ts.p.toppager) { $(ts.p.toppager).slideDown("fast"); }
                            if (ts.p.toolbar[0] === true) {
                                if (ts.p.toolbar[1] == 'both') {
                                    $(grid.ubDiv).slideDown("fast");
                                }
                                $(grid.uDiv).slideDown("fast");
                            }
                            if (ts.p.footerrow) { $(".ui-jqgrid-sdiv", "#gbox_" + ts.p.id).slideDown("fast"); }
                            $("span", this).removeClass("ui-icon-circle-triangle-s").addClass("ui-icon-circle-triangle-n");
                            if (hg) { ts.p.datatype = tdt; populate(); hg = false; }
                            ts.p.gridstate = 'visible';
                            if ($("#gbox_" + ts.p.id).hasClass("ui-resizable")) { $(".ui-resizable-handle", "#gbox_" + ts.p.id).show(); }
                            if (onHdCl) { ts.p.onHeaderClick.call(ts, ts.p.gridstate, e); }
                        }
                        return false;
                    });
                    if (hg) { ts.p.datatype = "local"; $(".ui-jqgrid-titlebar-close", grid.cDiv).trigger("click"); }
                }
            } else { $(grid.cDiv).hide(); }
            $(grid.hDiv).after(grid.bDiv)
		.mousemove(function(e) {
		    if (grid.resizing) { grid.dragMove(e); return false; }
		});
            $(".ui-jqgrid-labels", grid.hDiv).bind("selectstart", function() { return false; });
            $(document).mouseup(function(e) {
                if (grid.resizing) { grid.dragEnd(); return false; }
                return true;
            });
            this.updateColumns = function() {
                var r = this.rows[0], self = this;
                if (r) {
                    $("td", r).each(function(k) {
                        $(this).css("width", self.grid.headers[k].width + "px");
                    });
                    this.grid.cols = r.cells;
                }
                return this;
            };
            ts.formatCol = formatCol;
            ts.sortData = sortData;
            ts.updatepager = updatepager;
            ts.refreshIndex = refreshIndex;
            ts.formatter = function(rowId, cellval, colpos, rwdat, act) { return formatter(rowId, cellval, colpos, rwdat, act); };
            $.extend(grid, { populate: populate, emptyRows: emptyRows });
            this.grid = grid;
            ts.addXmlData = function(d) { addXmlData(d, ts.grid.bDiv); };
            ts.addJSONData = function(d) { addJSONData(d, ts.grid.bDiv); };
            populate(); ts.p.hiddengrid = false;
            //$(window).unload(function() {
            //    ts = null;
            //});
            $(window).on("unload", function () {
                ts = null;
            });

        });
    };
    $.jgrid.extend({
        getGridParam: function(pName) {
            var $t = this[0];
            if (!$t || !$t.grid) { return; }
            if (!pName) { return $t.p; }
            else { return typeof ($t.p[pName]) != "undefined" ? $t.p[pName] : null; }
        },
        setGridParam: function(newParams) {
            return this.each(function() {
                if (this.grid && typeof (newParams) === 'object') { $.extend(true, this.p, newParams); }
            });
        },
        getDataIDs: function() {
            var ids = [], i = 0, len;
            this.each(function() {
                len = this.rows.length;
                if (len && len > 0) {
                    while (i < len) {
                        ids[i] = this.rows[i].id;
                        i++;
                    }
                }
            });
            return ids;
        },
        setSelection: function(selection, onsr) {
            return this.each(function() {
                var $t = this, stat, pt, ner, ia, tpsr;
                if (selection === undefined) { return; }
                onsr = onsr === false ? false : true;
                pt = $t.rows.namedItem(selection + "");
                if (!pt) { return; }
                function scrGrid(iR) {
                    var ch = $($t.grid.bDiv)[0].clientHeight,
				st = $($t.grid.bDiv)[0].scrollTop,
				rpos = $t.rows[iR].offsetTop,
				rh = $t.rows[iR].clientHeight;
                    if (rpos + rh >= ch + st) { $($t.grid.bDiv)[0].scrollTop = rpos - (ch + st) + rh + st; }
                    else if (rpos < ch + st) {
                        if (rpos < st) {
                            $($t.grid.bDiv)[0].scrollTop = rpos;
                        }
                    }
                }
                if ($t.p.scrollrows === true) {
                    ner = $t.rows.namedItem(selection).rowIndex;
                    if (ner >= 0) {
                        scrGrid(ner);
                    }
                }
                if (!$t.p.multiselect) {
                    if (pt.className !== "ui-subgrid") {
                        if ($t.p.selrow) { $($t.rows.namedItem($t.p.selrow)).removeClass("ui-state-highlight").attr("aria-selected", "false"); }
                        $t.p.selrow = pt.id;
                        $(pt).addClass("ui-state-highlight").attr("aria-selected", "true");
                        if ($t.p.onSelectRow && onsr) { $t.p.onSelectRow.call($t, $t.p.selrow, true); }
                    }
                } else {
                    $t.p.selrow = pt.id;
                    ia = $.inArray($t.p.selrow, $t.p.selarrrow);
                    if (ia === -1) {
                        if (pt.className !== "ui-subgrid") { $(pt).addClass("ui-state-highlight").attr("aria-selected", "true"); }
                        stat = true;
                        $("#jqg_" + $.jgrid.jqID($t.p.selrow), $t.rows[pt.rowIndex]).attr("checked", stat);
                        $t.p.selarrrow.push($t.p.selrow);
                        if ($t.p.onSelectRow && onsr) { $t.p.onSelectRow.call($t, $t.p.selrow, stat); }
                    } else {
                        if (pt.className !== "ui-subgrid") { $(pt).removeClass("ui-state-highlight").attr("aria-selected", "false"); }
                        stat = false;
                        $("#jqg_" + $.jgrid.jqID($t.p.selrow), $t.rows[pt.rowIndex]).attr("checked", stat);
                        $t.p.selarrrow.splice(ia, 1);
                        if ($t.p.onSelectRow && onsr) { $t.p.onSelectRow.call($t, $t.p.selrow, stat); }
                        tpsr = $t.p.selarrrow[0];
                        $t.p.selrow = (tpsr === undefined) ? null : tpsr;
                    }
                }
            });
        },
        resetSelection: function() {
            return this.each(function() {
                var t = this, ind;
                if (!t.p.multiselect) {
                    if (t.p.selrow) {
                        $("tr#" + $.jgrid.jqID(t.p.selrow), t.grid.bDiv).removeClass("ui-state-highlight").attr("aria-selected", "false");
                        t.p.selrow = null;
                    }
                } else {
                    $(t.p.selarrrow).each(function(i, n) {
                        ind = t.rows.namedItem(n);
                        $(ind).removeClass("ui-state-highlight").attr("aria-selected", "false");
                        $("#jqg_" + $.jgrid.jqID(n), ind).attr("checked", false);
                    });
                    $("#cb_" + $.jgrid.jqID(t.p.id), t.grid.hDiv).attr("checked", false);
                    t.p.selarrrow = [];
                }
                t.p.savedRow = [];
            });
        },
        getRowData: function(rowid) {
            var res = {}, resall, getall = false, len, j = 0;
            this.each(function() {
                var $t = this, nm, ind;
                if (typeof (rowid) == 'undefined') {
                    getall = true;
                    resall = [];
                    len = $t.rows.length;
                } else {
                    ind = $t.rows.namedItem(rowid);
                    if (!ind) { return res; }
                    len = 1;
                }
                while (j < len) {
                    if (getall) { ind = $t.rows[j]; }
                    $('td', ind).each(function(i) {
                        nm = $t.p.colModel[i].name;
                        if (nm !== 'cb' && nm !== 'subgrid' && nm !== 'rn') {
                            if ($t.p.treeGrid === true && nm == $t.p.ExpandColumn) {
                                res[nm] = $.jgrid.htmlDecode($("span:first", this).html());
                            } else {
                                try {
                                    res[nm] = $.unformat(this, { rowId: ind.id, colModel: $t.p.colModel[i] }, i);
                                } catch (e) {
                                    res[nm] = $.jgrid.htmlDecode($(this).html());
                                }
                            }
                        }
                    });
                    j++;
                    if (getall) { resall.push(res); res = {}; }
                }
            });
            return resall ? resall : res;
        },
        delRowData: function(rowid) {
            var success = false, rowInd, ia, ri;
            this.each(function() {
                var $t = this;
                rowInd = $t.rows.namedItem(rowid);
                if (!rowInd) { return false; }
                else {
                    ri = rowInd.rowIndex;
                    $(rowInd).remove();
                    $t.p.records--;
                    $t.p.reccount--;
                    $t.updatepager(true, false);
                    success = true;
                    if ($t.p.multiselect) {
                        ia = $.inArray(rowid, $t.p.selarrrow);
                        if (ia != -1) { $t.p.selarrrow.splice(ia, 1); }
                    }
                    if (rowid == $t.p.selrow) { $t.p.selrow = null; }
                }
                if ($t.p.datatype == 'local') {
                    var pos = null;
                    pos = $t.p._index[rowid];
                    if (pos !== null) {
                        $t.p.data.splice(pos, 1);
                        $t.refreshIndex();
                    }
                }
                if (ri === 0 && success) {
                    $t.updateColumns();
                }
                if ($t.p.altRows === true && success) {
                    var cn = $t.p.altclass;
                    $($t.rows).each(function(i) {
                        if (i % 2 == 1) { $(this).addClass(cn); }
                        else { $(this).removeClass(cn); }
                    });
                }
            });
            return success;
        },
        setRowData: function(rowid, data, cssp) {
            var nm, success = true, title;
            this.each(function() {
                if (!this.grid) { return false; }
                var t = this, vl, ind, cp = typeof cssp, lcdata = {};
                ind = t.rows.namedItem(rowid);
                if (!ind) { return false; }
                if (data) {
                    try {
                        $(this.p.colModel).each(function(i) {
                            nm = this.name;
                            if (data[nm] !== undefined) {
                                // @TODO  we must handle propertly the formatter date
                                lcdata[nm] = this.formatter && typeof (this.formatter) === 'string' && this.formatter == 'date' ? $.unformat.date(data[nm], this) : data[nm];
                                vl = t.formatter(rowid, data[nm], i, data, 'edit');
                                title = this.title ? { "title": $.jgrid.stripHtml(vl)} : {};
                                if (t.p.treeGrid === true && nm == t.p.ExpandColumn) {
                                    $("td:eq(" + i + ") > span:first", ind).html(vl).attr(title);
                                } else {
                                    $("td:eq(" + i + ")", ind).html(vl).attr(title);
                                }
                            }
                        });
                        if (t.p.datatype == 'local') {
                            var pos;
                            pos = t.p._index[rowid];
                            if (typeof (pos) != 'undefined') {
                                t.p.data[pos] = $.extend(true, t.p.data[pos], lcdata);
                            }
                            lcdata = null;
                        }
                    } catch (e) {
                        success = false;
                    }
                }
                if (success) {
                    if (cp === 'string') { $(ind).addClass(cssp); } else if (cp === 'object') { $(ind).css(cssp); }
                }
            });
            return success;
        },
        addRowData: function(rowid, rdata, pos, src) {
            if (!pos) { pos = "last"; }
            var success = false, nm, row, gi, si, ni, sind, i, v, prp = "", aradd, cnm, cn, data, cm;
            if (rdata) {
                if ($.isArray(rdata)) {
                    aradd = true;
                    pos = "last";
                    cnm = rowid;
                } else {
                    rdata = [rdata];
                    aradd = false;
                }
                this.each(function() {
                    var t = this, datalen = rdata.length;
                    ni = t.p.rownumbers === true ? 1 : 0;
                    gi = t.p.multiselect === true ? 1 : 0;
                    si = t.p.subGrid === true ? 1 : 0;
                    if (!aradd) {
                        if (typeof (rowid) != 'undefined') { rowid = rowid + ""; }
                        else {
                            rowid = (t.p.records + 1) + "";
                            if (t.p.keyIndex !== false) {
                                cnm = t.p.colModel[t.p.keyIndex + gi + si + ni].name;
                                if (typeof rdata[0][cnm] != "undefined") { rowid = rdata[0][cnm]; }
                            }
                        }
                    }
                    cn = t.p.altclass;
                    var k = 0, cna = "", lcdata = {},
				air = $.isFunction(t.p.afterInsertRow) ? true : false;
                    while (k < datalen) {
                        data = rdata[k];
                        row = "";
                        if (aradd) {
                            try { rowid = data[cnm]; }
                            catch (e) { rowid = (t.p.records + 1) + ""; }
                            cna = t.p.altRows === true ? (t.rows.length - 1) % 2 === 0 ? cn : "" : "";
                        }
                        if (ni) {
                            prp = t.formatCol(0, 1, '');
                            row += "<td role=\"gridcell\" aria-describedby=\"" + t.p.id + "_rn\" class=\"ui-state-default jqgrid-rownum\" " + prp + ">0</td>";
                        }
                        if (gi) {
                            v = "<input role=\"checkbox\" type=\"checkbox\"" + " id=\"jqg_" + rowid + "\" class=\"cbox\"/>";
                            prp = t.formatCol(ni, 1, '');
                            row += "<td role=\"gridcell\" aria-describedby=\"" + t.p.id + "_cb\" " + prp + ">" + v + "</td>";
                        }
                        if (si) {
                            row += $(t).jqGrid("addSubGridCell", gi + ni, 1);
                        }
                        for (i = gi + si + ni; i < t.p.colModel.length; i++) {
                            cm = t.p.colModel[i];
                            nm = cm.name;
                            lcdata[nm] = cm.formatter && typeof (cm.formatter) === 'string' && cm.formatter == 'date' ? $.unformat.date(data[nm], cm) : data[nm];
                            v = t.formatter(rowid, data[nm], i, data, 'edit');
                            prp = t.formatCol(i, 1, v);
                            row += "<td role=\"gridcell\" aria-describedby=\"" + t.p.id + "_" + nm + "\" " + prp + ">" + v + "</td>";
                        }
                        row = "<tr id=\"" + rowid + "\" role=\"row\" class=\"ui-widget-content jqgrow ui-row-" + t.p.direction + " " + cna + "\">" + row + "</tr>";
                        if (t.p.subGrid === true) {
                            row = $(row)[0];
                            $(t).jqGrid("addSubGrid", row, gi + ni);
                        }
                        if (t.rows.length === 0) {
                            $("table:first", t.grid.bDiv).append(row);
                        } else {
                            switch (pos) {
                                case 'last':
                                    $(t.rows[t.rows.length - 1]).after(row);
                                    break;
                                case 'first':
                                    $(t.rows[0]).before(row);
                                    break;
                                case 'after':
                                    sind = t.rows.namedItem(src);
                                    if (sind) {
                                        if ($(t.rows[sind.rowIndex + 1]).hasClass("ui-subgrid")) { $(t.rows[sind.rowIndex + 1]).after(row); }
                                        else { $(sind).after(row); }
                                    }
                                    break;
                                case 'before':
                                    sind = t.rows.namedItem(src);
                                    if (sind) { $(sind).before(row); sind = sind.rowIndex; }
                                    break;
                            }
                        }
                        t.p.records++;
                        t.p.reccount++;
                        if (!t.grid.cols || !t.grid.cols.length) { t.grid.cols = t.rows[0].cells; }
                        if (pos === 'first' || (pos === 'before' && sind <= 1) || t.rows.length === 1) {
                            t.updateColumns();
                        }
                        if (air) { t.p.afterInsertRow.call(t, rowid, data, data); }
                        k++;
                        if (t.p.datatype == 'local') {
                            t.p._index[rowid] = t.p.data.length;
                            t.p.data.push(lcdata);
                            lcdata = {};
                        }
                    }
                    if (t.p.altRows === true && !aradd) {
                        if (pos == "last") {
                            if ((t.rows.length - 1) % 2 == 1) { $(t.rows[t.rows.length - 1]).addClass(cn); }
                        } else {
                            $(t.rows).each(function(i) {
                                if (i % 2 == 1) { $(this).addClass(cn); }
                                else { $(this).removeClass(cn); }
                            });
                        }
                    }
                    t.updatepager(true, true);
                    success = true;
                });
            }
            return success;
        },
        footerData: function(action, data, format) {
            var nm, success = false, res = {}, title;
            function isEmpty(obj) {
                for (var i in obj) {
                    if (obj.hasOwnProperty(i)) { return false; }
                }
                return true;
            }
            if (typeof (action) == "undefined") { action = "get"; }
            if (typeof (format) != "boolean") { format = true; }
            action = action.toLowerCase();
            this.each(function() {
                var t = this, vl;
                if (!t.grid || !t.p.footerrow) { return false; }
                if (action == "set") { if (isEmpty(data)) { return false; } }
                success = true;
                $(this.p.colModel).each(function(i) {
                    nm = this.name;
                    if (action == "set") {
                        if (data[nm] !== undefined) {
                            vl = format ? t.formatter("", data[nm], i, data, 'edit') : data[nm];
                            title = this.title ? { "title": $.jgrid.stripHtml(vl)} : {};
                            $("tr.footrow td:eq(" + i + ")", t.grid.sDiv).html(vl).attr(title);
                            success = true;
                        }
                    } else if (action == "get") {
                        res[nm] = $("tr.footrow td:eq(" + i + ")", t.grid.sDiv).html();
                    }
                });
            });
            return action == "get" ? res : success;
        },
        ShowHideCol: function(colname, show) {
            return this.each(function() {
                var $t = this, fndh = false;
                if (!$t.grid) { return; }
                if (typeof colname === 'string') { colname = [colname]; }
                show = show != "none" ? "" : "none";
                var sw = show == "" ? true : false;
                $(this.p.colModel).each(function(i) {
                    if ($.inArray(this.name, colname) !== -1 && this.hidden === sw) {
                        $("tr", $t.grid.hDiv).each(function() {
                            $("th:eq(" + i + ")", this).css("display", show);
                        });
                        $($t.rows).each(function(j) {
                            $("td:eq(" + i + ")", $t.rows[j]).css("display", show);
                        });
                        if ($t.p.footerrow) { $("td:eq(" + i + ")", $t.grid.sDiv).css("display", show); }
                        if (show == "none") { $t.p.tblwidth -= this.width; } else { $t.p.tblwidth += this.width; }
                        this.hidden = !sw;
                        fndh = true;
                    }
                });
                if (fndh === true) {
                    $("table:first", $t.grid.hDiv).width($t.p.tblwidth);
                    $("table:first", $t.grid.bDiv).width($t.p.tblwidth);
                    $t.grid.hDiv.scrollLeft = $t.grid.bDiv.scrollLeft;
                    if ($t.p.footerrow) {
                        $("table:first", $t.grid.sDiv).width($t.p.tblwidth);
                        $t.grid.sDiv.scrollLeft = $t.grid.bDiv.scrollLeft;
                    }
                }
            });
        },
        hideCol: function(colname) {
            return this.each(function() { $(this).jqGrid("ShowHideCol", colname, "none"); });
        },
        showCol: function(colname) {
            return this.each(function() { $(this).jqGrid("ShowHideCol", colname, ""); });
        },
        remapColumns: function(permutation, updateCells, keepHeader) {
            function resortArray(a) {
                var ac;
                if (a.length) {
                    ac = $.makeArray(a);
                } else {
                    ac = $.extend({}, a);
                }
                $.each(permutation, function(i) {
                    a[i] = ac[this];
                });
            }
            var ts = this.get(0);
            function resortRows(parent, clobj) {
                $(">tr" + (clobj || ""), parent).each(function() {
                    var row = this;
                    var elems = $.makeArray(row.cells);
                    $.each(permutation, function() {
                        var e = elems[this];
                        if (e) {
                            row.appendChild(e);
                        }
                    });
                });
            }
            resortArray(ts.p.colModel);
            resortArray(ts.p.colNames);
            resortArray(ts.grid.headers);
            resortRows($("thead:first", ts.grid.hDiv), keepHeader && ":not(.ui-jqgrid-labels)");
            if (updateCells) {
                resortRows($("tbody:first", ts.grid.bDiv), ".jqgrow");
            }
            if (ts.p.footerrow) {
                resortRows($("tbody:first", ts.grid.sDiv));
            }
            if (ts.p.remapColumns) {
                if (!ts.p.remapColumns.length) {
                    ts.p.remapColumns = $.makeArray(permutation);
                } else {
                    resortArray(ts.p.remapColumns);
                }
            }
            ts.p.lastsort = $.inArray(ts.p.lastsort, permutation);
            if (ts.p.treeGrid) { ts.p.expColInd = $.inArray(ts.p.expColInd, permutation); }
        },
        setGridWidth: function(nwidth, shrink) {
            return this.each(function() {
                if (!this.grid) { return; }
                var $t = this, cw,
			initwidth = 0, brd = $t.p.cellLayout, lvc, vc = 0, hs = false, scw = $t.p.scrollOffset, aw, gw = 0, tw = 0,
			cl = 0, cr;
                if (typeof shrink != 'boolean') {
                    shrink = $t.p.shrinkToFit;
                }
                if (isNaN(nwidth)) { return; }
                else { nwidth = parseInt(nwidth, 10); $t.grid.width = $t.p.width = nwidth; }
                $("#gbox_" + $t.p.id).css("width", nwidth + "px");
                $("#gview_" + $t.p.id).css("width", nwidth + "px");
                $($t.grid.bDiv).css("width", nwidth + "px");
                $($t.grid.hDiv).css("width", nwidth + "px");
                if ($t.p.pager) { $($t.p.pager).css("width", nwidth + "px"); }
                if ($t.p.toppager) { $($t.p.toppager).css("width", nwidth + "px"); }
                if ($t.p.toolbar[0] === true) {
                    $($t.grid.uDiv).css("width", nwidth + "px");
                    if ($t.p.toolbar[1] == "both") { $($t.grid.ubDiv).css("width", nwidth + "px"); }
                }
                if ($t.p.footerrow) { $($t.grid.sDiv).css("width", nwidth + "px"); }
                if (shrink === false && $t.p.forceFit === true) { $t.p.forceFit = false; }
                if (shrink === true) {
                    if (isSafari) { brd = 0; }
                    $.each($t.p.colModel, function(i) {
                        if (this.hidden === false) {
                            initwidth += parseInt(this.width, 10);
                            if (this.fixed) {
                                tw += this.width;
                                gw += this.width + brd;
                            } else {
                                vc++;
                            }
                            cl++;
                        }
                    });
                    if (vc === 0) { return; }
                    $t.p.tblwidth = initwidth;
                    aw = nwidth - brd * vc - gw;
                    if (!isNaN($t.p.height)) {
                        if ($($t.grid.bDiv)[0].clientHeight < $($t.grid.bDiv)[0].scrollHeight) {
                            hs = true;
                            aw -= scw;
                        }
                    }
                    initwidth = 0;
                    var cle = $t.grid.cols.length > 0;
                    $.each($t.p.colModel, function(i) {
                        if (this.hidden === false && !this.fixed) {
                            cw = Math.round(aw * this.width / ($t.p.tblwidth - tw));
                            if (cw < 0) { return; }
                            this.width = cw;
                            initwidth += cw;
                            $t.grid.headers[i].width = cw;
                            $t.grid.headers[i].el.style.width = cw + "px";
                            if ($t.p.footerrow) { $t.grid.footers[i].style.width = cw + "px"; }
                            if (cle) { $t.grid.cols[i].style.width = cw + "px"; }
                            lvc = i;
                        }
                    });
                    cr = 0;
                    if (hs) {
                        if (nwidth - gw - (initwidth + brd * vc) !== scw) {
                            cr = nwidth - gw - (initwidth + brd * vc) - scw;
                        }
                    } else if (Math.abs(nwidth - gw - (initwidth + brd * vc)) !== 1) {
                        cr = nwidth - gw - (initwidth + brd * vc);
                    }
                    $t.p.colModel[lvc].width += cr;
                    $t.p.tblwidth = initwidth + cr + tw + brd * cl;
                    if ($t.p.tblwidth > nwidth) {
                        var delta = $t.p.tblwidth - parseInt(nwidth, 10);
                        $t.p.tblwidth = nwidth;
                        cw = $t.p.colModel[lvc].width = $t.p.colModel[lvc].width - delta;
                    } else {
                        cw = $t.p.colModel[lvc].width;
                    }
                    $t.grid.headers[lvc].width = cw;
                    $t.grid.headers[lvc].el.style.width = cw + "px";
                    if (cle) { $t.grid.cols[lvc].style.width = cw + "px"; }
                    $('table:first', $t.grid.bDiv).css("width", $t.p.tblwidth + "px");
                    $('table:first', $t.grid.hDiv).css("width", $t.p.tblwidth + "px");
                    $t.grid.hDiv.scrollLeft = $t.grid.bDiv.scrollLeft;
                    if ($t.p.footerrow) {
                        $t.grid.footers[lvc].style.width = cw + "px";
                        $('table:first', $t.grid.sDiv).css("width", $t.p.tblwidth + "px");
                    }
                }
            });
        },
        setGridHeight: function(nh) {
            return this.each(function() {
                var $t = this;
                if (!$t.grid) { return; }
                $($t.grid.bDiv).css({ height: nh + (isNaN(nh) ? "" : "px") });
                $t.p.height = nh;
                if ($t.p.scroll) { $t.grid.populateVisible(); }
            });
        },
        setCaption: function(newcap) {
            return this.each(function() {
                this.p.caption = newcap;
                $("span.ui-jqgrid-title", this.grid.cDiv).html(newcap);
                $(this.grid.cDiv).show();
            });
        },
        setLabel: function(colname, nData, prop, attrp) {
            return this.each(function() {
                var $t = this, pos = -1;
                if (!$t.grid) { return; }
                if (isNaN(colname)) {
                    $($t.p.colModel).each(function(i) {
                        if (this.name == colname) {
                            pos = i; return false;
                        }
                    });
                } else { pos = parseInt(colname, 10); }
                if (pos >= 0) {
                    var thecol = $("tr.ui-jqgrid-labels th:eq(" + pos + ")", $t.grid.hDiv);
                    if (nData) {
                        var ico = $(".s-ico", thecol);
                        $("[id^=jqgh_]", thecol).empty().html(nData).append(ico);
                        $t.p.colNames[pos] = nData;
                    }
                    if (prop) {
                        if (typeof prop === 'string') { $(thecol).addClass(prop); } else { $(thecol).css(prop); }
                    }
                    if (typeof attrp === 'object') { $(thecol).attr(attrp); }
                }
            });
        },
        setCell: function(rowid, colname, nData, cssp, attrp, forceupd) {
            return this.each(function() {
                var $t = this, pos = -1, v, title;
                if (!$t.grid) { return; }
                if (isNaN(colname)) {
                    $($t.p.colModel).each(function(i) {
                        if (this.name == colname) {
                            pos = i; return false;
                        }
                    });
                } else { pos = parseInt(colname, 10); }
                if (pos >= 0) {
                    var ind = $t.rows.namedItem(rowid);
                    if (ind) {
                        var tcell = $("td:eq(" + pos + ")", ind);
                        if (nData !== "" || forceupd === true) {
                            v = $t.formatter(rowid, nData, pos, ind, 'edit');
                            title = $t.p.colModel[pos].title ? { "title": $.jgrid.stripHtml(v)} : {};
                            if ($t.p.treeGrid && $(".tree-wrap", $(tcell)).length > 0) {
                                $("span", $(tcell)).html(v).attr(title);
                            } else {
                                $(tcell).html(v).attr(title);
                            }
                            if ($t.p.datatype == "local") {
                                var cm = $t.p.colModel[pos], index;
                                nData = cm.formatter && typeof (cm.formatter) === 'string' && cm.formatter == 'date' ? $.unformat.date(nData, cm) : nData;
                                index = $t.p._index[rowid];
                                if (parseInt(index, 10) >= 0) {
                                    $t.p.data[index][cm.name] = nData;
                                }
                            }
                        }
                        if (typeof cssp === 'string') {
                            $(tcell).addClass(cssp);
                        } else if (cssp) {
                            $(tcell).css(cssp);
                        }
                        if (typeof attrp === 'object') { $(tcell).attr(attrp); }
                    }
                }
            });
        },
        getCell: function(rowid, col) {
            var ret = false;
            this.each(function() {
                var $t = this, pos = -1;
                if (!$t.grid) { return; }
                if (isNaN(col)) {
                    $($t.p.colModel).each(function(i) {
                        if (this.name === col) {
                            pos = i; return false;
                        }
                    });
                } else { pos = parseInt(col, 10); }
                if (pos >= 0) {
                    var ind = $t.rows.namedItem(rowid);
                    if (ind) {
                        try {
                            ret = $.unformat($("td:eq(" + pos + ")", ind), { rowId: ind.id, colModel: $t.p.colModel[pos] }, pos);
                        } catch (e) {
                            ret = $.jgrid.htmlDecode($("td:eq(" + pos + ")", ind).html());
                        }
                    }
                }
            });
            return ret;
        },
        getCol: function(col, obj, mathopr) {
            var ret = [], val, sum = 0;
            obj = typeof (obj) != 'boolean' ? false : obj;
            if (typeof mathopr == 'undefined') { mathopr = false; }
            this.each(function() {
                var $t = this, pos = -1;
                if (!$t.grid) { return; }
                if (isNaN(col)) {
                    $($t.p.colModel).each(function(i) {
                        if (this.name === col) {
                            pos = i; return false;
                        }
                    });
                } else { pos = parseInt(col, 10); }
                if (pos >= 0) {
                    var ln = $t.rows.length, i = 0;
                    if (ln && ln > 0) {
                        while (i < ln) {
                            try {
                                val = $.unformat($($t.rows[i].cells[pos]), { rowId: $t.rows[i].id, colModel: $t.p.colModel[pos] }, pos);
                            } catch (e) {
                                val = $.jgrid.htmlDecode($t.rows[i].cells[pos].innerHTML);
                            }
                            if (mathopr) { sum += parseFloat(val); }
                            else if (obj) { ret.push({ id: $t.rows[i].id, value: val }); }
                            else { ret[i] = val; }
                            i++;
                        }
                        if (mathopr) {
                            switch (mathopr.toLowerCase()) {
                                case 'sum': ret = sum; break;
                                case 'avg': ret = sum / ln; break;
                                case 'count': ret = ln; break;
                            }
                        }
                    }
                }
            });
            return ret;
        },
        clearGridData: function(clearfooter) {
            return this.each(function() {
                var $t = this;
                if (!$t.grid) { return; }
                if (typeof clearfooter != 'boolean') { clearfooter = false; }
                $("tbody:first tr", $t.grid.bDiv).remove();
                if ($t.p.footerrow && clearfooter) { $(".ui-jqgrid-ftable td", $t.grid.sDiv).html("&#160;"); }
                $t.p.selrow = null; $t.p.selarrrow = []; $t.p.savedRow = [];
                $t.p.records = 0; $t.p.page = '0'; $t.p.lastpage = '0'; $t.p.reccount = 0;
                $t.p.data = []; $t.p_index = {};
                $t.updatepager(true, false);
            });
        },
        getInd: function(rowid, rc) {
            var ret = false, rw;
            this.each(function() {
                rw = this.rows.namedItem(rowid);
                if (rw) {
                    ret = rc === true ? rw : rw.rowIndex;
                }
            });
            return ret;
        }
    });
})(jQuery);
;/*
**
 * formatter for values but most of the values if for jqGrid
 * Some of this was inspired and based on how YUI does the table datagrid but in jQuery fashion
 * we are trying to keep it as light as possible
 * Joshua Burnett josh@9ci.com	
 * http://www.greenbill.com
 *
 * Changes from Tony Tomov tony@trirand.com
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 * 
**/

;(function($) {
	$.fmatter = {};
	//opts can be id:row id for the row, rowdata:the data for the row, colmodel:the column model for this column
	//example {id:1234,}
	$.fn.fmatter = function(formatType, cellval, opts, rwd, act) {
		//debug(this);
		//debug(cellval);
		// build main options before element iteration
		opts = $.extend({}, $.jgrid.formatter, opts);
		return fireFormatter(formatType,cellval, opts, rwd, act); 
	};
	$.fmatter.util = {
		// Taken from YAHOO utils
		NumberFormat : function(nData,opts) {
			if(!isNumber(nData)) {
				nData *= 1;
			}
			if(isNumber(nData)) {
		        var bNegative = (nData < 0);
				var sOutput = nData + "";
				var sDecimalSeparator = (opts.decimalSeparator) ? opts.decimalSeparator : ".";
				var nDotIndex;
				if(isNumber(opts.decimalPlaces)) {
					// Round to the correct decimal place
					var nDecimalPlaces = opts.decimalPlaces;
					var nDecimal = Math.pow(10, nDecimalPlaces);
					sOutput = Math.round(nData*nDecimal)/nDecimal + "";
					nDotIndex = sOutput.lastIndexOf(".");
					if(nDecimalPlaces > 0) {
                    // Add the decimal separator
						if(nDotIndex < 0) {
							sOutput += sDecimalSeparator;
							nDotIndex = sOutput.length-1;
						}
						// Replace the "."
						else if(sDecimalSeparator !== "."){
							sOutput = sOutput.replace(".",sDecimalSeparator);
						}
                    // Add missing zeros
						while((sOutput.length - 1 - nDotIndex) < nDecimalPlaces) {
						    sOutput += "0";
						}
	                }
	            }
	            if(opts.thousandsSeparator) {
	                var sThousandsSeparator = opts.thousandsSeparator;
	                nDotIndex = sOutput.lastIndexOf(sDecimalSeparator);
	                nDotIndex = (nDotIndex > -1) ? nDotIndex : sOutput.length;
	                var sNewOutput = sOutput.substring(nDotIndex);
	                var nCount = -1;
	                for (var i=nDotIndex; i>0; i--) {
	                    nCount++;
	                    if ((nCount%3 === 0) && (i !== nDotIndex) && (!bNegative || (i > 1))) {
	                        sNewOutput = sThousandsSeparator + sNewOutput;
	                    }
	                    sNewOutput = sOutput.charAt(i-1) + sNewOutput;
	                }
	                sOutput = sNewOutput;
	            }
	            // Prepend prefix
	            sOutput = (opts.prefix) ? opts.prefix + sOutput : sOutput;
	            // Append suffix
	            sOutput = (opts.suffix) ? sOutput + opts.suffix : sOutput;
	            return sOutput;
				
			} else {
				return nData;
			}
		},
		// Tony Tomov
		// PHP implementation. Sorry not all options are supported.
		// Feel free to add them if you want
		DateFormat : function (format, date, newformat, opts)  {
			var	token = /\\.|[dDjlNSwzWFmMntLoYyaABgGhHisueIOPTZcrU]/g,
			timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
			timezoneClip = /[^-+\dA-Z]/g,
			pad = function (value, length) {
				value = String(value);
				length = parseInt(length) || 2;
				while (value.length < length) value = '0' + value;
				return value;
			},
		    ts = {m : 1, d : 1, y : 1970, h : 0, i : 0, s : 0},
		    timestamp=0, dM, k,hl,
		    dateFormat=["i18n"];
			// Internationalization strings
		    dateFormat["i18n"] = {
				dayNames:   opts.dayNames,
		    	monthNames: opts.monthNames
			};
			if( format in opts.masks ) format = opts.masks[format];
			date = date.split(/[\\\/:_;.\t\T\s-]/);
			format = format.split(/[\\\/:_;.\t\T\s-]/);
			// parsing for month names
		    for(k=0,hl=format.length;k<hl;k++){
				if(format[k] == 'M') {
					dM = $.inArray(date[k],dateFormat.i18n.monthNames);
					if(dM !== -1 && dM < 12){date[k] = dM+1;}
				}
				if(format[k] == 'F') {
					dM = $.inArray(date[k],dateFormat.i18n.monthNames);
					if(dM !== -1 && dM > 11){date[k] = dM+1-12;}
				}
		        ts[format[k].toLowerCase()] = parseInt(date[k],10);
		    }
		    ts.m = parseInt(ts.m)-1;
		    var ty = ts.y;
		    if (ty >= 70 && ty <= 99) ts.y = 1900+ts.y;
		    else if (ty >=0 && ty <=69) ts.y= 2000+ts.y;
		    timestamp = new Date(ts.y, ts.m, ts.d, ts.h, ts.i, ts.s,0);
			if( newformat in opts.masks )  {
				newformat = opts.masks[newformat];
			} else if ( !newformat ) {
				newformat = 'Y-m-d';
			}
		    var 
		        G = timestamp.getHours(),
		        i = timestamp.getMinutes(),
		        j = timestamp.getDate(),
				n = timestamp.getMonth() + 1,
				o = timestamp.getTimezoneOffset(),
				s = timestamp.getSeconds(),
				u = timestamp.getMilliseconds(),
				w = timestamp.getDay(),
				Y = timestamp.getFullYear(),
				N = (w + 6) % 7 + 1,
				z = (new Date(Y, n - 1, j) - new Date(Y, 0, 1)) / 86400000,
				flags = {
					// Day
					d: pad(j),
					D: dateFormat.i18n.dayNames[w],
					j: j,
					l: dateFormat.i18n.dayNames[w + 7],
					N: N,
					S: opts.S(j),
					//j < 11 || j > 13 ? ['st', 'nd', 'rd', 'th'][Math.min((j - 1) % 10, 3)] : 'th',
					w: w,
					z: z,
					// Week
					W: N < 5 ? Math.floor((z + N - 1) / 7) + 1 : Math.floor((z + N - 1) / 7) || ((new Date(Y - 1, 0, 1).getDay() + 6) % 7 < 4 ? 53 : 52),
					// Month
					F: dateFormat.i18n.monthNames[n - 1 + 12],
					m: pad(n),
					M: dateFormat.i18n.monthNames[n - 1],
					n: n,
					t: '?',
					// Year
					L: '?',
					o: '?',
					Y: Y,
					y: String(Y).substring(2),
					// Time
					a: G < 12 ? opts.AmPm[0] : opts.AmPm[1],
					A: G < 12 ? opts.AmPm[2] : opts.AmPm[3],
					B: '?',
					g: G % 12 || 12,
					G: G,
					h: pad(G % 12 || 12),
					H: pad(G),
					i: pad(i),
					s: pad(s),
					u: u,
					// Timezone
					e: '?',
					I: '?',
					O: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
					P: '?',
					T: (String(timestamp).match(timezone) || [""]).pop().replace(timezoneClip, ""),
					Z: '?',
					// Full Date/Time
					c: '?',
					r: '?',
					U: Math.floor(timestamp / 1000)
				};	
			return newformat.replace(token, function ($0) {
				return $0 in flags ? flags[$0] : $0.substring(1);
			});			
		}
	};
	$.fn.fmatter.defaultFormat = function(cellval, opts) {
		return (isValue(cellval) && cellval!=="" ) ?  cellval : opts.defaultValue ? opts.defaultValue : "&#160;";
	};
	$.fn.fmatter.email = function(cellval, opts) {
		if(!isEmpty(cellval)) {
			return "<a href=\"mailto:" + cellval + "\">" + cellval + "</a>";
        }else {
			return $.fn.fmatter.defaultFormat(cellval,opts );
        }
	};
	$.fn.fmatter.checkbox =function(cval, opts) {
		var op = $.extend({},opts.checkbox), ds;
		if(!isUndefined(opts.colModel.formatoptions)) {
			op = $.extend({},op,opts.colModel.formatoptions);
		}
		if(op.disabled===true) {ds = "disabled";} else {ds="";}
		if(isEmpty(cval) || isUndefined(cval) ) cval = $.fn.fmatter.defaultFormat(cval,op);
		cval=cval+""; cval=cval.toLowerCase();
		var bchk = cval.search(/(false|0|no|off)/i)<0 ? " checked='checked' " : "";
        return "<input type=\"checkbox\" " + bchk  + " value=\""+ cval+"\" offval=\"no\" "+ds+ "/>";
    },
	$.fn.fmatter.link = function(cellval, opts) {
		var op = {target:opts.target };
		var target = "";
		if(!isUndefined(opts.colModel.formatoptions)) {
            op = $.extend({},op,opts.colModel.formatoptions);
        }
		if(op.target) {target = 'target=' + op.target;}
        if(!isEmpty(cellval)) {
			return "<a "+target+" href=\"" + cellval + "\">" + cellval + "</a>";
        }else {
            return $.fn.fmatter.defaultFormat(cellval,opts);
        }
    };
	$.fn.fmatter.showlink = function(cellval, opts) {
		var op = {baseLinkUrl: opts.baseLinkUrl,showAction:opts.showAction, addParam: opts.addParam || "", target: opts.target, idName: opts.idName },
		target = "";
		if(!isUndefined(opts.colModel.formatoptions)) {
			op = $.extend({},op,opts.colModel.formatoptions);
		}
		if(op.target) {target = 'target=' + op.target;}
		idUrl = op.baseLinkUrl+op.showAction + '?'+ op.idName+'='+opts.rowId+op.addParam;
        if(isString(cellval)) {	//add this one even if its blank string
			return "<a "+target+" href=\"" + idUrl + "\">" + cellval + "</a>";
        }else {
			return $.fn.fmatter.defaultFormat(cellval,opts);
	    }
    };
	$.fn.fmatter.integer = function(cellval, opts) {
		var op = $.extend({},opts.integer);
		if(!isUndefined(opts.colModel.formatoptions)) {
			op = $.extend({},op,opts.colModel.formatoptions);
		}
		if(isEmpty(cellval)) {
			return op.defaultValue;
		}
		return $.fmatter.util.NumberFormat(cellval,op);
	};
	$.fn.fmatter.number = function (cellval, opts) {
		var op = $.extend({},opts.number);
		if(!isUndefined(opts.colModel.formatoptions)) {
			op = $.extend({},op,opts.colModel.formatoptions);
		}
		if(isEmpty(cellval)) {
			return op.defaultValue;
		}
		return $.fmatter.util.NumberFormat(cellval,op);
	};
	$.fn.fmatter.currency = function (cellval, opts) {
		var op = $.extend({},opts.currency);
		if(!isUndefined(opts.colModel.formatoptions)) {
			op = $.extend({},op,opts.colModel.formatoptions);
		}
		if(isEmpty(cellval)) {
			return op.defaultValue;
		}
		return $.fmatter.util.NumberFormat(cellval,op);
	};
	$.fn.fmatter.date = function (cellval, opts, rwd, act) {
		var op = $.extend({},opts.date);
		if(!isUndefined(opts.colModel.formatoptions)) {
			op = $.extend({},op,opts.colModel.formatoptions);
		}
		if(!op.reformatAfterEdit && act=='edit'){
			return $.fn.fmatter.defaultFormat(cellval, opts);
		} else if(!isEmpty(cellval)) {
			return  $.fmatter.util.DateFormat(op.srcformat,cellval,op.newformat,op);
		} else {
			return $.fn.fmatter.defaultFormat(cellval, opts);
		}
	};
	$.fn.fmatter.select = function (cellval,opts, rwd, act) {
		// jqGrid specific
		cellval = cellval + "";
		var oSelect = false, ret=[];
		if(!isUndefined(opts.colModel.editoptions)){
			oSelect= opts.colModel.editoptions.value;
		}
		if (oSelect) {
			var	msl =  opts.colModel.editoptions.multiple === true ? true : false,
			scell = [], sv;
			if(msl) { scell = cellval.split(","); scell = $.map(scell,function(n){return $.trim(n);})}
			if (isString(oSelect)) {
				// mybe here we can use some caching with care ????
				var so = oSelect.split(";"), j=0;
				for(var i=0; i<so.length;i++){
					sv = so[i].split(":");
					if(sv.length > 2 ) {
						sv[1] = jQuery.map(sv,function(n,i){if(i>0)return n;}).join(":");
					}
					if(msl) {
						if(jQuery.inArray(sv[0],scell)>-1) {
							ret[j] = sv[1];
							j++;
						}
					} else if($.trim(sv[0])==$.trim(cellval)) {
						ret[0] = sv[1];
						break;
					}
				}
			} else if(isObject(oSelect)) {
				// this is quicker
				if(msl) {
					ret = jQuery.map(scell, function(n, i){
						return oSelect[n];
					});
				} else {
					ret[0] = oSelect[cellval] || "";
				}
			}
		}
		cellval = ret.join(", ");
		return  cellval == "" ? $.fn.fmatter.defaultFormat(cellval,opts) : cellval;
	};
	$.fn.fmatter.rowactions = function(rid,gid,act,keys) {
		switch(act)
		{
			case 'edit':
				var restorerow = function()	{
					$(".ui-inline-edit, .ui-inline-del","#"+rid).show();
					$(".ui-inline-save, .ui-inline-cancel","#"+rid).hide();
				}
				$('#'+gid).jqGrid('editRow',rid,keys,null,null,null,{oper:'edit'},restorerow,null,restorerow);
				$(".ui-inline-edit, .ui-inline-del","#"+rid).hide();
				$(".ui-inline-save, .ui-inline-cancel","#"+rid).show();
			break;
			case 'save':
				$('#'+gid).jqGrid('saveRow',rid,null,null,{oper:'edit'});
				$(".ui-inline-edit, .ui-inline-del","#"+rid).show();
				$(".ui-inline-save, .ui-inline-cancel","#"+rid).hide();
				break;
			case 'cancel' :
				$('#'+gid).jqGrid('restoreRow',rid);
				$(".ui-inline-edit, .ui-inline-del","#"+rid).show();
				$(".ui-inline-save, .ui-inline-cancel","#"+rid).hide();
				break;
		}
	};
	$.fn.fmatter.actions = function(cellval,opts, rwd) {
		var op ={keys:false};
		if(!isUndefined(opts.colModel.formatoptions)) {
			op = $.extend(op,opts.colModel.formatoptions);
		}
		var rowid = opts.rowId;
		if(typeof(rowid) =='undefined' || isEmpty(rowid)) return "";
		var ocl = "onclick=$.fn.fmatter.rowactions('"+rowid+"','"+opts.gid+"','edit',"+op.keys+");"
		var str = "<div style='margin-left:8px;'><div title='"+$.jgrid.nav.edittitle+"' style='float:left;cursor:pointer;' class='ui-pg-div ui-inline-edit' "+ocl+"><span class='ui-icon ui-icon-pencil'></span></div>";
		ocl = "onclick=jQuery('#"+opts.gid+"').jqGrid('delGridRow','"+rowid+"');"
		str = str+"<div title='"+$.jgrid.nav.deltitle+"' style='float:left;margin-left:5px;' class='ui-pg-div ui-inline-del' "+ocl+"><span class='ui-icon ui-icon-trash'></span></div>";
		ocl = "onclick=$.fn.fmatter.rowactions('"+rowid+"','"+opts.gid+"','save',false);"
		str = str+"<div title='"+$.jgrid.edit.bSubmit+"' style='float:left;display:none' class='ui-pg-div ui-inline-save'><span class='ui-icon ui-icon-disk' "+ocl+"></span></div>";
		ocl = "onclick=$.fn.fmatter.rowactions('"+rowid+"','"+opts.gid+"','cancel',false);"
		str = str+"<div title='"+$.jgrid.edit.bCancel+"' style='float:left;display:none;margin-left:5px;' class='ui-pg-div ui-inline-cancel'><span class='ui-icon ui-icon-cancel' "+ocl+"></span></div></div>";
		return str;
	};
	$.unformat = function (cellval,options,pos,cnt) {
		// specific for jqGrid only
		var ret, formatType = options.colModel.formatter,
		op =options.colModel.formatoptions || {}, sep,
		re = /([\.\*\_\'\(\)\{\}\+\?\\])/g;
		unformatFunc = options.colModel.unformat||($.fn.fmatter[formatType] && $.fn.fmatter[formatType].unformat);
		if(typeof unformatFunc !== 'undefined' && isFunction(unformatFunc) ) {
			ret = unformatFunc($(cellval).text(), options, cellval);
		} else if(typeof formatType !== 'undefined' && isString(formatType) ) {
			var opts = $.jgrid.formatter || {}, stripTag;
			switch(formatType) {
				case 'integer' :
					op = $.extend({},opts.integer,op);
					sep = op.thousandsSeparator.replace(re,"\\$1");
					stripTag = new RegExp(sep, "g");
					ret = $(cellval).text().replace(stripTag,'');
					break;
				case 'number' :
					op = $.extend({},opts.number,op);
					sep = op.thousandsSeparator.replace(re,"\\$1");
					stripTag = new RegExp(sep, "g");
					ret = $(cellval).text().replace(stripTag,"").replace(op.decimalSeparator,'.');
					break;
				case 'currency':
					op = $.extend({},opts.currency,op);
					sep = op.thousandsSeparator.replace(re,"\\$1");
					stripTag = new RegExp(sep, "g");
					ret = $(cellval).text().replace(stripTag,'').replace(op.decimalSeparator,'.').replace(op.prefix,'').replace(op.suffix,'');
					break;
				case 'checkbox':
					var cbv = (options.colModel.editoptions) ? options.colModel.editoptions.value.split(":") : ["Yes","No"];
					ret = $('input',cellval).attr("checked") ? cbv[0] : cbv[1];
					break;
				case 'select' :
					ret = $.unformat.select(cellval,options,pos,cnt);
					break;
				case 'actions':
					return "";
                default:
                    ret= $(cellval).text();
			}
		}
		return ret ? ret : cnt===true ? $(cellval).text() : $.jgrid.htmlDecode($(cellval).html());
	};
	$.unformat.select = function (cellval,options,pos,cnt) {
		// Spacial case when we have local data and perform a sort
		// cnt is set to true only in sortDataArray
		var ret = [];
		var cell = $(cellval).text();
		if(cnt==true) return cell;
		var op = $.extend({},options.colModel.editoptions);
		if(op.value){
			var oSelect = op.value,
			msl =  op.multiple === true ? true : false,
			scell = [], sv;
			if(msl) { scell = cell.split(","); scell = $.map(scell,function(n){return $.trim(n);})}
			if (isString(oSelect)) {
				var so = oSelect.split(";"), j=0;
				for(var i=0; i<so.length;i++){
					sv = so[i].split(":");
					if(msl) {
						if(jQuery.inArray(sv[1],scell)>-1) {
							ret[j] = sv[0];
							j++;
						}
					} else if($.trim(sv[1])==$.trim(cell)) {
						ret[0] = sv[0];
						break;
					}
				}
			} else if(isObject(oSelect)) {
				if(!msl) scell[0] =  cell;
				ret = jQuery.map(scell, function(n){
					var rv;
					$.each(oSelect, function(i,val){
						if (val == n) {
							rv = i;
							return false;
						}
					});
					if( rv) return rv;
				});
			}
			return ret.join(", ");
		} else {
			return cell || "";
		}
	};
	function fireFormatter(formatType,cellval, opts, rwd, act) {
		var v=cellval;

        if ($.fn.fmatter[formatType]){
            v = $.fn.fmatter[formatType](cellval, opts, rwd, act);
        }

        return v;
	};
	//private methods and data
	function debug($obj) {
		if (window.console && window.console.log) window.console.log($obj);
	};
	/**
     * A convenience method for detecting a legitimate non-null value.
     * Returns false for null/undefined/NaN, true for other values, 
     * including 0/false/''
	 *  --taken from the yui.lang
     */
    isValue= function(o) {
		return (isObject(o) || isString(o) || isNumber(o) || isBoolean(o));
    };
	isBoolean= function(o) {
        return typeof o === 'boolean';
    };
    isNull= function(o) {
        return o === null;
    };
    isNumber= function(o) {
        return typeof o === 'number' && isFinite(o);
    };
    isString= function(o) {
        return typeof o === 'string';
    };
	/**
	* check if its empty trim it and replace \&nbsp and \&#160 with '' and check if its empty ===""
	* if its is not a string but has a value then it returns false, Returns true for null/undefined/NaN
	essentailly this provdes a way to see if it has any value to format for things like links
	*/
 	isEmpty= function(o) {
		if(!isString(o) && isValue(o)) {
			return false;
		}else if (!isValue(o)){
			return true;
		}
		o = $.trim(o).replace(/\&nbsp\;/ig,'').replace(/\&#160\;/ig,'');
        return o==="";
		
    };
    isUndefined= function(o) {
        return typeof o === 'undefined';
    };
	isObject= function(o) {
		return (o && (typeof o === 'object' || isFunction(o))) || false;
    };
	isFunction= function(o) {
        return typeof o === 'function';
    };

})(jQuery);;/*
 * jqGrid common function
 * Tony Tomov tony@trirand.com
 * http://trirand.com/blog/ 
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
*/ 
// Modal functions
var showModal = function(h) {
	h.w.show();
};
var closeModal = function(h) {
	h.w.hide().attr("aria-hidden","true");
	if(h.o) { h.o.remove(); }
};
var createModal = function(aIDs, content, p, insertSelector, posSelector, appendsel) {
	var mw  = document.createElement('div'), rtlsup;
	rtlsup = jQuery(p.gbox).attr("dir") == "rtl" ? true : false;
	mw.className= "ui-widget ui-widget-content ui-corner-all ui-jqdialog";
	mw.id = aIDs.themodal; 
	var mh = document.createElement('div');
	mh.className = "ui-jqdialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix";
	mh.id = aIDs.modalhead;
	jQuery(mh).append("<span class='ui-jqdialog-title'>"+p.caption+"</span>");
	var ahr= jQuery("<a href='javascript:void(0)' class='ui-jqdialog-titlebar-close ui-corner-all'></a>")
	.hover(function(){ahr.addClass('ui-state-hover');},
		   function(){ahr.removeClass('ui-state-hover');})
	.append("<span class='ui-icon ui-icon-closethick'></span>");
	jQuery(mh).append(ahr);
	if(rtlsup) {
		mw.dir = "rtl";
		jQuery(".ui-jqdialog-title",mh).css("float","right");
		jQuery(".ui-jqdialog-titlebar-close",mh).css("left",0.3+"em");
	} else {
		mw.dir = "ltr";
		jQuery(".ui-jqdialog-title",mh).css("float","left");
		jQuery(".ui-jqdialog-titlebar-close",mh).css("right",0.3+"em");
	}
	var mc = document.createElement('div');
	jQuery(mc).addClass("ui-jqdialog-content ui-widget-content").attr("id",aIDs.modalcontent);
	jQuery(mc).append(content);
	mw.appendChild(mc);
	jQuery(mw).prepend(mh);
	if(appendsel===true) { jQuery('body').append(mw); } //append as first child in body -for alert dialog
	else {jQuery(mw).insertBefore(insertSelector);}
	if(typeof p.jqModal === 'undefined') {p.jqModal = true;} // internal use
	var coord = {};
	if ( jQuery.fn.jqm && p.jqModal === true) {
		if(p.left ==0 && p.top==0) {
			var pos = [];
			pos = findPos(posSelector);
			p.left = pos[0] + 4;
			p.top = pos[1] + 4;
		}
		coord.top = p.top+"px";
		coord.left = p.left;
	} else if(p.left !=0 || p.top!=0) {
		coord.left = p.left;
		coord.top = p.top+"px";
	}
	jQuery("a.ui-jqdialog-titlebar-close",mh).click(function(e){
		var oncm = jQuery("#"+aIDs.themodal).data("onClose") || p.onClose;
		var gboxclose = jQuery("#"+aIDs.themodal).data("gbox") || p.gbox;
		hideModal("#"+aIDs.themodal,{gb:gboxclose,jqm:p.jqModal,onClose:oncm});
		return false;
	});
	if (p.width == 0 || !p.width) {p.width = 300;}
	if(p.height==0 || !p.height) {p.height =200;}
	if(!p.zIndex) {p.zIndex = 950;}
	var rtlt = 0;
	if( rtlsup && coord.left && !appendsel) {
		rtlt = jQuery(p.gbox).width()- (!isNaN(p.width) ? parseInt(p.width) :0) - 8; // to do
		// just in case
		coord.left = parseInt(coord.left) + parseInt(rtlt);
	}
	if(coord.left) coord.left += "px";
	jQuery(mw).css(jQuery.extend({
		width: isNaN(p.width) ? "auto": p.width+"px",
		height:isNaN(p.height) ? "auto" : p.height + "px",
		zIndex:p.zIndex,
		overflow: 'hidden'
	},coord))
	.attr({tabIndex: "-1","role":"dialog","aria-labelledby":aIDs.modalhead,"aria-hidden":"true"});
	if(typeof p.drag == 'undefined') { p.drag=true;}
	if(typeof p.resize == 'undefined') {p.resize=true;}
	if (p.drag) {
		jQuery(mh).css('cursor','move');
		if(jQuery.fn.jqDrag) {
			jQuery(mw).jqDrag(mh);
		} else {
			try {
				jQuery(mw).draggable({handle: jQuery("#"+mh.id)});
			} catch (e) {}
		}
	}
	if(p.resize) {
		if(jQuery.fn.jqResize) {
			jQuery(mw).append("<div class='jqResize ui-resizable-handle ui-resizable-se ui-icon ui-icon-gripsmall-diagonal-se ui-icon-grip-diagonal-se'></div>");
			jQuery("#"+aIDs.themodal).jqResize(".jqResize",aIDs.scrollelm ? "#"+aIDs.scrollelm : false);
		} else {
			try {
				jQuery(mw).resizable({handles: 'se, sw',alsoResize: aIDs.scrollelm ? "#"+aIDs.scrollelm : false});
			} catch (e) {}
		}
	}
	if(p.closeOnEscape === true){
		jQuery(mw).keydown( function( e ) {
			if( e.which == 27 ) {
				var cone = jQuery("#"+aIDs.themodal).data("onClose") || p.onClose;
				hideModal(this,{gb:p.gbox,jqm:p.jqModal,onClose: cone});
			}
		});
	}
};
var viewModal = function (selector,o){
	o = jQuery.extend({
		toTop: true,
		overlay: 10,
		modal: false,
		onShow: showModal,
		onHide: closeModal,
		gbox: '',
		jqm : true,
		jqM : true
	}, o || {});
	if (jQuery.fn.jqm && o.jqm == true) {
		if(o.jqM) jQuery(selector).attr("aria-hidden","false").jqm(o).jqmShow();
		else jQuery(selector).attr("aria-hidden","false").jqmShow();
	} else {
		if(o.gbox != '') {
			jQuery(".jqgrid-overlay:first",o.gbox).show();
			jQuery(selector).data("gbox",o.gbox);
		}
		jQuery(selector).show().attr("aria-hidden","false");
		try{jQuery(':input:visible',selector)[0].focus();}catch(_){}
	}
};
var hideModal = function (selector,o) {
	o = jQuery.extend({jqm : true, gb :''}, o || {});
    if(o.onClose) {
		var oncret =  o.onClose(selector);
		if (typeof oncret == 'boolean'  && !oncret ) return;
    }	
	if (jQuery.fn.jqm && o.jqm === true) {
		jQuery(selector).attr("aria-hidden","true").jqmHide();
	} else {
		if(o.gb != '') {
			try {jQuery(".jqgrid-overlay:first",o.gb).hide();} catch (e){}
		}
		jQuery(selector).hide().attr("aria-hidden","true");
	}
};

function info_dialog(caption, content,c_b, modalopt) {
	var mopt = {
		width:290,
		height:'auto',
		dataheight: 'auto',
		drag: true,
		resize: false,
		caption: "<strong>" + caption + "</strong>",
		left:250,
		top:170,
		zIndex : 1000,
		jqModal : true,
		modal : false,
		closeOnEscape : true,
		align: 'center',
		buttonalign : 'center',
		buttons : []
		// {text:'textbutt', id:"buttid", onClick : function(){...}}
		// if the id is not provided we set it like info_button_+ the index in the array - i.e info_button_0,info_button_1...
	};
	jQuery.extend(mopt,modalopt || {});
	var jm = mopt.jqModal;
	if(jQuery.fn.jqm && !jm) jm = false;
	// in case there is no jqModal
	var buttstr ="";
	if(mopt.buttons.length > 0) {
		for(var i=0;i<mopt.buttons.length;i++) {
			if(typeof mopt.buttons[i].id == "undefined") mopt.buttons[i].id = "info_button_"+i;
			buttstr += "<a href='javascript:void(0)' id='"+mopt.buttons[i].id+"' class='fm-button ui-state-default ui-corner-all'>"+mopt.buttons[i].text+"</a>";
		}
	}
	var dh = isNaN(mopt.dataheight) ? mopt.dataheight : mopt.dataheight+"px",
	cn = "text-align:"+mopt.align+";";
	var cnt = "<div id='info_id'>";
	cnt += "<div id='infocnt' style='margin:0px;padding-bottom:1em;width:100%;overflow:auto;position:relative;height:"+dh+";"+cn+"'>"+content+"</div>";
	cnt += c_b ? "<div class='ui-widget-content ui-helper-clearfix' style='text-align:"+mopt.buttonalign+";padding-bottom:0.8em;padding-top:0.5em;background-image: none;border-width: 1px 0 0 0;'><a href='javascript:void(0)' id='closedialog' class='fm-button ui-state-default ui-corner-all'>"+c_b+"</a>"+buttstr+"</div>" : "";
	cnt += "</div>";

	try {
		if(jQuery("#info_dialog").attr("aria-hidden") == "false")
			hideModal("#info_dialog",{jqm:jm});
		jQuery("#info_dialog").remove();
	} catch (e){}
	createModal({
		themodal:'info_dialog',
		modalhead:'info_head',
		modalcontent:'info_content',
		scrollelm: 'infocnt'},
		cnt,
		mopt,
		'','',true
	);
	// attach onclick after inserting into the dom
	if(buttstr) {
		jQuery.each(mopt.buttons,function(i){
			jQuery("#"+this.id,"#info_id").bind('click',function(){mopt.buttons[i].onClick.call(jQuery("#info_dialog")); return false;});
		});
	}
	jQuery("#closedialog", "#info_id").click(function(e){
		hideModal("#info_dialog",{jqm:jm});
		return false;
	});
	jQuery(".fm-button","#info_dialog").hover(
		function(){jQuery(this).addClass('ui-state-hover');}, 
		function(){jQuery(this).removeClass('ui-state-hover');}
	);
	viewModal("#info_dialog",{
		onHide: function(h) {
			h.w.hide().remove();
			if(h.o) { h.o.remove(); }
		},
		modal :mopt.modal,
		jqm:jm
	});
	try{$("#info_dialog").focus();} catch (e){}
}
//Helper functions
function findPos(obj) {
	var curleft = curtop = 0;
	if (obj.offsetParent) {
		do {
			curleft += obj.offsetLeft;
			curtop += obj.offsetTop; 
		} while (obj = obj.offsetParent);
		//do not change obj == obj.offsetParent 
	}
	return [curleft,curtop];
}
function isArray(obj) {
	if (obj.constructor.toString().indexOf("Array") == -1) {
		return false;
	} else {
		return true;
	}
}
// Form Functions
function createEl(eltype,options,vl,autowidth, ajaxso) {
	var elem = "";
	if(options.defaultValue) delete options['defaultValue'];
	function bindEv (el, opt) {
		if(jQuery.isFunction(opt.dataInit)) {
			// datepicker fix 
			el.id = opt.id;
			opt.dataInit(el);
			delete opt['id'];
			delete opt['dataInit'];
		}
		if(opt.dataEvents) {
		    jQuery.each(opt.dataEvents, function() {
		        if (this.data != null)
			        jQuery(el).bind(this.type, this.data, this.fn);
		        else
		            jQuery(el).bind(this.type, this.fn);
		    });
			delete opt['dataEvents'];
		}
		return opt;
	}
	switch (eltype)
	{
		case "textarea" :
				elem = document.createElement("textarea");
				if(autowidth) {
					if(!options.cols) jQuery(elem).css({width:"98%"});
				} else if (!options.cols) options.cols = 20;
				if(!options.rows) options.rows = 2;
				if(vl=='&nbsp;' || vl=='&#160;' || (vl.length==1 && vl.charCodeAt(0)==160)) {vl="";}
				elem.value = vl;
				options = bindEv(elem,options);
				jQuery(elem).attr(options);
				break;
		case "checkbox" : //what code for simple checkbox
			elem = document.createElement("input");
			elem.type = "checkbox";
			if( !options.value ) {
				var vl1 = vl.toLowerCase();
				if(vl1.search(/(false|0|no|off|undefined)/i)<0 && vl1!=="") {
					elem.checked=true;
					elem.defaultChecked=true;
					elem.value = vl;
				} else {
					elem.value = "on";
				}
				jQuery(elem).attr("offval","off");
			} else {
				var cbval = options.value.split(":");
				if(vl === cbval[0]) {
					elem.checked=true;
					elem.defaultChecked=true;
				}
				elem.value = cbval[0];
				jQuery(elem).attr("offval",cbval[1]);
				try {delete options['value'];} catch (e){}
			}
			options = bindEv(elem,options);
			jQuery(elem).attr(options);
			break;
		case "select" :
			elem = document.createElement("select");
			var msl, ovm = [];
			if(options.multiple===true) {
				msl = true;
				elem.multiple="multiple";
			} else msl = false;
			if(options.dataUrl != null) {
				jQuery.ajax(jQuery.extend({
					url: options.dataUrl,
					type : "GET",
					complete: function(data,status){
						try {delete options['dataUrl'];delete options['value'];} catch (e){}
						var a;
						if(options.buildSelect != null) {
							var b = options.buildSelect(data);
							a = jQuery(b).html();
							delete options['buildSelect'];
						} else 
							a = jQuery(data.responseText).html();
						if(a) {
							jQuery(elem).append(a);
							options = bindEv(elem,options);
							if(typeof options.size === 'undefined') { options.size =  msl ? 3 : 1;}
							if(msl) {
								ovm = vl.split(",");
								ovm = jQuery.map(ovm,function(n){return jQuery.trim(n)});
							} else {
								ovm[0] = vl;
							}
							jQuery(elem).attr(options);
							setTimeout(function(){
								jQuery("option",elem).each(function(i){
									if(i==0) this.selected = "";
									if(jQuery.inArray(jQuery(this).text(),ovm) > -1 || jQuery.inArray(jQuery(this).val(),ovm)>-1) {
										this.selected= "selected";
										if(!msl) return false;
									}
								});
							},0);
						}
					}
				},ajaxso || {}));
			} else if(options.value) {
				var i;
				if(msl) {
					ovm = vl.split(",");
					ovm = jQuery.map(ovm,function(n){return jQuery.trim(n)});
					if(typeof options.size === 'undefined') {options.size = 3;}
				} else {
					options.size = 1;
				}
				if(typeof options.value === 'function') options.value = options.value();
				if(typeof options.value === 'string') {
					var so = options.value.split(";"),sv, ov;
					for(i=0; i<so.length;i++){
						sv = so[i].split(":");
						if(sv.length > 2 ) {
							sv[1] = jQuery.map(sv,function(n,i){if(i>0)return n;}).join(":");
						}
						ov = document.createElement("option");
						ov.value = sv[0]; ov.innerHTML = sv[1];
						if (!msl &&  (sv[0] == vl || sv[1]==vl)) ov.selected ="selected";
						if (msl && (jQuery.inArray(sv[1], ovm)>-1 || jQuery.inArray(sv[0], ovm)>-1)) {ov.selected ="selected";}
						elem.appendChild(ov);
					}
				} else if (typeof options.value === 'object') {
					var oSv = options.value;
					for ( var key in oSv) {
						ov = document.createElement("option");
						ov.value = key; ov.innerHTML = oSv[key];
						if (!msl &&  (key == vl ||oSv[key]==vl) ) ov.selected ="selected";
						if (msl && (jQuery.inArray(oSv[key],ovm)>-1 || jQuery.inArray(key,ovm)>-1)) ov.selected ="selected";
						elem.appendChild(ov);
					}
				}
				options = bindEv(elem,options);
				try {delete options['value'];} catch (e){}
				jQuery(elem).attr(options);
			}
			break;
		case "text" :
		case "password" :
		case "button" :
			elem = document.createElement("input");
			elem.type = eltype;
			elem.value = vl;
			options = bindEv(elem,options);
			if(eltype != "button"){
				if(autowidth) {
					if(!options.size) jQuery(elem).css({width:"98%"});
				} else if (!options.size) options.size = 20;
			}
			jQuery(elem).attr(options);
			break;
		case "image" :
		case "file" :
			elem = document.createElement("input");
			elem.type = eltype;
			options = bindEv(elem,options);
			jQuery(elem).attr(options);
			break;
		case "custom" :
			elem = document.createElement("span");
			try {
				if(jQuery.isFunction(options.custom_element)) {
					var celm = options.custom_element.call(this,vl,options);
					if(celm) {
						celm = jQuery(celm).addClass("customelement").attr({id:options.id,name:options.name});
						jQuery(elem).empty().append(celm);
					}
					else throw "e2";
				} else 	throw "e1";
			} catch (e) {
				if (e=="e1") info_dialog(jQuery.jgrid.errors.errcap,"function 'custom_element' "+jQuery.jgrid.edit.msg.nodefined, jQuery.jgrid.edit.bClose);
				if (e=="e2") info_dialog(jQuery.jgrid.errors.errcap,"function 'custom_element' "+jQuery.jgrid.edit.msg.novalue,jQuery.jgrid.edit.bClose);
				else info_dialog(jQuery.jgrid.errors.errcap,e.message,jQuery.jgrid.edit.bClose);
			}
			break;
	}
	return elem;
}
function checkValues(val, valref,g) {
	var edtrul,i, nm;
	if(typeof(valref)=='string'){
		for( i =0, len=g.p.colModel.length;i<len; i++){
			if(g.p.colModel[i].name==valref) {
				edtrul = g.p.colModel[i].editrules;
				valref = i;
				try { nm = g.p.colModel[i].formoptions.label; } catch (e) {}
				break;
			}
		}
	} else if(valref >=0) {
		edtrul = g.p.colModel[valref].editrules;
	}
	if(edtrul) {
		if(!nm) nm = g.p.colNames[valref];
		if(edtrul.required === true) {
			if( val.match(/^s+$/) || val == "" )  return [false,nm+": "+jQuery.jgrid.edit.msg.required,""];
		}
		// force required
		var rqfield = edtrul.required === false ? false : true;
		if(edtrul.number === true) {
			if( !(rqfield === false && isEmpty(val)) ) {
				if(isNaN(val)) return [false,nm+": "+jQuery.jgrid.edit.msg.number,""];
			}
		}
		if(typeof edtrul.minValue != 'undefined' && !isNaN(edtrul.minValue)) {
			if (parseFloat(val) < parseFloat(edtrul.minValue) ) return [false,nm+": "+jQuery.jgrid.edit.msg.minValue+" "+edtrul.minValue,""];
		}
		if(typeof edtrul.maxValue != 'undefined' && !isNaN(edtrul.maxValue)) {
			if (parseFloat(val) > parseFloat(edtrul.maxValue) ) return [false,nm+": "+jQuery.jgrid.edit.msg.maxValue+" "+edtrul.maxValue,""];
		}
		var filter;
		if(edtrul.email === true) {
			if( !(rqfield === false && isEmpty(val)) ) {
			// taken from jquery Validate plugin
				filter = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i;
				if(!filter.test(val)) {return [false,nm+": "+jQuery.jgrid.edit.msg.email,""];}
			}
		}
		if(edtrul.integer === true) {
			if( !(rqfield === false && isEmpty(val)) ) {
				if(isNaN(val)) return [false,nm+": "+jQuery.jgrid.edit.msg.integer,""];
				if ((val % 1 != 0) || (val.indexOf('.') != -1)) return [false,nm+": "+jQuery.jgrid.edit.msg.integer,""];
			}
		}
		if(edtrul.date === true) {
			if( !(rqfield === false && isEmpty(val)) ) {
				var dft = g.p.colModel[valref].datefmt || "Y-m-d";
				if(!checkDate (dft, val)) return [false,nm+": "+jQuery.jgrid.edit.msg.date+" - "+dft,""];
			}
		}
		if(edtrul.time === true) {
			if( !(rqfield === false && isEmpty(val)) ) {
				if(!checkTime (val)) return [false,nm+": "+jQuery.jgrid.edit.msg.date+" - hh:mm (am/pm)",""];
			}
		}
        if(edtrul.url === true) {
            if( !(rqfield === false && isEmpty(val)) ) {
                filter = /^(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
                if(!filter.test(val)) {return [false,nm+": "+jQuery.jgrid.edit.msg.url,""];}
            }
        }
		if(edtrul.custom === true) {
            if( !(rqfield === false && isEmpty(val)) ) {
				if(jQuery.isFunction(edtrul.custom_func)) {
					var ret = edtrul.custom_func.call(g,val,nm);
					if(jQuery.isArray(ret)) {
						return ret;
					} else {
						return [false,jQuery.jgrid.edit.msg.customarray,""];
					}
				} else {
					return [false,jQuery.jgrid.edit.msg.customfcheck,""];
				}
			}
		}
	}
	return [true,"",""];
}
// Date Validation Javascript
function checkDate (format, date) {
	var tsp = {}, sep;
	format = format.toLowerCase();
	//we search for /,-,. for the date separator
	if(format.indexOf("/") != -1) {
		sep = "/";
	} else if(format.indexOf("-") != -1) {
		sep = "-";
	} else if(format.indexOf(".") != -1) {
		sep = ".";
	} else {
		sep = "/";
	}
	format = format.split(sep);
	date = date.split(sep);
	if (date.length != 3) return false;
	var j=-1,yln, dln=-1, mln=-1;
	for(var i=0;i<format.length;i++){
		var dv =  isNaN(date[i]) ? 0 : parseInt(date[i],10); 
		tsp[format[i]] = dv;
		yln = format[i];
		if(yln.indexOf("y") != -1) { j=i; }
		if(yln.indexOf("m") != -1) {mln=i}
		if(yln.indexOf("d") != -1) {dln=i}
	}
	if (format[j] == "y" || format[j] == "yyyy") {
		yln=4;
	} else if(format[j] =="yy"){
		yln = 2;
	} else {
		yln = -1;
	}
	var daysInMonth = DaysArray(12);
	var strDate;
	if (j === -1) {
		return false;
	} else {
		strDate = tsp[format[j]].toString();
		if(yln == 2 && strDate.length == 1) {yln = 1;}
		if (strDate.length != yln || (tsp[format[j]]==0 && date[j]!="00")){
			return false;
		}
	}
	if(mln === -1) {
		return false;
	} else {
		strDate = tsp[format[mln]].toString();
		if (strDate.length<1 || tsp[format[mln]]<1 || tsp[format[mln]]>12){
			return false;
		}
	}
	if(dln === -1) {
		return false;
	} else {
		strDate = tsp[format[dln]].toString();
		if (strDate.length<1 || tsp[format[dln]]<1 || tsp[format[dln]]>31 || (tsp[format[mln]]==2 && tsp[format[dln]]>daysInFebruary(tsp[format[j]])) || tsp[format[dln]] > daysInMonth[tsp[format[mln]]]){
			return false;
		}
	}
	return true;
}
function daysInFebruary (year){
	// February has 29 days in any year evenly divisible by four,
    // EXCEPT for centurial years which are not also divisible by 400.
    return (((year % 4 == 0) && ( (!(year % 100 == 0)) || (year % 400 == 0))) ? 29 : 28 );
}
function DaysArray(n) {
	for (var i = 1; i <= n; i++) {
		this[i] = 31;
		if (i==4 || i==6 || i==9 || i==11) {this[i] = 30;}
		if (i==2) {this[i] = 29;}
	} 
	return this;
}

function isEmpty(val) {
    val = "" + val;
    if (val.match(/^s+$/) || val == "") {
        return true;
    } else {
        return false;
    }
}
function checkTime(time){
	// checks only hh:ss (and optional am/pm)
	var re = /^(\d{1,2}):(\d{2})([ap]m)?$/,regs;
	if(!isEmpty(time))
	{
		regs = time.match(re);
		if(regs) {
			if(regs[3]) {
				if(regs[1] < 1 || regs[1] > 12) 
					return false;
			} else {
				if(regs[1] > 23) 
					return false;
			}
			if(regs[2] > 59) {
				return false;
			}
		} else {
			return false;
		}
	}
	return true;
};;(function($){
/**
 * jqGrid extension for form editing Grid Data
 * Tony Tomov tony@trirand.com
 * http://trirand.com/blog/ 
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
**/ 
var rp_ge = null;
$.jgrid.extend({
	searchGrid : function (p) {
		p = $.extend({
			recreateFilter: false,
			drag: true,
			sField:'searchField',
			sValue:'searchString',
			sOper: 'searchOper',
			sFilter: 'filters',
			beforeShowSearch: null,
			afterShowSearch : null,
			onInitializeSearch: null,
			closeAfterSearch : false,
			closeOnEscape : false,
			multipleSearch : false,
			// translation
			// if you want to change or remove the order change it in sopt
			// ['bw','eq','ne','lt','le','gt','ge','ew','cn']
			sopt: null,
			onClose : null
			// these are common options
		}, $.jgrid.search, p || {});
		return this.each(function() {
			var $t = this;
			if(!$t.grid) {return;}
			if($.fn.searchFilter) {
				var fid = "fbox_"+$t.p.id;
				if(p.recreateFilter===true) {$("#"+fid).remove();}
				if( $("#"+fid).html() != null ) {
					if ( $.isFunction(p.beforeShowSearch) ) { p.beforeShowSearch($("#"+fid)); };
					showFilter();
					if( $.isFunction(p.afterShowSearch) ) { p.afterShowSearch($("#"+fid)); }
				} else {
					var fields = [],
					colNames = $("#"+$t.p.id).jqGrid("getGridParam","colNames"),
					colModel = $("#"+$t.p.id).jqGrid("getGridParam","colModel"),
					stempl = ['eq','ne','lt','le','gt','ge','bw','bn','in','ni','ew','en','cn','nc'],
					j,pos,k,oprtr;
					oprtr = jQuery.fn.searchFilter.defaults.operators;
					if (p.sopt !=null) {
						oprtr = [];
						k=0;
						for(j=0;j<p.sopt.length;j++) {
							if( (pos= $.inArray(p.sopt[j],stempl)) != -1 ){
								oprtr[k] = {op:p.sopt[j],text: p.odata[pos]};
								k++;
							}
						}
					}
					var searchable;
				    $.each(colModel, function(i, v) {
				        searchable = (typeof v.search === 'undefined') ?  true: v.search ,
				        hidden = (v.hidden === true),
						soptions = $.extend({}, {text: colNames[i], itemval: v.index || v.name}, this.searchoptions),
						ignoreHiding = (soptions.searchhidden === true);
						if(typeof soptions.sopt == 'undefined') soptions.sopt = p.sopt ||  stempl;
						k=0;
						soptions.ops =[];
						if(soptions.sopt.length>0) {
							for(j=0;j<soptions.sopt.length;j++) {
								if( (pos= $.inArray(soptions.sopt[j],stempl)) != -1 ){
									soptions.ops[k] = {op:soptions.sopt[j],text: p.odata[pos]};
									k++;
								}
							}
						}
						if(typeof(this.stype) === 'undefined') this.stype='text';
						if(this.stype == 'select') {
							if ( soptions.dataUrl != null) {}
							else {
								var eov;
								if(soptions.value)
									eov = soptions.value;
								else if(this.editoptions)
									eov = this.editoptions.value;
								if(eov) {
									soptions.dataValues =[];
									if(typeof(eov) === 'string') {
										var so = eov.split(";"),sv;
										for(j=0;j<so.length;j++) {
											sv = so[j].split(":");
											soptions.dataValues[j] ={value:sv[0],text:sv[1]};
										}
									} else if (typeof(eov) === 'object') {
										j=0;
										for (var key in eov) {
											soptions.dataValues[j] ={value:key,text:eov[key]};
											j++;
										}
									}
								}
							}
						}
				        if ((ignoreHiding && searchable) || (searchable && !hidden)) {
							fields.push(soptions);
						}
					});
					if(fields.length>0){
						$("<div id='"+fid+"' role='dialog' tabindex='-1'></div>").insertBefore("#gview_"+$t.p.id);
						$("#"+fid).searchFilter(fields, { groupOps: p.groupOps, operators: oprtr, onClose:hideFilter, resetText: p.Reset, searchText: p.Find, windowTitle: p.caption,  rulesText:p.rulesText, matchText:p.matchText, onSearch: searchFilters, onReset: resetFilters,stringResult:p.multipleSearch, ajaxSelectOptions: $.extend({},$.jgrid.ajaxOptions,$t.p.ajaxSelectOptions ||{}) });
						$(".ui-widget-overlay","#"+fid).remove();
						if($t.p.direction=="rtl") $(".ui-closer","#"+fid).css("float","left");
						if (p.drag===true) {
							$("#"+fid+" table thead tr:first td:first").css('cursor','move');
							if(jQuery.fn.jqDrag) {
								$("#"+fid).jqDrag($("#"+fid+" table thead tr:first td:first"));
							} else {
								try {
									$("#"+fid).draggable({handle: $("#"+fid+" table thead tr:first td:first")});
								} catch (e) {}
							}
						}
						if(p.multipleSearch === false) {
							$(".ui-del, .ui-add, .ui-del, .ui-add-last, .matchText, .rulesText", "#"+fid).hide();
							$("select[name='groupOp']","#"+fid).hide();
						}
						if ( $.isFunction(p.onInitializeSearch) ) { p.onInitializeSearch( $("#"+fid) ); };
						if ( $.isFunction(p.beforeShowSearch) ) { p.beforeShowSearch($("#"+fid)); };
						showFilter();
						if( $.isFunction(p.afterShowSearch) ) { p.afterShowSearch($("#"+fid)); }
						if(p.closeOnEscape===true){
							$("#"+fid).keydown( function( e ) {
								if( e.which == 27 ) {
									hideFilter($("#"+fid));
								}
							});
						}
					}
				}
			}
			function searchFilters(filters) {
				var hasFilters = (filters !== undefined),
				grid = $("#"+$t.p.id), sdata={};
				if(p.multipleSearch===false) {
					sdata[p.sField] = filters.rules[0].field;
					sdata[p.sValue] = filters.rules[0].data;
					sdata[p.sOper] = filters.rules[0].op;
				} else {
					sdata[p.sFilter] = filters;
				}
				grid[0].p.search = hasFilters;
				$.extend(grid[0].p.postData,sdata);
				grid.trigger("reloadGrid",[{page:1}]);
				if(p.closeAfterSearch) hideFilter($("#"+fid));
			}
			function resetFilters(filters) {
				var hasFilters = (filters !== undefined),
				grid = $("#"+$t.p.id), sdata=[];
				grid[0].p.search = hasFilters;
				if(p.multipleSearch===false) {
					sdata[p.sField] = sdata[p.sValue] = sdata[p.sOper] = "";
				} else {
					sdata[p.sFilter] = "";
				}
				$.extend(grid[0].p.postData,sdata);
				grid.trigger("reloadGrid",[{page:1}]);
			}
			function hideFilter(selector) {
				if(p.onClose){
					var fclm = p.onClose(selector);
					if(typeof fclm == 'boolean' && !fclm) return;
				}
				selector.hide();
				$(".jqgrid-overlay:first","#gbox_"+$t.p.id).hide();
			}
			function showFilter(){
				var fl = $(".ui-searchFilter").length;
				if(fl > 1) {
					var zI = $("#"+fid).css("zIndex");
					$("#"+fid).css({zIndex:parseInt(zI)+fl});
				}
				$("#"+fid).show();
				$(".jqgrid-overlay:first","#gbox_"+$t.p.id).show();
				try{$(':input:visible',"#"+fid)[0].focus();}catch(_){}
			}
		});
	},
	editGridRow : function(rowid, p){
		p = $.extend({
			top : 0,
			left: 0,
			width: 300,
			height: 'auto',
			dataheight: 'auto',
			modal: false,
			drag: true,
			resize: true,
			url: null,
			mtype : "POST",
			clearAfterAdd :true,
			closeAfterEdit : false,
			reloadAfterSubmit : true,
			onInitializeForm: null,
			beforeInitData: null,
			beforeShowForm: null,
			afterShowForm: null,
			beforeSubmit: null,
			afterSubmit: null,
			onclickSubmit: null,
			afterComplete: null,
			onclickPgButtons : null,
			afterclickPgButtons: null,
			editData : {},
			recreateForm : false,
			jqModal : true,
			closeOnEscape : false,
			addedrow : "first",
			topinfo : '',
			bottominfo: '',
			saveicon : [],
			closeicon : [],
			savekey: [false,13],
			navkeys: [false,38,40],
			checkOnSubmit : false,
			checkOnUpdate : false,
			_savedData : {},
			processing : false,
			onClose : null,
			ajaxEditOptions : {},
			serializeEditData : null,
			viewPagerButtons : true
		}, $.jgrid.edit, p || {});
		rp_ge = p;
		return this.each(function(){
			var $t = this;
			if (!$t.grid || !rowid) { return; }
			var gID = $t.p.id,
			frmgr = "FrmGrid_"+gID,frmtb = "TblGrid_"+gID,
			IDs = {themodal:'editmod'+gID,modalhead:'edithd'+gID,modalcontent:'editcnt'+gID, scrollelm : frmgr},
			onBeforeShow = $.isFunction(rp_ge.beforeShowForm) ? rp_ge.beforeShowForm : false,
			onAfterShow = $.isFunction(rp_ge.afterShowForm) ? rp_ge.afterShowForm : false,
			onBeforeInit = $.isFunction(rp_ge.beforeInitData) ? rp_ge.beforeInitData : false,
			onInitializeForm = $.isFunction(rp_ge.onInitializeForm) ? rp_ge.onInitializeForm : false,
			copydata = null,
			maxCols = 1, maxRows=0,	gurl, postdata, ret, extpost, newData, diff;
			if (rowid=="new") {
				rowid = "_empty";
				p.caption=p.addCaption;
			} else {
				p.caption=p.editCaption;
			};
			if(p.recreateForm===true && $("#"+IDs.themodal).html() != null) {
				$("#"+IDs.themodal).remove();
			}
			var closeovrl = true;
			if(p.checkOnUpdate && p.jqModal && !p.modal) {
				closeovrl = false;
			}
			if ( $("#"+IDs.themodal).html() != null ) {
				$(".ui-jqdialog-title","#"+IDs.modalhead).html(p.caption);
				$("#FormError","#"+frmtb).hide();
				if(rp_ge.topinfo) {
					$(".topinfo","#"+frmtb+"_2").html(rp_ge.topinfo);
					$(".tinfo","#"+frmtb+"_2").show();
				}
					else $(".tinfo","#"+frmtb+"_2").hide();
				if(rp_ge.bottominfo) {
					$(".bottominfo","#"+frmtb+"_2").html(rp_ge.bottominfo);
					$(".binfo","#"+frmtb+"_2").show();
				}
				else $(".binfo","#"+frmtb+"_2").hide();

				if(onBeforeInit) { onBeforeInit($("#"+frmgr)); }
				// filldata
				fillData(rowid,$t,frmgr);
				///
				if(rowid=="_empty" || !rp_ge.viewPagerButtons) {
					$("#pData, #nData","#"+frmtb+"_2").hide();
				} else { 
					$("#pData, #nData","#"+frmtb+"_2").show();
				}
				if(rp_ge.processing===true) {
					rp_ge.processing=false;
					$("#sData", "#"+frmtb+"_2").removeClass('ui-state-active');
				}
				if($("#"+frmgr).data("disabled")===true) {
					$(".confirm","#"+IDs.themodal).hide();
					$("#"+frmgr).data("disabled",false);
				}
				if(onBeforeShow) { onBeforeShow($("#"+frmgr)); }
				$("#"+IDs.themodal).data("onClose",rp_ge.onClose);
				viewModal("#"+IDs.themodal,{gbox:"#gbox_"+gID,jqm:p.jqModal, jqM: false, closeoverlay: closeovrl, modal:p.modal});
				if(!closeovrl) {
					$(".jqmOverlay").click(function(){
						if(!checkUpdates()) return false;
						hideModal("#"+IDs.themodal,{gb:"#gbox_"+gID,jqm:p.jqModal, onClose: rp_ge.onClose});
						return false;
					});
				}
				if(onAfterShow) { onAfterShow($("#"+frmgr)); }
			} else {
				$($t.p.colModel).each( function(i) {
					var fmto = this.formoptions;
					maxCols = Math.max(maxCols, fmto ? fmto.colpos || 0 : 0 );
					maxRows = Math.max(maxRows, fmto ? fmto.rowpos || 0 : 0 );
				});
				var dh = isNaN(p.dataheight) ? p.dataheight : p.dataheight+"px";
				var flr, frm = $("<form name='FormPost' id='"+frmgr+"' class='FormGrid' style='width:100%;overflow:auto;position:relative;height:"+dh+";'></form>").data("disabled",false),
				tbl =$("<table id='"+frmtb+"' class='EditTable' cellspacing='0' cellpading='0' border='0'><tbody></tbody></table>");
				$(frm).append(tbl);
				flr = $("<tr id='FormError' style='display:none'><td class='ui-state-error' colspan='"+(maxCols*2)+"'></td></tr>");
				flr[0].rp = 0;
				$(tbl).append(flr);
				//topinfo
				flr = $("<tr style='display:none' class='tinfo'><td class='topinfo' colspan='"+(maxCols*2)+"'>"+rp_ge.topinfo+"</td></tr>");
				flr[0].rp = 0;
				$(tbl).append(flr);
				// set the id.
				// use carefull only to change here colproperties.
				if(onBeforeInit) { onBeforeInit($("#"+frmgr)); }
				// create data
				var rtlb = $t.p.direction == "rtl" ? true :false,
				bp = rtlb ? "nData" : "pData",
				bn = rtlb ? "pData" : "nData",
				valref = createData(rowid,$t,tbl,maxCols),
				// buttons at footer
				bP = "<a href='javascript:void(0)' id='"+bp+"' class='fm-button ui-state-default ui-corner-left'><span class='ui-icon ui-icon-triangle-1-w'></span></div>",
				bN = "<a href='javascript:void(0)' id='"+bn+"' class='fm-button ui-state-default ui-corner-right'><span class='ui-icon ui-icon-triangle-1-e'></span></div>",
				bS  ="<a href='javascript:void(0)' id='sData' class='fm-button ui-state-default ui-corner-all'>"+p.bSubmit+"</a>",
				bC  ="<a href='javascript:void(0)' id='cData' class='fm-button ui-state-default ui-corner-all'>"+p.bCancel+"</a>";
				var bt = "<table border='0' class='EditTable' id='"+frmtb+"_2'><tbody><tr id='Act_Buttons'><td class='navButton ui-widget-content'>"+(rtlb ? bN+bP : bP+bN)+"</td><td class='EditButton ui-widget-content'>"+bS+bC+"</td></tr>";
				bt += "<tr style='display:none' class='binfo'><td class='bottominfo' colspan='2'>"+rp_ge.bottominfo+"</td></tr>";
				bt += "</tbody></table>";
				if(maxRows >  0) {
					var sd=[];
					$.each($(tbl)[0].rows,function(i,r){
						sd[i] = r;
					});
					sd.sort(function(a,b){
						if(a.rp > b.rp) {return 1;}
						if(a.rp < b.rp) {return -1;}
						return 0;
					});
					$.each(sd, function(index, row) {
						$('tbody',tbl).append(row);
					});
				}
				p.gbox = "#gbox_"+gID;
				var cle = false;
				if(p.closeOnEscape===true){
					p.closeOnEscape = false;
					cle = true;
				}
				var tms = $("<span></span>").append(frm).append(bt);
				createModal(IDs,tms,p,"#gview_"+$t.p.id,$("#gview_"+$t.p.id)[0]);
				if(rtlb) {
					$("#pData, #nData","#"+frmtb+"_2").css("float","right");
					$(".EditButton","#"+frmtb+"_2").css("text-align","left");
				}
				if(rp_ge.topinfo) $(".tinfo","#"+frmtb+"_2").show();
				if(rp_ge.bottominfo) $(".binfo","#"+frmtb+"_2").show();
				tms = null; bt=null;
				$("#"+IDs.themodal).keydown( function( e ) {
					var wkey = e.target;
					if ($("#"+frmgr).data("disabled")===true ) return false; //??
					if(rp_ge.savekey[0] === true && e.which == rp_ge.savekey[1]) { // save
						if(wkey.tagName != "TEXTAREA") {
							$("#sData", "#"+frmtb+"_2").trigger("click");
							return false;
						}
					}
					if(e.which === 27) {
						if(!checkUpdates()) return false;
						if(cle)	hideModal(this,{gb:p.gbox,jqm:p.jqModal, onClose: rp_ge.onClose});
						return false;
					}
					if(rp_ge.navkeys[0]===true) {
						if($("#id_g","#"+frmtb).val() == "_empty") return true;
						if(e.which == rp_ge.navkeys[1]){ //up
							$("#pData", "#"+frmtb+"_2").trigger("click");
							return false;
						}
						if(e.which == rp_ge.navkeys[2]){ //down
							$("#nData", "#"+frmtb+"_2").trigger("click");
							return false;
						}
					}
				});
				if(p.checkOnUpdate) {
					$("a.ui-jqdialog-titlebar-close span","#"+IDs.themodal).removeClass("jqmClose");
					$("a.ui-jqdialog-titlebar-close","#"+IDs.themodal).unbind("click")
					.click(function(){
						if(!checkUpdates()) return false;
						hideModal("#"+IDs.themodal,{gb:"#gbox_"+gID,jqm:p.jqModal,onClose: rp_ge.onClose});
						return false;
					});
				}
				p.saveicon = $.extend([true,"left","ui-icon-disk"],p.saveicon);
				p.closeicon = $.extend([true,"left","ui-icon-close"],p.closeicon);
				// beforeinitdata after creation of the form
				if(p.saveicon[0]==true) {
					$("#sData","#"+frmtb+"_2").addClass(p.saveicon[1] == "right" ? 'fm-button-icon-right' : 'fm-button-icon-left')
					.append("<span class='ui-icon "+p.saveicon[2]+"'></span>");
				}
				if(p.closeicon[0]==true) {
					$("#cData","#"+frmtb+"_2").addClass(p.closeicon[1] == "right" ? 'fm-button-icon-right' : 'fm-button-icon-left')
					.append("<span class='ui-icon "+p.closeicon[2]+"'></span>");
				}
				if(rp_ge.checkOnSubmit || rp_ge.checkOnUpdate) {
					bS  ="<a href='javascript:void(0)' id='sNew' class='fm-button ui-state-default ui-corner-all' style='z-index:1002'>"+p.bYes+"</a>";
					bN  ="<a href='javascript:void(0)' id='nNew' class='fm-button ui-state-default ui-corner-all' style='z-index:1002'>"+p.bNo+"</a>";
					bC  ="<a href='javascript:void(0)' id='cNew' class='fm-button ui-state-default ui-corner-all' style='z-index:1002'>"+p.bExit+"</a>";
					var ii, zI = p.zIndex  || 999; zI ++;
					if ($.browser.msie && $.browser.version ==6) {
						ii = '<iframe style="display:block;position:absolute;z-index:-1;filter:Alpha(Opacity=\'0\');" src="javascript:false;"></iframe>';
					} else { ii="";}
					$("<div class='ui-widget-overlay jqgrid-overlay confirm' style='z-index:"+zI+";display:none;'>&#160;"+ii+"</div><div class='confirm ui-widget-content ui-jqconfirm' style='z-index:"+(zI+1)+"'>"+p.saveData+"<br/><br/>"+bS+bN+bC+"</div>").insertAfter("#"+frmgr);
					$("#sNew","#"+IDs.themodal).click(function(){
						postIt();
						$("#"+frmgr).data("disabled",false);
						$(".confirm","#"+IDs.themodal).hide();
						return false;
					});
					$("#nNew","#"+IDs.themodal).click(function(){
						$(".confirm","#"+IDs.themodal).hide();
						$("#"+frmgr).data("disabled",false);
						setTimeout(function(){$(":input","#"+frmgr)[0].focus();},0);
						return false;
					});
					$("#cNew","#"+IDs.themodal).click(function(){
						$(".confirm","#"+IDs.themodal).hide();
						$("#"+frmgr).data("disabled",false);
						hideModal("#"+IDs.themodal,{gb:"#gbox_"+gID,jqm:p.jqModal,onClose: rp_ge.onClose});
						return false;
					});
				}
				// here initform - only once
				if(onInitializeForm) { onInitializeForm($("#"+frmgr)); }
				if(rowid=="_empty" || !rp_ge.viewPagerButtons) { $("#pData,#nData","#"+frmtb+"_2").hide(); } else { $("#pData,#nData","#"+frmtb+"_2").show(); }
				if(onBeforeShow) { onBeforeShow($("#"+frmgr)); }
				$("#"+IDs.themodal).data("onClose",rp_ge.onClose);
				viewModal("#"+IDs.themodal,{gbox:"#gbox_"+gID,jqm:p.jqModal,closeoverlay:closeovrl,modal:p.modal});
				if(!closeovrl) {
					$(".jqmOverlay").click(function(){
						if(!checkUpdates()) return false;
						hideModal("#"+IDs.themodal,{gb:"#gbox_"+gID,jqm:p.jqModal, onClose: rp_ge.onClose});
						return false;
					});
				}
				if(onAfterShow) { onAfterShow($("#"+frmgr)); }
				$(".fm-button","#"+IDs.themodal).hover(
				   function(){$(this).addClass('ui-state-hover');}, 
				   function(){$(this).removeClass('ui-state-hover');}
				);
				$("#sData", "#"+frmtb+"_2").click(function(e){
					postdata = {}; extpost={};
					$("#FormError","#"+frmtb).hide();
					// all depend on ret array
					//ret[0] - succes
					//ret[1] - msg if not succes
					//ret[2] - the id  that will be set if reload after submit false
					getFormData();
					if(postdata[$t.p.id+"_id"] == "_empty")	postIt();
					else if(p.checkOnSubmit===true ) {
						newData = $.extend({},postdata,extpost);
						diff = compareData(newData,rp_ge._savedData);
						if(diff) {
							$("#"+frmgr).data("disabled",true);
							$(".confirm","#"+IDs.themodal).show();
						} else {
							postIt();
						}
					} else {
						postIt();
					}
					return false;
				});
				$("#cData", "#"+frmtb+"_2").click(function(e){
					if(!checkUpdates()) return false;
					hideModal("#"+IDs.themodal,{gb:"#gbox_"+gID,jqm:p.jqModal,onClose: rp_ge.onClose});
					return false;
				});
				$("#nData", "#"+frmtb+"_2").click(function(e){
					if(!checkUpdates()) return false;
					$("#FormError","#"+frmtb).hide();
					var npos = getCurrPos();
					npos[0] = parseInt(npos[0]);
					if(npos[0] != -1 && npos[1][npos[0]+1]) {
						if($.isFunction(p.onclickPgButtons)) {
							p.onclickPgButtons('next',$("#"+frmgr),npos[1][npos[0]]);
						}
						fillData(npos[1][npos[0]+1],$t,frmgr);
						$($t).jqGrid("setSelection",npos[1][npos[0]+1]);
						if($.isFunction(p.afterclickPgButtons)) {
							p.afterclickPgButtons('next',$("#"+frmgr),npos[1][npos[0]+1]);
						}
						updateNav(npos[0]+1,npos[1].length-1);
					};
					return false;
				});
				$("#pData", "#"+frmtb+"_2").click(function(e){
					if(!checkUpdates()) return false;
					$("#FormError","#"+frmtb).hide();
					var ppos = getCurrPos();
					if(ppos[0] != -1 && ppos[1][ppos[0]-1]) {
						if($.isFunction(p.onclickPgButtons)) {
							p.onclickPgButtons('prev',$("#"+frmgr),ppos[1][ppos[0]]);
						}
						fillData(ppos[1][ppos[0]-1],$t,frmgr);
						$($t).jqGrid("setSelection",ppos[1][ppos[0]-1]);
						if($.isFunction(p.afterclickPgButtons)) {
							p.afterclickPgButtons('prev',$("#"+frmgr),ppos[1][ppos[0]-1]);
						}
						updateNav(ppos[0]-1,ppos[1].length-1);
					};
					return false;
				});
			}
			var posInit =getCurrPos();
			updateNav(posInit[0],posInit[1].length-1);
			function updateNav(cr,totr,rid){
				if (cr==0) { $("#pData","#"+frmtb+"_2").addClass('ui-state-disabled'); } else { $("#pData","#"+frmtb+"_2").removeClass('ui-state-disabled'); }
				if (cr==totr) { $("#nData","#"+frmtb+"_2").addClass('ui-state-disabled'); } else { $("#nData","#"+frmtb+"_2").removeClass('ui-state-disabled'); }
			}
			function getCurrPos() {
				var rowsInGrid = $($t).jqGrid("getDataIDs"),
				selrow = $("#id_g","#"+frmtb).val(),
				pos = $.inArray(selrow,rowsInGrid);
				return [pos,rowsInGrid];
			}
			function checkUpdates () {
				var stat = true;
				$("#FormError","#"+frmtb).hide();
				if(rp_ge.checkOnUpdate) {
					postdata = {}; extpost={};
					getFormData();
					newData = $.extend({},postdata,extpost);
					diff = compareData(newData,rp_ge._savedData);
					if(diff) {
						$("#"+frmgr).data("disabled",true);
						$(".confirm","#"+IDs.themodal).show();
						stat = false;
					}
				}
				return stat;
			}
			function getFormData(){
				$(".FormElement", "#"+frmtb).each(function(i) {
					var celm = $(".customelement", this);
					if (celm.length) {
						var  elem = celm[0], nm = elem.name;
						$.each($t.p.colModel, function(i,n){
							if(this.name == nm && this.editoptions && $.isFunction(this.editoptions.custom_value)) {
								try {
									postdata[nm] = this.editoptions.custom_value($("#"+nm,"#"+frmtb),'get');
									if (postdata[nm] === undefined) throw "e1";
								} catch (e) {
									if (e=="e1") info_dialog(jQuery.jgrid.errors.errcap,"function 'custom_value' "+$.jgrid.edit.msg.novalue,jQuery.jgrid.edit.bClose);
									else info_dialog(jQuery.jgrid.errors.errcap,e.message,jQuery.jgrid.edit.bClose);
								}
								return true;
							}
						});
					} else {
					switch ($(this).get(0).type) {
						case "checkbox":
							if($(this).attr("checked")) {
								postdata[this.name]= $(this).val();
							}else {
								var ofv = $(this).attr("offval");
								postdata[this.name]= ofv;
							}
						break;
						case "select-one":
							postdata[this.name]= $("option:selected",this).val();
							extpost[this.name]= $("option:selected",this).text();
						break;
						case "select-multiple":
							postdata[this.name]= $(this).val();
							if(postdata[this.name]) postdata[this.name] = postdata[this.name].join(",");
							else postdata[this.name] ="";
							var selectedText = [];
							$("option:selected",this).each(
								function(i,selected){
									selectedText[i] = $(selected).text();
								}
							);
							extpost[this.name]= selectedText.join(",");
						break;								
						case "password":
						case "text":
						case "textarea":
						case "button":
							postdata[this.name] = $(this).val();
							
						break;
					}
					if($t.p.autoencode) postdata[this.name] = $.jgrid.htmlEncode(postdata[this.name]);
					}
				});
				return true;
			}
			function createData(rowid,obj,tb,maxcols){
				var nm, hc,trdata, cnt=0,tmp, dc,elc, retpos=[], ind=false, rp,cp,
				tdtmpl = "<td class='CaptionTD ui-widget-content'>&#160;</td><td class='DataTD ui-widget-content' style='white-space:pre'>&#160;</td>", tmpl=""; //*2
				for (var i =1;i<=maxcols;i++) {
					tmpl += tdtmpl;
				}
				if(rowid != '_empty') {
					ind = $(obj).jqGrid("getInd",rowid);
				}
				$(obj.p.colModel).each( function(i) {
					nm = this.name;
					// hidden fields are included in the form
					if(this.editrules && this.editrules.edithidden == true) {
						hc = false;
					} else {
						hc = this.hidden === true ? true : false;
					}
					dc = hc ? "style='display:none'" : "";
					if ( nm !== 'cb' && nm !== 'subgrid' && this.editable===true && nm !== 'rn') {
						if(ind === false) {
							tmp = "";
						} else {
							if(nm == obj.p.ExpandColumn && obj.p.treeGrid === true) {
								tmp = $("td:eq("+i+")",obj.rows[ind]).text();
							} else {
								try {
									tmp =  $.unformat($("td:eq("+i+")",obj.rows[ind]),{rowId:rowid, colModel:this},i);
								} catch (_) {
									tmp = $("td:eq("+i+")",obj.rows[ind]).html();
								}
							}
						}
						var opt = $.extend({}, this.editoptions || {} ,{id:nm,name:nm});
						frmopt = $.extend({}, {elmprefix:'',elmsuffix:'',rowabove:false,rowcontent:''}, this.formoptions || {}),
						rp = parseInt(frmopt.rowpos) || cnt+1,
						cp = parseInt((parseInt(frmopt.colpos) || 1)*2);
						if(rowid == "_empty" && opt.defaultValue ) {
							tmp = $.isFunction(opt.defaultValue) ? opt.defaultValue() : opt.defaultValue; 
						}
						if(!this.edittype) this.edittype = "text";
						if($t.p.autoencode) tmp = $.jgrid.htmlDecode(tmp);
						elc = createEl(this.edittype,opt,tmp,false,$.extend({},$.jgrid.ajaxOptions,obj.p.ajaxSelectOptions || {}));
						if(tmp == "" && this.edittype == "checkbox") {tmp = $(elc).attr("offval");}
						if(rp_ge.checkOnSubmit || rp_ge.checkOnUpdate) rp_ge._savedData[nm] = tmp;
						$(elc).addClass("FormElement");
						trdata = $(tb).find("tr[rowpos="+rp+"]");
						if(frmopt.rowabove) {
							var newdata = $("<tr><td class='contentinfo' colspan='"+(maxcols*2)+"'>"+frmopt.rowcontent+"</td></tr>");
							$(tb).append(newdata);
							newdata[0].rp = rp;
						}
						if ( trdata.length==0 ) {
							trdata = $("<tr "+dc+" rowpos='"+rp+"'></tr>").addClass("FormData").attr("id","tr_"+nm);
							$(trdata).append(tmpl);
							$(tb).append(trdata);
							trdata[0].rp = rp;
						}
						$("td:eq("+(cp-2)+")",trdata[0]).html( typeof frmopt.label === 'undefined' ? obj.p.colNames[i]: frmopt.label);
						$("td:eq("+(cp-1)+")",trdata[0]).append(frmopt.elmprefix).append(elc).append(frmopt.elmsuffix);
						retpos[cnt] = i;
						cnt++;
					};
				});
				if( cnt > 0) {
					var idrow = $("<tr class='FormData' style='display:none'><td class='CaptionTD'></td><td colspan='"+ (maxcols*2-1)+"' class='DataTD'><input class='FormElement' id='id_g' type='text' name='"+obj.p.id+"_id' value='"+rowid+"'/></td></tr>");
					idrow[0].rp = cnt+999;
					$(tb).append(idrow);
					if(rp_ge.checkOnSubmit || rp_ge.checkOnUpdate) rp_ge._savedData.id = rowid;
				}
				return retpos;
			}
			function fillData(rowid,obj,fmid){
				var nm, hc,cnt=0,tmp, fld,opt,vl,vlc;
				if(rp_ge.checkOnSubmit || rp_ge.checkOnUpdate) {rp_ge._savedData = {};rp_ge._savedData.id=rowid;}
				var cm = obj.p.colModel;
				if(rowid == '_empty') {
					$(cm).each(function(i){
						nm = this.name;
						opt = $.extend({}, this.editoptions || {} );
						fld = $("#"+$.jgrid.jqID(nm),"#"+fmid);
						if(fld[0] != null) {
							vl = "";
							if(opt.defaultValue ) {
								vl = $.isFunction(opt.defaultValue) ? opt.defaultValue() : opt.defaultValue;
								if(fld[0].type=='checkbox') {
									vlc = vl.toLowerCase();
									if(vlc.search(/(false|0|no|off|undefined)/i)<0 && vlc!=="") {
										fld[0].checked = true;
										fld[0].defaultChecked = true;
										fld[0].value = vl;
									} else {
										fld.attr({checked:"",defaultChecked:""});
									}
								} else {fld.val(vl); }
							} else {
								if( fld[0].type=='checkbox' ) {
									fld[0].checked = false;
									fld[0].defaultChecked = false;
									vl = $(fld).attr("offval");
								} else if (fld[0].type.substr(0,6)=='select') {
									fld[0].selectedIndex = 0; 
								} else {
									fld.val(vl);
								}
							}
							if(rp_ge.checkOnSubmit===true || rp_ge.checkOnUpdate) rp_ge._savedData[nm] = vl;
						}
					});
					$("#id_g","#"+fmid).val(rowid);
					return;
				}
				var tre = $(obj).jqGrid("getInd",rowid,true);
				if(!tre) return;
				$('td',tre).each( function(i) {
					nm = cm[i].name;
					// hidden fields are included in the form
					if ( nm !== 'cb' && nm !== 'subgrid' && nm !== 'rn' && cm[i].editable===true) {
						if(nm == obj.p.ExpandColumn && obj.p.treeGrid === true) {
							tmp = $(this).text();
						} else {
							try {
								tmp =  $.unformat(this,{rowId:rowid, colModel:cm[i]},i);
							} catch (_) {
								tmp = $(this).html();
							}
						}
						if($t.p.autoencode) tmp = $.jgrid.htmlDecode(tmp);
						if(rp_ge.checkOnSubmit===true || rp_ge.checkOnUpdate) rp_ge._savedData[nm] = tmp;
						nm = $.jgrid.jqID(nm);
						switch (cm[i].edittype) {
							case "password":
							case "text":
							case "button" :
							case "image":
								$("#"+nm,"#"+fmid).val(tmp);
								break;
							case "textarea":
								if(tmp == "&nbsp;" || tmp == "&#160;" || (tmp.length==1 && tmp.charCodeAt(0)==160) ) {tmp='';}
								$("#"+nm,"#"+fmid).val(tmp);
								break;
							case "select":
								var opv = tmp.split(",");
								opv = $.map(opv,function(n){return $.trim(n)});
								$("#"+nm+" option","#"+fmid).each(function(j){
									if (!cm[i].editoptions.multiple && (opv[0] == $(this).text() || opv[0] == $(this).val()) ){
										this.selected= true;
									} else if (cm[i].editoptions.multiple){
										if(  $.inArray($(this).text(), opv ) > -1 || $.inArray($(this).val(), opv ) > -1  ){
											this.selected = true;
										}else{
											this.selected = false;
										}
									} else {
										this.selected = false;
									}
								});
								break;
							case "checkbox":
								tmp = tmp+"";
								tmp = tmp.toLowerCase();
								if(tmp.search(/(false|0|no|off|undefined)/i)<0 && tmp!=="") {
									$("#"+nm,"#"+fmid).attr("checked",true);
									$("#"+nm,"#"+fmid).attr("defaultChecked",true); //ie
								} else {
									$("#"+nm,"#"+fmid).attr("checked",false);
									$("#"+nm,"#"+fmid).attr("defaultChecked",""); //ie
								}
								break;
							case 'custom' :
								try {
									if(cm[i].editoptions && $.isFunction(cm[i].editoptions.custom_value)) {
										var dummy = cm[i].editoptions.custom_value($("#"+nm,"#"+fmid),'set',tmp);
									} else throw "e1";
								} catch (e) {
									if (e=="e1") info_dialog(jQuery.jgrid.errors.errcap,"function 'custom_value' "+$.jgrid.edit.msg.nodefined,jQuery.jgrid.edit.bClose);
									else info_dialog(jQuery.jgrid.errors.errcap,e.message,jQuery.jgrid.edit.bClose);
								}
								break;
						}
						cnt++;
					}
				});
				if(cnt>0) { $("#id_g","#"+frmtb).val(rowid); }
			}
			function postIt() {
				var copydata, ret=[true,"",""], onCS = {}, opers = $t.p.prmNames, idname, oper;
				if($.isFunction(rp_ge.beforeCheckValues)) {
					var retvals = rp_ge.beforeCheckValues(postdata,$("#"+frmgr),postdata[$t.p.id+"_id"] == "_empty" ? opers.addoper : opers.editoper);
					if(retvals && typeof(retvals) === 'object') postdata = retvals;
				}
				for( var key in postdata ){
					ret = checkValues(postdata[key],key,$t);
					if(ret[0] == false) break;
				}
				if(ret[0]) {
					if( $.isFunction( rp_ge.onclickSubmit)) { onCS = rp_ge.onclickSubmit(rp_ge,postdata) || {}; }
					if( $.isFunction(rp_ge.beforeSubmit))  { ret = rp_ge.beforeSubmit(postdata,$("#"+frmgr)); }
				}
				gurl = rp_ge.url ? rp_ge.url : $($t).jqGrid('getGridParam','editurl');
				if(ret[0]) {
					if(!gurl) { ret[0]=false; ret[1] += " "+$.jgrid.errors.nourl; }
				}
				if(ret[0] === false) {
					$("#FormError>td","#"+frmtb).html(ret[1]);
					$("#FormError","#"+frmtb).show();
					return;
				}
				if(!rp_ge.processing) {
					rp_ge.processing = true;
					$("#sData", "#"+frmtb+"_2").addClass('ui-state-active');
					oper = opers.oper;
					idname = opers.id;
					// we add to pos data array the action - the name is oper
					postdata[oper] = ($.trim(postdata[$t.p.id+"_id"]) == "_empty") ? opers.addoper : opers.editoper;
					if(postdata[oper] != opers.addoper) 
						postdata[idname] = postdata[$t.p.id+"_id"];
					else {
						// check to see if we have allredy this field in the form and if yes lieve it
						if( postdata[idname] === undefined ) postdata[idname] = postdata[$t.p.id+"_id"];
					}
					delete postdata[$t.p.id+"_id"];
					postdata = $.extend(postdata,rp_ge.editData,onCS);
					$.ajax( $.extend({
						url:gurl,
						type: rp_ge.mtype,
						data: $.isFunction(rp_ge.serializeEditData) ? rp_ge.serializeEditData(postdata) :  postdata,
						complete:function(data,Status){
							if(Status != "success") {
							    ret[0] = false;
							    if ($.isFunction(rp_ge.errorTextFormat)) {
							        ret[1] = rp_ge.errorTextFormat(data);
							    } else {
							        ret[1] = Status + " Status: '" + data.statusText + "'. Error code: " + data.status;
								}
							} else {
								// data is posted successful
								// execute aftersubmit with the returned data from server
								if( $.isFunction(rp_ge.afterSubmit) ) {
									ret = rp_ge.afterSubmit(data,postdata);
								}
							}
							if(ret[0] === false) {
								$("#FormError>td","#"+frmtb).html(ret[1]);
								$("#FormError","#"+frmtb).show();
							} else {
								// remove some values if formattaer select or checkbox
								$.each($t.p.colModel, function(i,n){
									if(extpost[this.name] && this.formatter && this.formatter=='select') {
										try {delete extpost[this.name];} catch (e) {}
									}
								});
								postdata = $.extend(postdata,extpost);
								if($t.p.autoencode) {
									$.each(postdata,function(n,v){
										postdata[n] = $.jgrid.htmlDecode(v);
									});
								}
								// the action is add
								if(postdata[oper] == opers.addoper ) {
									//id processing
									// user not set the id ret[2]
									if(!ret[2]) { ret[2] = parseInt($t.p.records)+1; }
									postdata[idname] = ret[2];
									if(rp_ge.closeAfterAdd) {
										if(rp_ge.reloadAfterSubmit) { $($t).trigger("reloadGrid"); }
										else {
											$($t).jqGrid("addRowData",ret[2],postdata,p.addedrow);
											$($t).jqGrid("setSelection",ret[2]);
										}
										hideModal("#"+IDs.themodal,{gb:"#gbox_"+gID,jqm:p.jqModal,onClose: rp_ge.onClose});
									} else if (rp_ge.clearAfterAdd) {
										if(rp_ge.reloadAfterSubmit) { $($t).trigger("reloadGrid"); }
										else { $($t).jqGrid("addRowData",ret[2],postdata,p.addedrow); }
										fillData("_empty",$t,frmgr);
									} else {
										if(rp_ge.reloadAfterSubmit) { $($t).trigger("reloadGrid"); }
										else { $($t).jqGrid("addRowData",ret[2],postdata,p.addedrow); }
									}
								} else {
									// the action is update
									if(rp_ge.reloadAfterSubmit) {
										$($t).trigger("reloadGrid");
										if( !rp_ge.closeAfterEdit ) { setTimeout(function(){$($t).jqGrid("setSelection",postdata[idname]);},1000); }
									} else {
										if($t.p.treeGrid === true) {
											$($t).jqGrid("setTreeRow",postdata[idname],postdata);
										} else {
											$($t).jqGrid("setRowData",postdata[idname],postdata);
										}
									}
									if(rp_ge.closeAfterEdit) { hideModal("#"+IDs.themodal,{gb:"#gbox_"+gID,jqm:p.jqModal,onClose: rp_ge.onClose}); }
								}
								if($.isFunction(rp_ge.afterComplete)) {
									copydata = data;
									setTimeout(function(){rp_ge.afterComplete(copydata,postdata,$("#"+frmgr));copydata=null;},500);
								}
							}
							rp_ge.processing=false;
							if(rp_ge.checkOnSubmit || rp_ge.checkOnUpdate) {
								$("#"+frmgr).data("disabled",false);
								if(rp_ge._savedData.id !="_empty") rp_ge._savedData = postdata;
							}
							$("#sData", "#"+frmtb+"_2").removeClass('ui-state-active');
							try{$(':input:visible',"#"+frmgr)[0].focus();} catch (e){}
						},
						error:function(xhr,st,err){
							$("#FormError>td","#"+frmtb).html(st+ " : "+err);
							$("#FormError","#"+frmtb).show();
							rp_ge.processing=false;
							$("#"+frmgr).data("disabled",false);
							$("#sData", "#"+frmtb+"_2").removeClass('ui-state-active');
						}
					}, $.jgrid.ajaxOptions, rp_ge.ajaxEditOptions ));
				}
				
			}
			function compareData(nObj, oObj ) {
				var ret = false,key;
				for (key in nObj) {
					if(nObj[key] != oObj[key]) {
						ret = true;
						break;
					}
				}
				return ret;
			}
		});
	},
	viewGridRow : function(rowid, p){
		p = $.extend({
			top : 0,
			left: 0,
			width: 0,
			height: 'auto',
			dataheight: 'auto',
			modal: false,
			drag: true,
			resize: true,
			jqModal: true,
			closeOnEscape : false,
			labelswidth: '30%',
			closeicon: [],
			navkeys: [false,38,40],
			onClose: null,
			beforeShowForm : null,
			viewPagerButtons : true
		}, $.jgrid.view, p || {});
		return this.each(function(){
			var $t = this;
			if (!$t.grid || !rowid) { return; }
			if(!p.imgpath) { p.imgpath= $t.p.imgpath; }
			// I hate to rewrite code, but ...
			var gID = $t.p.id,
			frmgr = "ViewGrid_"+gID , frmtb = "ViewTbl_"+gID,
			IDs = {themodal:'viewmod'+gID,modalhead:'viewhd'+gID,modalcontent:'viewcnt'+gID, scrollelm : frmgr},
			maxCols = 1, maxRows=0;
			if ( $("#"+IDs.themodal).html() != null ) {
				$(".ui-jqdialog-title","#"+IDs.modalhead).html(p.caption);
				$("#FormError","#"+frmtb).hide();
				fillData(rowid,$t);
				if($.isFunction(p.beforeShowForm)) p.beforeShowForm($("#"+frmgr));
				viewModal("#"+IDs.themodal,{gbox:"#gbox_"+gID,jqm:p.jqModal, jqM: false, modal:p.modal});
				focusaref();
			} else {
				$($t.p.colModel).each( function(i) {
					var fmto = this.formoptions;
					maxCols = Math.max(maxCols, fmto ? fmto.colpos || 0 : 0 );
					maxRows = Math.max(maxRows, fmto ? fmto.rowpos || 0 : 0 );
				});
				var dh = isNaN(p.dataheight) ? p.dataheight : p.dataheight+"px";
				var flr, frm = $("<form name='FormPost' id='"+frmgr+"' class='FormGrid' style='width:100%;overflow:auto;position:relative;height:"+dh+";'></form>"),
				tbl =$("<table id='"+frmtb+"' class='EditTable' cellspacing='1' cellpading='2' border='0' style='table-layout:fixed'><tbody></tbody></table>");
				// set the id.
				$(frm).append(tbl);
				var valref = createData(rowid, $t, tbl, maxCols),
				rtlb = $t.p.direction == "rtl" ? true :false,
				bp = rtlb ? "nData" : "pData",
				bn = rtlb ? "pData" : "nData",

				// buttons at footer
				bP = "<a href='javascript:void(0)' id='"+bp+"' class='fm-button ui-state-default ui-corner-left'><span class='ui-icon ui-icon-triangle-1-w'></span></div>",
				bN = "<a href='javascript:void(0)' id='"+bn+"' class='fm-button ui-state-default ui-corner-right'><span class='ui-icon ui-icon-triangle-1-e'></span></div>",
				bC  ="<a href='javascript:void(0)' id='cData' class='fm-button ui-state-default ui-corner-all'>"+p.bClose+"</a>";
				if(maxRows >  0) {
					var sd=[];
					$.each($(tbl)[0].rows,function(i,r){
						sd[i] = r;
					});
					sd.sort(function(a,b){
						if(a.rp > b.rp) {return 1;}
						if(a.rp < b.rp) {return -1;}
						return 0;
					});
					$.each(sd, function(index, row) {
						$('tbody',tbl).append(row);
					});
				}
				p.gbox = "#gbox_"+gID;
				var cle = false;
				if(p.closeOnEscape===true){
					p.closeOnEscape = false;
					cle = true;
				}				
				var bt = $("<span></span>").append(frm).append("<table border='0' class='EditTable' id='"+frmtb+"_2'><tbody><tr id='Act_Buttons'><td class='navButton ui-widget-content' width='"+p.labelswidth+"'>"+(rtlb ? bN+bP : bP+bN)+"</td><td class='EditButton ui-widget-content'>"+bC+"</td></tr></tbody></table>");
				createModal(IDs,bt,p,"#gview_"+$t.p.id,$("#gview_"+$t.p.id)[0]);
				if(rtlb) {
					$("#pData, #nData","#"+frmtb+"_2").css("float","right");
					$(".EditButton","#"+frmtb+"_2").css("text-align","left");
				}
				if(!p.viewPagerButtons) $("#pData, #nData","#"+frmtb+"_2").hide();
				bt = null;
				$("#"+IDs.themodal).keydown( function( e ) {
					if(e.which === 27) {
						if(cle)	hideModal(this,{gb:p.gbox,jqm:p.jqModal, onClose: p.onClose});
						return false;
					}
					if(p.navkeys[0]===true) {
						if(e.which === p.navkeys[1]){ //up
							$("#pData", "#"+frmtb+"_2").trigger("click");
							return false;
						}
						if(e.which === p.navkeys[2]){ //down
							$("#nData", "#"+frmtb+"_2").trigger("click");
							return false;
						}
					}
				});
				p.closeicon = $.extend([true,"left","ui-icon-close"],p.closeicon);
				if(p.closeicon[0]==true) {
					$("#cData","#"+frmtb+"_2").addClass(p.closeicon[1] == "right" ? 'fm-button-icon-right' : 'fm-button-icon-left')
					.append("<span class='ui-icon "+p.closeicon[2]+"'></span>");
				}
				if($.isFunction(p.beforeShowForm)) p.beforeShowForm($("#"+frmgr));
				viewModal("#"+IDs.themodal,{gbox:"#gbox_"+gID,jqm:p.jqModal, modal:p.modal});
				$(".fm-button:not(.ui-state-disabled)","#"+frmtb+"_2").hover(
				   function(){$(this).addClass('ui-state-hover');}, 
				   function(){$(this).removeClass('ui-state-hover');}
				);
				focusaref();
				$("#cData", "#"+frmtb+"_2").click(function(e){
					hideModal("#"+IDs.themodal,{gb:"#gbox_"+gID,jqm:p.jqModal, onClose: p.onClose});
					return false;
				});
				$("#nData", "#"+frmtb+"_2").click(function(e){
					$("#FormError","#"+frmtb).hide();
					var npos = getCurrPos();
					npos[0] = parseInt(npos[0]);
					if(npos[0] != -1 && npos[1][npos[0]+1]) {
						if($.isFunction(p.onclickPgButtons)) {
							p.onclickPgButtons('next',$("#"+frmgr),npos[1][npos[0]]);
						}
						fillData(npos[1][npos[0]+1],$t);
						$($t).jqGrid("setSelection",npos[1][npos[0]+1]);
						if($.isFunction(p.afterclickPgButtons)) {
							p.afterclickPgButtons('next',$("#"+frmgr),npos[1][npos[0]+1]);
						}
						updateNav(npos[0]+1,npos[1].length-1);
					};
					focusaref();
					return false;
				});
				$("#pData", "#"+frmtb+"_2").click(function(e){
					$("#FormError","#"+frmtb).hide();
					var ppos = getCurrPos();
					if(ppos[0] != -1 && ppos[1][ppos[0]-1]) {
						if($.isFunction(p.onclickPgButtons)) {
							p.onclickPgButtons('prev',$("#"+frmgr),ppos[1][ppos[0]]);
						}
						fillData(ppos[1][ppos[0]-1],$t);
						$($t).jqGrid("setSelection",ppos[1][ppos[0]-1]);
						if($.isFunction(p.afterclickPgButtons)) {
							p.afterclickPgButtons('prev',$("#"+frmgr),ppos[1][ppos[0]-1]);
						}
						updateNav(ppos[0]-1,ppos[1].length-1);
					};
					focusaref();
					return false;
				});
			};
			function focusaref(){ //Sfari 3 issues
				if(p.closeOnEscape===true || p.navkeys[0]===true) {
					setTimeout(function(){$(".ui-jqdialog-titlebar-close","#"+IDs.modalhead).focus()},0);
				}
			}
			var posInit =getCurrPos();
			updateNav(posInit[0],posInit[1].length-1);
			function updateNav(cr,totr,rid){
				if (cr==0) { $("#pData","#"+frmtb+"_2").addClass('ui-state-disabled'); } else { $("#pData","#"+frmtb+"_2").removeClass('ui-state-disabled'); }
				if (cr==totr) { $("#nData","#"+frmtb+"_2").addClass('ui-state-disabled'); } else { $("#nData","#"+frmtb+"_2").removeClass('ui-state-disabled'); }
			}
			function getCurrPos() {
				var rowsInGrid = $($t).jqGrid("getDataIDs"),
				selrow = $("#id_g","#"+frmtb).val(),
				pos = $.inArray(selrow,rowsInGrid);
				return [pos,rowsInGrid];
			}
			function createData(rowid,obj,tb,maxcols){
				var nm, hc,trdata, tdl, tde, cnt=0,tmp, dc, retpos=[], ind=false,
				tdtmpl = "<td class='CaptionTD form-view-label ui-widget-content' width='"+p.labelswidth+"'>&#160;</td><td class='DataTD form-view-data ui-helper-reset ui-widget-content'>&#160;</td>", tmpl="",
				tdtmpl2 = "<td class='CaptionTD form-view-label ui-widget-content'>&#160;</td><td class='DataTD form-view-data ui-widget-content'>&#160;</td>",
				fmtnum = ['integer','number','currency'],max1 =0, max2=0 ,maxw,setme, viewfld;
				for (var i =1;i<=maxcols;i++) {
					tmpl += i == 1 ? tdtmpl : tdtmpl2;
				}
				// find max number align rigth with property formatter
				$(obj.p.colModel).each( function(i) {
					if(this.editrules && this.editrules.edithidden === true) {
						hc = false;
					} else {
						hc = this.hidden === true ? true : false;
					}
					if(!hc && this.align==='right') {
						if(this.formatter && $.inArray(this.formatter,fmtnum) !== -1 ) {
							max1 = Math.max(max1,parseInt(this.width,10));
						} else {
							max2 = Math.max(max2,parseInt(this.width,10));
						}
					}
				});
				maxw  = max1 !==0 ? max1 : max2 !==0 ? max2 : 0;
				ind = $(obj).jqGrid("getInd",rowid);
				$(obj.p.colModel).each( function(i) {
					nm = this.name;
					setme = false;
					// hidden fields are included in the form
					if(this.editrules && this.editrules.edithidden === true) {
						hc = false;
					} else {
						hc = this.hidden === true ? true : false;
					}
					dc = hc ? "style='display:none'" : "";
					viewfld = (typeof this.viewable != 'boolean') ? true : this.viewable;
					if ( nm !== 'cb' && nm !== 'subgrid' && nm !== 'rn' && viewfld) {
						if(ind === false) {
							tmp = "";
						} else {
							if(nm == obj.p.ExpandColumn && obj.p.treeGrid === true) {
								tmp = $("td:eq("+i+")",obj.rows[ind]).text();
							} else {
								tmp = $("td:eq("+i+")",obj.rows[ind]).html();
							}
						}
						setme = this.align === 'right' && maxw !==0 ? true : false;
						var opt = $.extend({}, this.editoptions || {} ,{id:nm,name:nm}),
						frmopt = $.extend({},{rowabove:false,rowcontent:''}, this.formoptions || {}),
						rp = parseInt(frmopt.rowpos) || cnt+1,
						cp = parseInt((parseInt(frmopt.colpos) || 1)*2);
						if(frmopt.rowabove) {
							var newdata = $("<tr><td class='contentinfo' colspan='"+(maxcols*2)+"'>"+frmopt.rowcontent+"</td></tr>");
							$(tb).append(newdata);
							newdata[0].rp = rp;
						}
						trdata = $(tb).find("tr[rowpos="+rp+"]");
						if ( trdata.length==0 ) {
							trdata = $("<tr "+dc+" rowpos='"+rp+"'></tr>").addClass("FormData").attr("id","trv_"+nm);
							$(trdata).append(tmpl);
							$(tb).append(trdata);
							trdata[0].rp = rp;
						}
						$("td:eq(" + (cp - 2) + ")", trdata[0]).html('<strong>' + (typeof frmopt.label === 'undefined' ? obj.p.colNames[i] : frmopt.label) + '</strong>');
						$("td:eq("+(cp-1)+")",trdata[0]).append("<span>"+tmp+"</span>").attr("id","v_"+nm);
						if(setme){
							$("td:eq("+(cp-1)+") span",trdata[0]).css({'text-align':'right',width:maxw+"px"});
						}
						retpos[cnt] = i;
						cnt++;
					};
				});
				if( cnt > 0) {
					var idrow = $("<tr class='FormData' style='display:none'><td class='CaptionTD'></td><td colspan='"+ (maxcols*2-1)+"' class='DataTD'><input class='FormElement' id='id_g' type='text' name='id' value='"+rowid+"'/></td></tr>");
					idrow[0].rp = cnt+99;
					$(tb).append(idrow);
				}
				return retpos;
			};
			function fillData(rowid,obj){
				var nm, hc,cnt=0,tmp, opt,trv;
				trv = $(obj).jqGrid("getInd",rowid,true);
				if(!trv) return;
				$('td',trv).each( function(i) {
					nm = obj.p.colModel[i].name;
					// hidden fields are included in the form
					if(obj.p.colModel[i].editrules && obj.p.colModel[i].editrules.edithidden === true) {
						hc = false;
					} else {
						hc = obj.p.colModel[i].hidden === true ? true : false;
					}
					if ( nm !== 'cb' && nm !== 'subgrid' && nm !== 'rn') {
						if(nm == obj.p.ExpandColumn && obj.p.treeGrid === true) {
							tmp = $(this).text();
						} else {
							tmp = $(this).html();
						}
						opt = $.extend({},obj.p.colModel[i].editoptions || {});
						nm = $.jgrid.jqID("v_"+nm);
						$("#"+nm+" span","#"+frmtb).html(tmp);
						if (hc) { $("#"+nm,"#"+frmtb).parents("tr:first").hide(); }
						cnt++;
					}
				});
				if(cnt>0) { $("#id_g","#"+frmtb).val(rowid); }
			};
		});
	},
	delGridRow : function(rowids,p) {
		p = $.extend({
			top : 0,
			left: 0,
			width: 240,
			height: 'auto',
			dataheight : 'auto',
			modal: false,
			drag: true,
			resize: true,
			url : '',
			mtype : "POST",
			reloadAfterSubmit: true,
			beforeShowForm: null,
			afterShowForm: null,
			beforeSubmit: null,
			onclickSubmit: null,
			afterSubmit: null,
			jqModal : true,
			closeOnEscape : false,
			delData: {},
			delicon : [],
			cancelicon : [],
			onClose : null,
			ajaxDelOptions : {},
			processing : false,
			serializeDelData : null
		}, $.jgrid.del, p ||{});
		rp_ge = p;
		return this.each(function(){
			var $t = this;
			if (!$t.grid ) { return; }
			if(!rowids) { return; }
			var onBeforeShow = typeof p.beforeShowForm === 'function' ? true: false,
			onAfterShow = typeof p.afterShowForm === 'function' ? true: false,
			gID = $t.p.id, onCS = {},
			dtbl = "DelTbl_"+gID,postd, idname, opers, oper,
			IDs = {themodal:'delmod'+gID,modalhead:'delhd'+gID,modalcontent:'delcnt'+gID, scrollelm: dtbl};
			if (isArray(rowids)) { rowids = rowids.join(); }
			if ( $("#"+IDs.themodal).html() != null ) {
				$("#DelData>td","#"+dtbl).text(rowids);
				$("#DelError","#"+dtbl).hide();
				if( rp_ge.processing === true) {
					rp_ge.processing=false;
					$("#dData", "#"+dtbl).removeClass('ui-state-active');
				}
				if(onBeforeShow) { p.beforeShowForm($("#"+dtbl)); }
				viewModal("#"+IDs.themodal,{gbox:"#gbox_"+gID,jqm:p.jqModal,jqM: false, modal:p.modal});
				if(onAfterShow) { p.afterShowForm($("#"+dtbl)); }
			} else {
				var dh = isNaN(p.dataheight) ? p.dataheight : p.dataheight+"px";
				var tbl = "<div id='"+dtbl+"' class='formdata' style='width:100%;overflow:auto;position:relative;height:"+dh+";'>";
				tbl += "<table class='DelTable'><tbody>";
				// error data 
				tbl += "<tr id='DelError' style='display:none'><td class='ui-state-error'></td></tr>";
				tbl += "<tr id='DelData' style='display:none'><td >"+rowids+"</td></tr>";
				tbl += "<tr><td class=\"delmsg\" style=\"white-space:pre;\">"+p.msg+"</td></tr><tr><td >&#160;</td></tr>";
				// buttons at footer
				tbl += "</tbody></table></div>"
				var bS  = "<a href='javascript:void(0)' id='dData' class='fm-button ui-state-default ui-corner-all'>"+p.bSubmit+"</a>",
				bC  = "<a href='javascript:void(0)' id='eData' class='fm-button ui-state-default ui-corner-all'>"+p.bCancel+"</a>";
				tbl += "<table cellspacing='0' cellpadding='0' border='0' class='EditTable' id='"+dtbl+"_2'><tbody><tr><td class='DataTD ui-widget-content'></td></tr><tr style='display:block;height:3px;'><td></td></tr><tr><td class='DelButton EditButton'>"+bS+"&#160;"+bC+"</td></tr></tbody></table>";
				p.gbox = "#gbox_"+gID;
				createModal(IDs,tbl,p,"#gview_"+$t.p.id,$("#gview_"+$t.p.id)[0]);
				$(".fm-button","#"+dtbl+"_2").hover(
				   function(){$(this).addClass('ui-state-hover');}, 
				   function(){$(this).removeClass('ui-state-hover');}
				);
				p.delicon = $.extend([true,"left","ui-icon-scissors"],p.delicon);
				p.cancelicon = $.extend([true,"left","ui-icon-cancel"],p.cancelicon);
				if(p.delicon[0]==true) {
					$("#dData","#"+dtbl+"_2").addClass(p.delicon[1] == "right" ? 'fm-button-icon-right' : 'fm-button-icon-left')
					.append("<span class='ui-icon "+p.delicon[2]+"'></span>");
				}
				if(p.cancelicon[0]==true) {
					$("#eData","#"+dtbl+"_2").addClass(p.cancelicon[1] == "right" ? 'fm-button-icon-right' : 'fm-button-icon-left')
					.append("<span class='ui-icon "+p.cancelicon[2]+"'></span>");
				}				
				$("#dData","#"+dtbl+"_2").click(function(e){
					var ret=[true,""]; onCS = {};
					var postdata = $("#DelData>td","#"+dtbl).text(); //the pair is name=val1,val2,...
					if( typeof p.onclickSubmit === 'function' ) { onCS = p.onclickSubmit(rp_ge) || {}; }
					if( typeof p.beforeSubmit === 'function' ) { ret = p.beforeSubmit(postdata); }
					if(ret[0]){
						var gurl = rp_ge.url ? rp_ge.url : $($t).jqGrid('getGridParam','editurl');
						if(!gurl) { ret[0]=false;ret[1] += " "+$.jgrid.errors.nourl;}
					}
					if(ret[0] === false) {
						$("#DelError>td","#"+dtbl).html(ret[1]);
						$("#DelError","#"+dtbl).show();
					} else {
						if(!rp_ge.processing) {
							rp_ge.processing = true;
							$(this).addClass('ui-state-active');
							opers = $t.p.prmNames;
							postd = $.extend({},rp_ge.delData, onCS);
							oper = opers.oper;
							postd[oper] = opers.deloper;
							idname = opers.id;
							postd[idname] = postdata;
							$.ajax( $.extend({
								url:gurl,
								type: p.mtype,
								data: $.isFunction(p.serializeDelData) ? p.serializeDelData(postd) : postd,
								complete:function(data,Status){
									if(Status != "success") {
										ret[0] = false;
									    if ($.isFunction(rp_ge.errorTextFormat)) {
									        ret[1] = rp_ge.errorTextFormat(data);
									    } else {
									        ret[1] = Status + " Status: '" + data.statusText + "'. Error code: " + data.status;
										}
									} else {
										// data is posted successful
										// execute aftersubmit with the returned data from server
										if( typeof rp_ge.afterSubmit === 'function' ) {
											ret = rp_ge.afterSubmit(data,postd);
										}
									}
									if(ret[0] === false) {
										$("#DelError>td","#"+dtbl).html(ret[1]);
										$("#DelError","#"+dtbl).show();
									} else {
										if(rp_ge.reloadAfterSubmit) {
											$($t).trigger("reloadGrid");
										} else {
											var toarr = [];
											toarr = postdata.split(",");
											if($t.p.treeGrid===true){
												try {$($t).jqGrid("delTreeNode",toarr[0])} catch(e){}
											} else {
												for(var i=0;i<toarr.length;i++) {
													$($t).jqGrid("delRowData",toarr[i]);
												}
											}
											$t.p.selrow = null;
											$t.p.selarrrow = [];
										}
										if($.isFunction(rp_ge.afterComplete)) {
											setTimeout(function(){rp_ge.afterComplete(data,postdata);},500);
										}
									}
									rp_ge.processing=false;
									$("#dData", "#"+dtbl+"_2").removeClass('ui-state-active');
									if(ret[0]) { hideModal("#"+IDs.themodal,{gb:"#gbox_"+gID,jqm:p.jqModal, onClose: rp_ge.onClose}); }
								},
								error:function(xhr,st,err){
									$("#DelError>td","#"+dtbl).html(st+ " : "+err);
									$("#DelError","#"+dtbl).show();
									rp_ge.processing=false;
									$("#dData", "#"+dtbl+"_2").removeClass('ui-state-active');;
								}
							}, $.jgrid.ajaxOptions, p.ajaxDelOptions));
						}
					}
					return false;
				});
				$("#eData", "#"+dtbl+"_2").click(function(e){
					hideModal("#"+IDs.themodal,{gb:"#gbox_"+gID,jqm:p.jqModal, onClose: rp_ge.onClose});
					return false;
				});
				if(onBeforeShow) { p.beforeShowForm($("#"+dtbl)); }
				viewModal("#"+IDs.themodal,{gbox:"#gbox_"+gID,jqm:p.jqModal,modal:p.modal});
				if(onAfterShow) { p.afterShowForm($("#"+dtbl)); }
			}
			if(p.closeOnEscape===true) {
				setTimeout(function(){$(".ui-jqdialog-titlebar-close","#"+IDs.modalhead).focus()},0);
			}
		});
	},
	navGrid : function (elem, o, pEdit,pAdd,pDel,pSearch, pView) {
		o = $.extend({
			edit: true,
			editicon: "ui-icon-pencil",
			add: true,
			addicon:"ui-icon-plus",
			del: true,
			delicon:"ui-icon-trash",
			search: true,
			searchicon:"ui-icon-search",
			refresh: true,
			refreshicon:"ui-icon-refresh",
			refreshstate: 'firstpage',
			view: false,
			viewicon : "ui-icon-document",
			position : "left",
			closeOnEscape : true,
			beforeRefresh : null,
			afterRefresh : null,
			cloneToTop : false
		}, $.jgrid.nav, o ||{});
		return this.each(function() {       
			var alertIDs = {themodal:'alertmod',modalhead:'alerthd',modalcontent:'alertcnt'},
			$t = this, vwidth, vheight, twd, tdw;
			if(!$t.grid || typeof elem != 'string') { return; }
			if ($("#"+alertIDs.themodal).html() == null) {
				if (typeof window.innerWidth != 'undefined') {
					vwidth = window.innerWidth,
					vheight = window.innerHeight
				} else if (typeof document.documentElement != 'undefined' && typeof document.documentElement.clientWidth != 'undefined' && document.documentElement.clientWidth != 0) {
					vwidth = document.documentElement.clientWidth,
					vheight = document.documentElement.clientHeight
				} else {
					vwidth=1024;
					vheight=768;
				}
				createModal(alertIDs,"<div>"+o.alerttext+"</div><span tabindex='0'><span tabindex='-1' id='jqg_alrt'></span></span>",{gbox:"#gbox_"+$t.p.id,jqModal:true,drag:true,resize:true,caption:o.alertcap,top:vheight/2-25,left:vwidth/2-100,width:200,height:'auto',closeOnEscape:o.closeOnEscape},"","",true);
			}
			var clone = 1;
			if(o.cloneToTop && $t.p.toppager) clone = 2;
			for(var i = 0; i<clone; i++) {
				var tbd,
				navtbl = $("<table cellspacing='0' cellpadding='0' border='0' class='ui-pg-table navtable' style='float:left;table-layout:auto;'><tbody><tr></tr></tbody></table>"),
				sep = "<td class='ui-pg-button ui-state-disabled' style='width:4px;'><span class='ui-separator'></span></td>",
				pgid, elemids;
				if(i==0) {
					pgid = elem;
					elemids = $t.p.id;
					if(pgid == $t.p.toppager) {
						elemids += "_top";
						clone = 1;
					}
				} else {
					pgid = $t.p.toppager;
					elemids = $t.p.id+"_top";
				}
				if($t.p.direction == "rtl") $(navtbl).attr("dir","rtl").css("float","right");
				if (o.add) {
					pAdd = pAdd || {};
					tbd = $("<td class='ui-pg-button ui-corner-all'></td>");
					$(tbd).append("<div class='ui-pg-div'><span class='ui-icon "+o.addicon+"'></span>"+o.addtext+"</div>");
					$("tr",navtbl).append(tbd);
					$(tbd,navtbl)
					.attr({"title":o.addtitle || "",id : pAdd.id || "add_"+elemids})
					.click(function(){
						if (typeof o.addfunc == 'function') {
							o.addfunc();
						} else {
							$($t).jqGrid("editGridRow","new",pAdd);
						}
						return false;
					}).hover(function () {$(this).addClass("ui-state-hover");},
						function () {$(this).removeClass("ui-state-hover");}
					);
					tbd = null;
				}
				if (o.edit) {
					tbd = $("<td class='ui-pg-button ui-corner-all'></td>");
					pEdit = pEdit || {};
					$(tbd).append("<div class='ui-pg-div'><span class='ui-icon "+o.editicon+"'></span>"+o.edittext+"</div>");
					$("tr",navtbl).append(tbd);
					$(tbd,navtbl)
					.attr({"title":o.edittitle || "",id: pEdit.id || "edit_"+elemids})
					.click(function(){
						var sr = $t.p.selrow;
						if (sr) {
							if(typeof o.editfunc == 'function') {
								o.editfunc(sr);
							} else {
								$($t).jqGrid("editGridRow",sr,pEdit);
							}
						} else {
							viewModal("#"+alertIDs.themodal,{gbox:"#gbox_"+$t.p.id,jqm:true});
							$("#jqg_alrt").focus();
						}
						return false;
					}).hover( function () {$(this).addClass("ui-state-hover");},
						function () {$(this).removeClass("ui-state-hover");}
					);
					tbd = null;
				}
				if (o.view) {
					tbd = $("<td class='ui-pg-button ui-corner-all'></td>");
					pView = pView || {};
					$(tbd).append("<div class='ui-pg-div'><span class='ui-icon "+o.viewicon+"'></span>"+o.viewtext+"</div>");
					$("tr",navtbl).append(tbd);
					$(tbd,navtbl)
					.attr({"title":o.viewtitle || "",id: pView.id || "view_"+elemids})
					.click(function(){
						var sr = $t.p.selrow;
						if (sr) {
							$($t).jqGrid("viewGridRow",sr,pView);
						} else {
							viewModal("#"+alertIDs.themodal,{gbox:"#gbox_"+$t.p.id,jqm:true});
							$("#jqg_alrt").focus();
						}
						return false;
					}).hover( function () {$(this).addClass("ui-state-hover");},
						function () {$(this).removeClass("ui-state-hover");}
					);
					tbd = null;
				}
				if (o.del) {
					tbd = $("<td class='ui-pg-button ui-corner-all'></td>");
					pDel = pDel || {};
					$(tbd).append("<div class='ui-pg-div'><span class='ui-icon "+o.delicon+"'></span>"+o.deltext+"</div>");
					$("tr",navtbl).append(tbd);
					$(tbd,navtbl)
					.attr({"title":o.deltitle || "",id: pDel.id || "del_"+elemids})
					.click(function(){
						var dr;
						if($t.p.multiselect) {
							dr = $t.p.selarrrow;
							if(dr.length==0) { dr = null; }
						} else {
							dr = $t.p.selrow;
						}
						if (dr) { $($t).jqGrid("delGridRow",dr,pDel); }
						else  {viewModal("#"+alertIDs.themodal,{gbox:"#gbox_"+$t.p.id,jqm:true}); $("#jqg_alrt").focus(); }
						return false;
					}).hover(function () {$(this).addClass("ui-state-hover");},
						function () {$(this).removeClass("ui-state-hover");}
					);
					tbd = null;
				}
				if(o.add || o.edit || o.del || o.view) { $("tr",navtbl).append(sep); }
				if (o.search) {
					tbd = $("<td class='ui-pg-button ui-corner-all'></td>");
					pSearch = pSearch || {};
					$(tbd).append("<div class='ui-pg-div'><span class='ui-icon "+o.searchicon+"'></span>"+o.searchtext+"</div>");
					$("tr",navtbl).append(tbd);
					$(tbd,navtbl)
					.attr({"title":o.searchtitle  || "",id:pSearch.id || "search_"+elemids})
					.click(function(){
						$($t).jqGrid("searchGrid",pSearch);
						return false;
					}).hover(function () {$(this).addClass("ui-state-hover");},
						function () {$(this).removeClass("ui-state-hover");}
					);
					tbd = null;
				}
				if (o.refresh) {
					tbd = $("<td class='ui-pg-button ui-corner-all'></td>");
					$(tbd).append("<div class='ui-pg-div'><span class='ui-icon "+o.refreshicon+"'></span>"+o.refreshtext+"</div>");
					$("tr",navtbl).append(tbd);
					$(tbd,navtbl)
					.attr({"title":o.refreshtitle  || "",id: "refresh_"+elemids})
					.click(function(){
						if($.isFunction(o.beforeRefresh)) o.beforeRefresh();
						$t.p.search = false;
						try {
							var gID = $t.p.id;
							$("#fbox_"+gID).searchFilter().reset();
	                        $t.clearToolbar(false);
						} catch (e) {}
						switch (o.refreshstate) {
							case 'firstpage':
							    $($t).trigger("reloadGrid", [{page:1}]);
								break;
							case 'current':
							    $($t).trigger("reloadGrid", [{current:true}]);
								break;
						}
						if($.isFunction(o.afterRefresh)) o.afterRefresh();
						return false;
					}).hover(function () {$(this).addClass("ui-state-hover");},
						function () {$(this).removeClass("ui-state-hover");}
					);
					tbd = null;
				}
				tdw = $(".ui-jqgrid").css("font-size") || "11px";
				$('body').append("<div id='testpg2' class='ui-jqgrid ui-widget ui-widget-content' style='font-size:"+tdw+";visibility:hidden;' ></div>");
				twd = $(navtbl).clone().appendTo("#testpg2").width();
				$("#testpg2").remove();
				$(pgid+"_"+o.position,pgid).append(navtbl);
				if($t.p._nvtd) {
					if(twd > $t.p._nvtd[0] ) {
						$(pgid+"_"+o.position,pgid).width(twd);
						$t.p._nvtd[0] = twd;
					}
					$t.p._nvtd[1] = twd;
				}
				tdw =null; twd=null; navtbl =null;
			}
		});
	},
	navButtonAdd : function (elem, p) {
		p = $.extend({
			caption : "newButton",
			title: '',
			buttonicon : 'ui-icon-newwin',
			onClickButton: null,
			position : "last",
			cursor : 'pointer'
		}, p ||{});
		return this.each(function() {
			if( !this.grid)  { return; }
			if( elem.indexOf("#") != 0) { elem = "#"+elem; }
			var findnav = $(".navtable",elem)[0], $t = this;
			if (findnav) {
				var tbd = $("<td></td>");
				$(tbd).addClass('ui-pg-button ui-corner-all').append("<div class='ui-pg-div'><span class='ui-icon "+p.buttonicon+"'></span>"+p.caption+"</div>");
				if(p.id) {$(tbd).attr("id",p.id);}
				if(p.position=='first'){
					if(findnav.rows[0].cells.length ===0 ) {
						$("tr",findnav).append(tbd);
					} else {
						$("tr td:eq(0)",findnav).before(tbd);
					}
				} else {
					$("tr",findnav).append(tbd);
				}
				$(tbd,findnav)
				.attr("title",p.title  || "")
				.click(function(e){
					if ($.isFunction(p.onClickButton) ) { p.onClickButton.call($t,e); }
					return false;
				})
				.hover(
					function () {$(this).addClass("ui-state-hover");},
					function () {$(this).removeClass("ui-state-hover");}
				)
				.css("cursor",p.cursor ? p.cursor : "normal");
			}
		});
	},
	navSeparatorAdd:function (elem,p) {
		p = $.extend({
			sepclass : "ui-separator",
			sepcontent: ''
		}, p ||{});		
		return this.each(function() {
			if( !this.grid)  { return; }
			if( elem.indexOf("#") != 0) { elem = "#"+elem; }
			var findnav = $(".navtable",elem)[0];
			if(findnav) {
				var sep = "<td class='ui-pg-button ui-state-disabled' style='width:4px;'><span class='"+p.sepclass+"'></span>"+p.sepcontent+"</td>";
				$("tr",findnav).append(sep);
			}
		});
	},
	GridToForm : function( rowid, formid ) {
		return this.each(function(){
			var $t = this;
			if (!$t.grid) { return; } 
			var rowdata = $($t).jqGrid("getRowData",rowid);
			if (rowdata) {
				for(var i in rowdata) {
					if ( $("[name="+i+"]",formid).is("input:radio") || $("[name="+i+"]",formid).is("input:checkbox"))  {
						$("[name="+i+"]",formid).each( function() {
							if( $(this).val() == rowdata[i] ) {
								$(this).attr("checked","checked");
							} else {
								$(this).attr("checked","");
							}
						});
					} else {
					// this is very slow on big table and form.
						$("[name="+i+"]",formid).val(rowdata[i]);
					}
				}
			}
		});
	},
	FormToGrid : function(rowid, formid, mode, position){
		return this.each(function() {
			var $t = this;
			if(!$t.grid) { return; }
			if(!mode) mode = 'set';
			if(!position) position = 'first';
			var fields = $(formid).serializeArray();
			var griddata = {};
			$.each(fields, function(i, field){
				griddata[field.name] = field.value;
			});
			if(mode=='add') $($t).jqGrid("addRowData",rowid,griddata, position);
			else if(mode=='set') $($t).jqGrid("setRowData",rowid,griddata);
		});
	}
});
})(jQuery);
;;(function($){
/*
**
 * jqGrid addons using jQuery UI 
 * Author: Mark Williams
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 * depends on jQuery UI sortable
**/
if ($.browser.msie && $.browser.version==8) {
	$.expr[":"].hidden = function(elem) {
		return elem.offsetWidth === 0 || elem.offsetHeight === 0 ||
			elem.style.display == "none";
	}
}

if ($.ui && $.ui.multiselect && $.ui.multiselect.prototype._setSelected) {
    var setSelected = $.ui.multiselect.prototype._setSelected;
    $.ui.multiselect.prototype._setSelected = function(item,selected) {
        var ret = setSelected.call(this,item,selected);
        if (selected && this.selectedList) {
            var elt = this.element;
		    this.selectedList.find('li').each(function() {
			    if ($(this).data('optionLink'))
				    $(this).data('optionLink').remove().appendTo(elt);
		    });
        }
        return ret;
    }
}
        
$.jgrid.extend({
	sortableColumns : function (tblrow)
	{
		return this.each(function (){
			var ts = this;
			function start() {ts.p.disableClick = true;};
			var sortable_opts = {
				"tolerance" : "pointer",
				"axis" : "x",
				"items": '>th:not(:has(#jqgh_cb,#jqgh_rn,#jqgh_subgrid),:hidden)',
				"placeholder": {
					element: function(item) {
						var el = $(document.createElement(item[0].nodeName))
						.addClass(item[0].className+" ui-sortable-placeholder ui-state-highlight")
						.removeClass("ui-sortable-helper")[0];
						return el;
					},
					update: function(self, p) {
						p.height(self.currentItem.innerHeight() - parseInt(self.currentItem.css('paddingTop')||0, 10) - parseInt(self.currentItem.css('paddingBottom')||0, 10));
						p.width(self.currentItem.innerWidth() - parseInt(self.currentItem.css('paddingLeft')||0, 10) - parseInt(self.currentItem.css('paddingRight')||0, 10));
					}
				},
				"update": function(event, ui) {
					var p = $(ui.item).parent();
					var th = $(">th", p);
					var colModel = ts.p.colModel;
					var cmMap = {};
					$.each(colModel, function(i) { cmMap[this.name]=i });
					var permutation = [];
					th.each(function(i) {
						var id = $(">div", this).get(0).id.replace(/^jqgh_/, "");
							if (id in cmMap) {
								permutation.push(cmMap[id]);
							}
					});
	
					$(ts).jqGrid("remapColumns",permutation, true, true);
					if ($.isFunction(ts.p.sortable.update)) {
						ts.p.sortable.update(permutation);
					}
					setTimeout(function(){ts.p.disableClick=false}, 50);
				}
			};
			if (ts.p.sortable.options) {
				$.extend(sortable_opts, ts.p.sortable.options);
			} else if ($.isFunction(ts.p.sortable)) {
				ts.p.sortable = { "update" : ts.p.sortable };
			}
			if (sortable_opts.start) {
				var s = sortable_opts.start;
				sortable_opts.start = function(e,ui) {
					start();
					s.call(this,e,ui);
				}
			} else {
				sortable_opts.start = start;
			}
			if (ts.p.sortable.exclude) {
				sortable_opts.items += ":not("+ts.p.sortable.exclude+")";
			}
			tblrow.sortable(sortable_opts).data("sortable").floating = true;
		});
	},
    columnChooser : function(opts) {
        var self = this;
		if($("#colchooser_"+self[0].p.id).length ) return;
        var selector = $('<div id="colchooser_'+self[0].p.id+'" style="position:relative;overflow:hidden"><div><select multiple="multiple"></select></div></div>');
        var select = $('select', selector);

        opts = $.extend({
            "width" : 420,
            "height" : 240,
            "classname" : null,
            "done" : function(perm) { if (perm) self.jqGrid("remapColumns", perm, true) },
            /* msel is either the name of a ui widget class that
               extends a multiselect, or a function that supports
               creating a multiselect object (with no argument,
               or when passed an object), and destroying it (when
               passed the string "destroy"). */
            "msel" : "multiselect",
            /* "msel_opts" : {}, */

            /* dlog is either the name of a ui widget class that 
               behaves in a dialog-like way, or a function, that
               supports creating a dialog (when passed dlog_opts)
               or destroying a dialog (when passed the string
               "destroy")
               */
            "dlog" : "dialog",

            /* dlog_opts is either an option object to be passed 
               to "dlog", or (more likely) a function that creates
               the options object.
               The default produces a suitable options object for
               ui.dialog */
            "dlog_opts" : function(opts) {
                var buttons = {};
                buttons[opts.bSubmit] = function() {
                    opts.apply_perm();
                    opts.cleanup(false);
                };
                buttons[opts.bCancel] = function() {
                    opts.cleanup(true);
                };
                return {
                    "buttons": buttons,
                    "close": function() {
                        opts.cleanup(true);
                    },
					"modal" : false,
                    "resizable": false,
                    "width": opts.width+20
                };
            },
            /* Function to get the permutation array, and pass it to the
               "done" function */
            "apply_perm" : function() {
                $('option',select).each(function(i) {
                    if (this.selected) {
                        self.jqGrid("showCol", colModel[this.value].name);
                    } else {
                        self.jqGrid("hideCol", colModel[this.value].name);
                    }
                });
                
                var perm = [];
				//fixedCols.slice(0);
                $('option[selected]',select).each(function() { perm.push(parseInt(this.value)) });
                $.each(perm, function() { delete colMap[colModel[parseInt(this)].name] });
                $.each(colMap, function() {
					var ti = parseInt(this);
					perm = insert(perm,ti,ti);
				});
                if (opts.done) {
                    opts.done.call(self, perm);
                }
            },
            /* Function to cleanup the dialog, and select. Also calls the
               done function with no permutation (to indicate that the
               columnChooser was aborted */
            "cleanup" : function(calldone) {
                call(opts.dlog, selector, 'destroy');
                call(opts.msel, select, 'destroy');
                selector.remove();
                if (calldone && opts.done) {
                    opts.done.call(self);
                }
            },
			"msel_opts" : {}
        }, $.jgrid.col, opts || {});

        if (opts.caption) {
            selector.attr("title", opts.caption);
        }
        if (opts.classname) {
            selector.addClass(classname);
            select.addClass(classname);
        }
        if (opts.width) {
            $(">div",selector).css({"width": opts.width,"margin":"0 auto"});
            select.css("width", opts.width);
        }
        if (opts.height) {
            $(">div",selector).css("height", opts.height);
            select.css("height", opts.height - 10);
        }
        var colModel = self.jqGrid("getGridParam", "colModel");
        var colNames = self.jqGrid("getGridParam", "colNames");
        var colMap = {}, fixedCols = [];

        select.empty();
        $.each(colModel, function(i) {
            colMap[this.name] = i;
            if (this.hidedlg) {
                if (!this.hidden) {
                    fixedCols.push(i);
                }
                return;
            }

            select.append("<option value='"+i+"' "+
                          (this.hidden?"":"selected='selected'")+">"+colNames[i]+"</option>");
        });
		function insert(perm,i,v) {
			if(i>=0){
				var a = perm.slice();
				var b = a.splice(i);
				if(i>perm.length) i = perm.length;
				a[i] = v;
				return a.concat(b);
			}
		}
        function call(fn, obj) {
            if (!fn) return;
            if (typeof fn == 'string') {
                if ($.fn[fn]) {
                    $.fn[fn].apply(obj, $.makeArray(arguments).slice(2));
                }
            } else if ($.isFunction(fn)) {
                fn.apply(obj, $.makeArray(arguments).slice(2));
            }
        }

        var dopts = $.isFunction(opts.dlog_opts) ? opts.dlog_opts.call(self, opts) : opts.dlog_opts;
        call(opts.dlog, selector, dopts);
        var mopts = $.isFunction(opts.msel_opts) ? opts.msel_opts.call(self, opts) : opts.msel_opts;
        call(opts.msel, select, mopts);
    },
	sortableRows : function (opts) {
		// Can accept all sortable options and events
		return this.each(function(){
			var $t = this;
			if(!$t.grid) return;
			// Currently we disable a treeGrid sortable
			if($t.p.treeGrid) return;
			if($.fn['sortable']) {
				opts = $.extend({
					"cursor":"move",
					"axis" : "y",
					"items": ".jqgrow"
					},
				opts || {});
				if(opts.start && $.isFunction(opts.start)) {
					opts._start_ = opts.start;
					delete opts.start;
				} else {opts._start_=false;}
				if(opts.update && $.isFunction(opts.update)) {
					opts._update_ = opts.update;
					delete opts.update;
				} else {opts._update_ = false;}
				opts.start = function(ev,ui) {
					$(ui.item).css("border-width","0px");
					$("td",ui.item).each(function(i){
						this.style.width = $t.grid.cols[i].style.width;
					});
					if($t.p.subGrid) {
						var subgid = $(ui.item).attr("id");
						try {
							$($t).jqGrid('collapseSubGridRow',subgid);
						} catch (e) {}
					}
					if(opts._start_) {
						opts._start_.apply(this,[ev,ui]);
					}
				}
				opts.update = function (ev,ui) {
					$(ui.item).css("border-width","");
					$t.updateColumns();
					if($t.p.rownumbers === true) {
						$("td.jqgrid-rownum",$t.rows).each(function(i){
							$(this).html(i+1);
						});
					}
					if(opts._update_) {
						opts._update_.apply(this,[ev,ui]);
					}
				}
				$("tbody:first",$t).sortable(opts);
			}
		});
	},
	gridDnD : function(opts) {
		return this.each(function(){
		var $t = this;
		if(!$t.grid) return;
		// Currently we disable a treeGrid drag and drop
		if($t.p.treeGrid) return;
		if(!$.fn['draggable'] || !$.fn['droppable']) return;
		function updateDnD ()
		{
			var datadnd = $.data($t,"dnd");
		    $("tr.jqgrow:not(.ui-draggable)",$t).draggable($.isFunction(datadnd.drag) ? datadnd.drag.call($($t),datadnd) : datadnd.drag);
		}
		var appender = "<table id='jqgrid_dnd' class='ui-jqgrid-dnd'></table>";
		if($("#jqgrid_dnd").html() == null) {
			$('body').append(appender);
		}

		if(typeof opts == 'string' && opts == 'updateDnD' && $t.p.jqgdnd==true) {
			updateDnD();
			return;
		}
		opts = $.extend({
			"drag" : function (opts) {
				return $.extend({
					start : function (ev, ui) {
						// if we are in subgrid mode try to collapse the node
						if($t.p.subGrid) {
							var subgid = $(ui.helper).attr("id");
							try {
								$($t).jqGrid('collapseSubGridRow',subgid);
							} catch (e) {}
						}
						// hack
						// drag and drop does not insert tr in table, when the table has no rows
						// we try to insert new empty row on the target(s)
						for (var i=0;i<$.data($t,"dnd").connectWith.length;i++){
							if($($.data($t,"dnd").connectWith[i]).jqGrid('getGridParam','reccount') == "0" ){
								$($.data($t,"dnd").connectWith[i]).jqGrid('addRowData','jqg_empty_row',{});
							}
						}
						ui.helper.addClass("ui-state-highlight");
						$("td",ui.helper).each(function(i) {
							this.style.width = $t.grid.headers[i].width+"px";
						});
						if(opts.onstart && $.isFunction(opts.onstart) ) opts.onstart.call($($t),ev,ui);
					},
					stop :function(ev,ui) {
						if(ui.helper.dropped) {
							var ids = $(ui.helper).attr("id");
							$($t).jqGrid('delRowData',ids );
						}
						// if we have a empty row inserted from start event try to delete it
						for (var i=0;i<$.data($t,"dnd").connectWith.length;i++){
							$($.data($t,"dnd").connectWith[i]).jqGrid('delRowData','jqg_empty_row');
						}
						if(opts.onstop && $.isFunction(opts.onstop) ) opts.onstop.call($($t),ev,ui);
					}
				},opts.drag_opts || {});
			},
			"drop" : function (opts) {
				return $.extend({
					accept: function(d) {
						var tid = $(d).closest("table.ui-jqgrid-btable");
						if($.data(tid[0],"dnd") != undefined) {
						    var cn = $.data(tid[0],"dnd").connectWith;
						    return $.inArray('#'+this.id,cn) != -1 ? true : false;
						}
						return d;
					},
					drop: function(ev, ui) {
						var accept = $(ui.draggable).attr("id");
						var getdata = $('#'+$t.id).jqGrid('getRowData',accept);
						if(!opts.dropbyname) {
							var j =0, tmpdata = {}, dropname;
							var dropmodel = $("#"+this.id).jqGrid('getGridParam','colModel');
							try {
								for (key in getdata) {
									if(dropmodel[j]) {
										dropname = dropmodel[j].name;
										tmpdata[dropname] = getdata[key];
									}
									j++;
								}
								getdata = tmpdata;
							} catch (e) {}
 						}
						ui.helper.dropped = true;
						if(opts.beforedrop && $.isFunction(opts.beforedrop) ) {
							//parameters to this callback - event, element, data to be inserted, sender, reciever
							// should return object which will be inserted into the reciever
							var datatoinsert = opts.beforedrop.call(this,ev,ui,getdata,$('#'+$t.id),$(this));
							if (typeof datatoinsert != "undefined" && datatoinsert !== null && typeof datatoinsert == "object") getdata = datatoinsert;
						}
						if(ui.helper.dropped) {
							var grid;
							if(opts.autoid) {
								if($.isFunction(opts.autoid)) {
									grid = opts.autoid.call(this,getdata);
								} else {
									grid = Math.ceil(Math.random()*1000);
									grid = opts.autoidprefix+grid;
								}
							}
							// NULL is interpreted as undefined while null as object
							$("#"+this.id).jqGrid('addRowData',grid,getdata,opts.droppos);
						}
						if(opts.ondrop && $.isFunction(opts.ondrop) ) opts.ondrop.call(this,ev,ui, getdata);
					}}, opts.drop_opts || {});
			},
			"onstart" : null,
			"onstop" : null,
			"beforedrop": null,
			"ondrop" : null,
			"drop_opts" : {
				"activeClass": "ui-state-active",
				"hoverClass": "ui-state-hover"
			},
			"drag_opts" : {
				"revert": "invalid",
				"helper": "clone",
				"cursor": "move",
				"appendTo" : "#jqgrid_dnd",
				"zIndex": 5000
			},
			"dropbyname" : false,
			"droppos" : "first",
			"autoid" : true,
			"autoidprefix" : "dnd_"
		}, opts || {});
		
		if(!opts.connectWith) return;
		opts.connectWith = opts.connectWith.split(",");
		opts.connectWith = $.map(opts.connectWith,function(n){return $.trim(n);});
		$.data($t,"dnd",opts);
		
		if($t.p.reccount != "0" && !$t.p.jqgdnd) {
			updateDnD();
		}
		$t.p.jqgdnd = true;
		for (var i=0;i<opts.connectWith.length;i++){
			var cn =opts.connectWith[i]
			$(cn).droppable($.isFunction(opts.drop) ? opts.drop.call($($t),opts) : opts.drop);
		};
		});
	},
	gridResize : function(opts) {
		return this.each(function(){
			var $t = this;
			if(!$t.grid || !$.fn['resizable']) return;
			opts = $.extend({}, opts || {});
			if(opts.alsoResize ) {
				opts._alsoResize_ = opts.alsoResize;
				delete opts.alsoResize;
			} else {
				opts._alsoResize_ = false;
			}
			if(opts.stop && $.isFunction(opts.stop)) {
				opts._stop_ = opts.stop;
				delete opts.stop;
			} else {
				opts._stop_ = false;
			}
			opts.stop = function (ev, ui) {
				$($t).jqGrid('setGridParam',{height:$("#gview_"+$t.p.id+" .ui-jqgrid-bdiv").height()});
				$($t).jqGrid('setGridWidth',ui.size.width,opts.shrinkToFit);
				if(opts._stop_) opts._stop_.call($t,ev,ui);
			};
			if(opts._alsoResize_) {
				var optstest = "{'\#gview_"+$t.p.id+" .ui-jqgrid-bdiv\':true,'" +opts._alsoResize_+"':true}";
				opts.alsoResize = eval('('+optstest+')'); // the only way that I found to do this
			} else {
				opts.alsoResize = $(".ui-jqgrid-bdiv","#gview_"+$t.p.id);
			}
			delete opts._alsoResize_;
			$("#gbox_"+$t.p.id).resizable(opts);
		});
	}
});
})(jQuery);
;;(function($){
/**
 * jqGrid extension
 * Paul Tiseo ptiseo@wasteconsultants.com
 * 
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl-2.0.html
**/ 
$.jgrid.extend({
	getPostData : function(){
		var $t = this[0];
		if(!$t.grid) { return; }
		return $t.p.postData;
	},
	setPostData : function( newdata ) {
		var $t = this[0];
		if(!$t.grid) { return; }
		// check if newdata is correct type
		if ( typeof(newdata) === 'object' ) {
			$t.p.postData = newdata;
		}
		else {
			alert("cannot add a non-object postData value. postData unchanged.");
		}
	},
	appendPostData : function( newdata ) { 
		var $t = this[0];
		if(!$t.grid) { return; }
		// check if newdata is correct type
		if ( typeof(newdata) === 'object' ) {
			$.extend($t.p.postData, newdata);
		}
		else {
			alert("cannot append a non-object postData value. postData unchanged.");
		}
	},
	setPostDataItem : function( key, val ) {
		var $t = this[0];
		if(!$t.grid) { return; }
		$t.p.postData[key] = val;
	},
	getPostDataItem : function( key ) {
		var $t = this[0];
		if(!$t.grid) { return; }
		return $t.p.postData[key];
	},
	removePostDataItem : function( key ) {
		var $t = this[0];
		if(!$t.grid) { return; }
		delete $t.p.postData[key];
	},
	getUserData : function(){
		var $t = this[0];
		if(!$t.grid) { return; }
		return $t.p.userData;
	},
	getUserDataItem : function( key ) {
		var $t = this[0];
		if(!$t.grid) { return; }
		return $t.p.userData[key];
	}
});
})(jQuery);;;(function($){
/**
 * jqGrid extension for custom methods
 * Tony Tomov tony@trirand.com
 * http://trirand.com/blog/ 
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl-2.0.html
**/ 
$.jgrid.extend({
	getColProp : function(colname){
		var ret ={}, $t = this[0];
		if ( !$t.grid ) { return; }
		var cM = $t.p.colModel;
		for ( var i =0;i<cM.length;i++ ) {
			if ( cM[i].name == colname ) {
				ret = cM[i];
				break;
			}
		}
		return ret;
	},
	setColProp : function(colname, obj){
		//do not set width will not work
		return this.each(function(){
			if ( this.grid ) {
				if ( obj ) {
					var cM = this.p.colModel;
					for ( var i =0;i<cM.length;i++ ) {
						if ( cM[i].name == colname ) {
							$.extend(this.p.colModel[i],obj);
							break;
						}
					}
				}
			}
		});
	},
	sortGrid : function(colname,reload, sor){
		return this.each(function(){
			var $t=this,idx=-1;
			if ( !$t.grid ) { return;}
			if ( !colname ) { colname = $t.p.sortname; }
			for ( var i=0;i<$t.p.colModel.length;i++ ) {
				if ( $t.p.colModel[i].index == colname || $t.p.colModel[i].name==colname ) {
					idx = i;
					break;
				}
			}
			if ( idx!=-1 ){
				var sort = $t.p.colModel[idx].sortable;
				if ( typeof sort !== 'boolean' ) { sort =  true; }
				if ( typeof reload !=='boolean' ) { reload = false; }
				if ( sort ) { $t.sortData("jqgh_"+colname, idx, reload, sor); }
			}
		});
	},
	GridDestroy : function () {
		return this.each(function(){
			if ( this.grid ) { 
				if ( this.p.pager ) { // if not part of grid
					$(this.p.pager).remove();
				}
				var gid = this.id;
				try {
					$("#gbox_"+gid).remove();
				} catch (_) {}
			}
		});
	},
	GridUnload : function(){
		return this.each(function(){
			if ( !this.grid ) {return;}
			var defgrid = {id: $(this).attr('id'),cl: $(this).attr('class')};
			if (this.p.pager) {
				$(this.p.pager).empty().removeClass("ui-state-default ui-jqgrid-pager corner-bottom");
			}
			var newtable = document.createElement('table');
			$(newtable).attr({id:defgrid.id});
			newtable.className = defgrid.cl;
			var gid = this.id;
			$(newtable).removeClass("ui-jqgrid-btable");
			if( $(this.p.pager).parents("#gbox_"+gid).length === 1 ) {
				$(newtable).insertBefore("#gbox_"+gid).show();
				$(this.p.pager).insertBefore("#gbox_"+gid);
			} else {
				$(newtable).insertBefore("#gbox_"+gid).show();
			}
			$("#gbox_"+gid).remove();
		});
	},
    setGridState : function(state) {
		return this.each(function(){
			if ( !this.grid ) {return;}
            var $t = this;
            if(state == 'hidden'){
				$(".ui-jqgrid-bdiv, .ui-jqgrid-hdiv","#gview_"+$t.p.id).slideUp("fast");
				if($t.p.pager) {$($t.p.pager).slideUp("fast");}
				if($t.p.toppager) {$($t.p.toppager).slideUp("fast");}
				if($t.p.toolbar[0]===true) {
					if( $t.p.toolbar[1]=='both') {
						$($t.grid.ubDiv).slideUp("fast");
					}
					$($t.grid.uDiv).slideUp("fast");
				}
				if($t.p.footerrow) { $(".ui-jqgrid-sdiv","#gbox_"+$t.p.id).slideUp("fast"); }
				$(".ui-jqgrid-titlebar-close span",$t.grid.cDiv).removeClass("ui-icon-circle-triangle-n").addClass("ui-icon-circle-triangle-s");
				$t.p.gridstate = 'hidden';
            } else if(state=='visible') {
				$(".ui-jqgrid-hdiv, .ui-jqgrid-bdiv","#gview_"+$t.p.id).slideDown("fast");
				if($t.p.pager) {$($t.p.pager).slideDown("fast");}
				if($t.p.toppager) {$($t.p.toppager).slideDown("fast");}
				if($t.p.toolbar[0]===true) {
					if( $t.p.toolbar[1]=='both') {
						$($t.grid.ubDiv).slideDown("fast");
					}
					$($t.grid.uDiv).slideDown("fast");
				}
				if($t.p.footerrow) { $(".ui-jqgrid-sdiv","#gbox_"+$t.p.id).slideDown("fast"); }
				$(".ui-jqgrid-titlebar-close span",$t.grid.cDiv).removeClass("ui-icon-circle-triangle-s").addClass("ui-icon-circle-triangle-n");
				$t.p.gridstate = 'visible';
			}

		});
	},
	updateGridRows : function (data, rowidname, jsonreader) {
		var nm, success=false, title;
		this.each(function(){
			var t = this, vl, ind, srow, sid;
			if(!t.grid) {return false;}
			if(!rowidname) { rowidname = "id"; }
			if( data  && data.length >0 ) {
				$(data).each(function(j){
					srow = this;
					ind = t.rows.namedItem(srow[rowidname]);
					if(ind) {
						sid = srow[rowidname];
						if(jsonreader === true){
							if(t.p.jsonReader.repeatitems === true) {
								if(t.p.jsonReader.cell) {srow = srow[t.p.jsonReader.cell];}
								for (var k=0;k<srow.length;k++) {
									vl = t.formatter( sid, srow[k], k, srow, 'edit');
									title = t.p.colModel[k].title ? {"title":$.jgrid.stripHtml(vl)} : {};
									if(t.p.treeGrid===true && nm == t.p.ExpandColumn) {
										$("td:eq("+k+") > span:first",ind).html(vl).attr(title);
									} else {
										$("td:eq("+k+")",ind).html(vl).attr(title); 
									}
								}
								success = true;
								return true;
							}
						} 
						$(t.p.colModel).each(function(i){
							nm = jsonreader===true ? this.jsonmap || this.name :this.name;
							if( srow[nm] !== undefined) {
								vl = t.formatter( sid, srow[nm], i, srow, 'edit');
								title = this.title ? {"title":$.jgrid.stripHtml(vl)} : {};
								if(t.p.treeGrid===true && nm == t.p.ExpandColumn) {
									$("td:eq("+i+") > span:first",ind).html(vl).attr(title);
								} else {
									$("td:eq("+i+")",ind).html(vl).attr(title); 
								}
								success = true;
							}
						});
					}
				});
			}
		});
		return success;
	},
	filterGrid : function(gridid,p){
		p = $.extend({
			gridModel : false,
			gridNames : false,
			gridToolbar : false,
			filterModel: [], // label/name/stype/defval/surl/sopt
			formtype : "horizontal", // horizontal/vertical
			autosearch: true, // if set to false a serch button should be enabled.
			formclass: "filterform",
			tableclass: "filtertable",
			buttonclass: "filterbutton",
			searchButton: "Search",
			clearButton: "Clear",
			enableSearch : false,
			enableClear: false,
			beforeSearch: null,
			afterSearch: null,
			beforeClear: null,
			afterClear: null,
			url : '',
			marksearched: true
		},p  || {});
		return this.each(function(){
			var self = this;
			this.p = p;
			if(this.p.filterModel.length === 0 && this.p.gridModel===false) { alert("No filter is set"); return;}
			if( !gridid) {alert("No target grid is set!"); return;}
			this.p.gridid = gridid.indexOf("#") != -1 ? gridid : "#"+gridid;
			var gcolMod = $(this.p.gridid).jqGrid("getGridParam",'colModel');
			if(gcolMod) {
				if( this.p.gridModel === true) {
					var thegrid = $(this.p.gridid)[0];
					var sh;
					// we should use the options search, edittype, editoptions
					// additionally surl and defval can be added in grid colModel
					$.each(gcolMod, function (i,n) {
						var tmpFil = [];
						this.search = this.search === false ? false : true;
						if(this.editrules && this.editrules.searchhidden === true) {
							sh = true;
						} else {
							if(this.hidden === true ) {
								sh = false;
							} else {
								sh = true;
							}
						}
						if( this.search === true && sh === true) {
							if(self.p.gridNames===true) {
								tmpFil.label = thegrid.p.colNames[i];
							} else {
								tmpFil.label = '';
							}
							tmpFil.name = this.name;
							tmpFil.index = this.index || this.name;
							// we support only text and selects, so all other to text
							tmpFil.stype = this.edittype || 'text';
							if(tmpFil.stype != 'select' ) {
								tmpFil.stype = 'text';
							}
							tmpFil.defval = this.defval || '';
							tmpFil.surl = this.surl || '';
							tmpFil.sopt = this.editoptions || {};
							tmpFil.width = this.width;
							self.p.filterModel.push(tmpFil);
						}
					});
				} else {
					$.each(self.p.filterModel,function(i,n) {
						for(var j=0;j<gcolMod.length;j++) {
							if(this.name == gcolMod[j].name) {
								this.index = gcolMod[j].index || this.name;
								break;
							}
						}
						if(!this.index) {
							this.index = this.name;
						}
					});
				}
			} else {
				alert("Could not get grid colModel"); return;
			}
			var triggerSearch = function() {
				var sdata={}, j=0, v;
				var gr = $(self.p.gridid)[0], nm;
                gr.p.searchdata = {};
				if($.isFunction(self.p.beforeSearch)){self.p.beforeSearch();}
				$.each(self.p.filterModel,function(i,n){
                    nm = this.index;
					switch (this.stype) {
						case 'select' :
							v = $("select[name="+nm+"]",self).val();
							if(v) {
								sdata[nm] = v;
								if(self.p.marksearched){
									$("#jqgh_"+this.name,gr.grid.hDiv).addClass("dirty-cell");
								}
								j++;
							} else {
								if(self.p.marksearched){
									$("#jqgh_"+this.name,gr.grid.hDiv).removeClass("dirty-cell");
								}
                                try {
                                    delete gr.p.postData[this.index];
                                } catch (e) {}
							}
							break;
						default:
							v = $("input[name="+nm+"]",self).val();
							if(v) {
								sdata[nm] = v;
								if(self.p.marksearched){
									$("#jqgh_"+this.name,gr.grid.hDiv).addClass("dirty-cell");
								}
								j++;
							} else {
								if(self.p.marksearched){
									$("#jqgh_"+this.name,gr.grid.hDiv).removeClass("dirty-cell");
								}
                                try {
                                    delete gr.p.postData[this.index];
                                } catch(e) {}
							}
					}
				});
				var sd =  j>0 ? true : false;
                $.extend(gr.p.postData,sdata);
				var saveurl;
				if(self.p.url) {
					saveurl = $(gr).jqGrid("getGridParam",'url');
					$(gr).jqGrid("setGridParam",{url:self.p.url});
				}
			    $(gr).jqGrid("setGridParam",{search:sd}).trigger("reloadGrid",[{page:1}]);
				if(saveurl) {$(gr).jqGrid("setGridParam",{url:saveurl});}
				if($.isFunction(self.p.afterSearch)){self.p.afterSearch();}
			};
			var clearSearch = function(){
				var sdata={}, v, j=0;
				var gr = $(self.p.gridid)[0], nm;
				if($.isFunction(self.p.beforeClear)){self.p.beforeClear();}
				$.each(self.p.filterModel,function(i,n){
                    nm = this.index;
					v = (this.defval) ? this.defval : "";
					if(!this.stype){this.stype='text';}
					switch (this.stype) {
						case 'select' :
							var v1;
							$("select[name="+nm+"] option",self).each(function (i){
                                if(i===0) { this.selected = true; }
								if ($(this).text() == v) {
									this.selected = true;
									v1 = $(this).val();
									return false;
								}
							});
							if(v1) {
								// post the key and not the text
								sdata[nm] = v1;
								if(self.p.marksearched){
									$("#jqgh_"+this.name,gr.grid.hDiv).addClass("dirty-cell");
								}
								j++;
							} else {
								if(self.p.marksearched){
									$("#jqgh_"+this.name,gr.grid.hDiv).removeClass("dirty-cell");
								}
                                try {
                                    delete gr.p.postData[this.index];
                                } catch (e) {}
							}
							break;
						case 'text':
							$("input[name="+nm+"]",self).val(v);
							if(v) {
								sdata[nm] = v;
								if(self.p.marksearched){
									$("#jqgh_"+this.name,gr.grid.hDiv).addClass("dirty-cell");
								}
								j++;
							} else {
								if(self.p.marksearched){
									$("#jqgh_"+this.name,gr.grid.hDiv).removeClass("dirty-cell");
								}
                                try {
                                    delete gr.p.postData[this.index];
                                } catch (e) {}
							}
                            break;
					}
				});
				var sd =  j>0 ? true : false;
                $.extend(gr.p.postData,sdata);
				var saveurl;
				if(self.p.url) {
					saveurl = $(gr).jqGrid("getGridParam",'url');
					$(gr).jqGrid("setGridParam",{url:self.p.url});
				}
				$(gr).jqGrid("setGridParam",{search:sd}).trigger("reloadGrid",[{page:1}]);
				if(saveurl) {$(gr).jqGrid("setGridParam",{url:saveurl});}
				if($.isFunction(self.p.afterClear)){self.p.afterClear();}
			};
			var formFill = function(){
				var tr = document.createElement("tr");
				var tr1, sb, cb,tl,td;
				if(self.p.formtype=='horizontal'){
					$(tbl).append(tr);
				}
				$.each(self.p.filterModel,function(i,n){
					tl = document.createElement("td");
					$(tl).append("<label for='"+this.name+"'>"+this.label+"</label>");
					td = document.createElement("td");
					var $t=this;
					if(!this.stype) { this.stype='text';}
					switch (this.stype)
					{
					case "select":
						if(this.surl) {
							// data returned should have already constructed html select
							$(td).load(this.surl,function(){
								if($t.defval) { $("select",this).val($t.defval); }
								$("select",this).attr({name:$t.index || $t.name, id: "sg_"+$t.name});
								if($t.sopt) { $("select",this).attr($t.sopt); }
								if(self.p.gridToolbar===true && $t.width) {
									$("select",this).width($t.width);
								}
								if(self.p.autosearch===true){
									$("select",this).change(function(e){
										triggerSearch();
										return false;
									});
								}
							});
						} else {
							// sopt to construct the values
							if($t.sopt.value) {
								var oSv = $t.sopt.value;
								var elem = document.createElement("select");
								$(elem).attr({name:$t.index || $t.name, id: "sg_"+$t.name}).attr($t.sopt);
								var so, sv, ov;
								if(typeof oSv === "string") {
									so = oSv.split(";");
									for(var k=0; k<so.length;k++){
										sv = so[k].split(":");
										ov = document.createElement("option");
										ov.value = sv[0]; ov.innerHTML = sv[1];
										if (sv[1]==$t.defval) { ov.selected ="selected"; }
										elem.appendChild(ov);
									}
								} else if(typeof oSv === "object" ) {
									for ( var key in oSv) {
										if(oSv.hasOwnProperty(key)) {
											i++;
											ov = document.createElement("option");
											ov.value = key; ov.innerHTML = oSv[key];
											if (oSv[key]==$t.defval) { ov.selected ="selected"; }
											elem.appendChild(ov);
										}
									}
								}
								if(self.p.gridToolbar===true && $t.width) {
									$(elem).width($t.width);
								}
								$(td).append(elem);
								if(self.p.autosearch===true){
									$(elem).change(function(e){
										triggerSearch();
										return false;
									});
								}
							}
						}
						break;
					case 'text':
						var df = this.defval ? this.defval: "";
						$(td).append("<input type='text' name='"+(this.index || this.name)+"' id='sg_"+this.name+"' value='"+df+"'/>");
						if($t.sopt) { $("input",td).attr($t.sopt); }
						if(self.p.gridToolbar===true && $t.width) {
							if($.browser.msie) {
								$("input",td).width($t.width-4);
							} else {
								$("input",td).width($t.width-2);
							}
						}
						if(self.p.autosearch===true){
							$("input",td).keypress(function(e){
								var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;
								if(key == 13){
									triggerSearch();
									return false;
								}
								return this;
							});
						}
						break;
					}
					if(self.p.formtype=='horizontal'){
						if(self.p.gridToolbar===true && self.p.gridNames===false) {
							$(tr).append(td);
						} else {
							$(tr).append(tl).append(td);
						}
						$(tr).append(td);
					} else {
						tr1 = document.createElement("tr");
						$(tr1).append(tl).append(td);
						$(tbl).append(tr1);
					}
				});
				td = document.createElement("td");
				if(self.p.enableSearch === true){
					sb = "<input type='button' id='sButton' class='"+self.p.buttonclass+"' value='"+self.p.searchButton+"'/>";
					$(td).append(sb);
					$("input#sButton",td).click(function(){
						triggerSearch();
						return false;
					});
				}
				if(self.p.enableClear === true) {
					cb = "<input type='button' id='cButton' class='"+self.p.buttonclass+"' value='"+self.p.clearButton+"'/>";
					$(td).append(cb);
					$("input#cButton",td).click(function(){
						clearSearch();
						return false;
					});
				}
				if(self.p.enableClear === true || self.p.enableSearch === true) {
					if(self.p.formtype=='horizontal') {
						$(tr).append(td);
					} else {
						tr1 = document.createElement("tr");
						$(tr1).append("<td>&#160;</td>").append(td);
						$(tbl).append(tr1);
					}
				}
			};
			var frm = $("<form name='SearchForm' style=display:inline;' class='"+this.p.formclass+"'></form>");
			var tbl =$("<table class='"+this.p.tableclass+"' cellspacing='0' cellpading='0' border='0'><tbody></tbody></table>");
			$(frm).append(tbl);
			formFill();
			$(this).append(frm);
			this.triggerSearch = triggerSearch;
			this.clearSearch = clearSearch;
		});
	},
	filterToolbar : function(p){
		p = $.extend({
			autosearch: true,
			searchOnEnter : true,
			beforeSearch: null,
			afterSearch: null,
			beforeClear: null,
			afterClear: null,
			searchurl : '',
			stringResult: false,
			groupOp: 'AND'
		},p  || {});
		return this.each(function(){
			var $t = this;
			var triggerToolbar = function() {
				var sdata={}, j=0, v, nm, sopt={};
				$.each($t.p.colModel,function(i,n){
					nm = this.index || this.name;
					var so = (this.searchoptions && this.searchoptions.sopt) ? this.searchoptions.sopt[0] : "bw";
					switch (this.stype) {
						case 'select' :
							v = $("select[name="+nm+"]",$t.grid.hDiv).val();
							if(v) {
								sdata[nm] = v;
								sopt[nm] = so;
								j++;
							} else {
								try {
									delete $t.p.postData[nm];
								} catch (e) {}
							}
							break;
						case 'text':
							v = $("input[name="+nm+"]",$t.grid.hDiv).val();
							if(v) {
								sdata[nm] = v;
								sopt[nm] = so;
								j++;
							} else {
								try {
									delete $t.p.postData[nm];
								} catch (e) {}
							}
							break;
					}
				});
				var sd =  j>0 ? true : false;
				if(p.stringResult === true || $t.p.datatype == "local") {
					var ruleGroup = "{\"groupOp\":\"" + p.groupOp + "\",\"rules\":[";
					var gi=0;
					$.each(sdata,function(i,n){
						if (gi > 0) {ruleGroup += ",";}
						ruleGroup += "{\"field\":\"" + i + "\",";
						ruleGroup += "\"op\":\"" + sopt[i] + "\",";
						ruleGroup += "\"data\":\"" + n + "\"}";
						gi++;
					});
					ruleGroup += "]}";
					$.extend($t.p.postData,{filters:ruleGroup});
				} else {
					$.extend($t.p.postData,sdata);
				}
				var saveurl;
				if($t.p.searchurl) {
					saveurl = $t.p.url;
					$($t).jqGrid("setGridParam",{url:$t.p.searchurl});
				}
				var bsr = false;
				if($.isFunction(p.beforeSearch)){bsr = p.beforeSearch.call($t);}
				if(!bsr) { $($t).jqGrid("setGridParam",{search:sd}).trigger("reloadGrid",[{page:1}]); }
				if(saveurl) {$($t).jqGrid("setGridParam",{url:saveurl});}
				if($.isFunction(p.afterSearch)){p.afterSearch();}
			};
			var clearToolbar = function(trigger){
				var sdata={}, v, j=0, nm;
				trigger = (typeof trigger != 'boolean') ? true : trigger;
				$.each($t.p.colModel,function(i,n){
					v = (this.searchoptions && this.searchoptions.defaultValue) ? this.searchoptions.defaultValue : "";
					nm = this.index || this.name;
					switch (this.stype) {
						case 'select' :
							var v1;
							$("select[name="+nm+"] option",$t.grid.hDiv).each(function (i){
								if(i===0) { this.selected = true; }
								if ($(this).text() == v) {
									this.selected = true;
									v1 = $(this).val();
									return false;
								}
							});
							if (v1) {
								// post the key and not the text
								sdata[nm] = v1;
								j++;
							} else {
								try {
									delete $t.p.postData[nm];
								} catch(e) {}
							}
							break;
						case 'text':
							$("input[name="+nm+"]",$t.grid.hDiv).val(v);
							if(v) {
								sdata[nm] = v;
								j++;
							} else {
								try {
									delete $t.p.postData[nm];
								} catch (e){}
							}
							break;
					}
				});
				var sd =  j>0 ? true : false;
				if(p.stringResult === true || $t.p.datatype == "local") {
					var ruleGroup = "{\"groupOp\":\"" + p.groupOp + "\",\"rules\":[";
					var gi=0;
					$.each(sdata,function(i,n){
						if (gi > 0) {ruleGroup += ",";}
						ruleGroup += "{\"field\":\"" + i + "\",";
						ruleGroup += "\"op\":\"" + "eq" + "\",";
						ruleGroup += "\"data\":\"" + n + "\"}";
						gi++;
					});
					ruleGroup += "]}";
					$.extend($t.p.postData,{filters:ruleGroup});
				} else {
					$.extend($t.p.postData,sdata);
				}
				var saveurl;
				if($t.p.searchurl) {
					saveurl = $t.p.url;
					$($t).jqGrid("setGridParam",{url:$t.p.searchurl});
				}
				var bcv = false;
				if($.isFunction(p.beforeClear)){bcv = p.beforeClear.call($t);}
				if(!bcv) {
					if(trigger) {
						$($t).jqGrid("setGridParam",{search:sd}).trigger("reloadGrid",[{page:1}]);
					}
				}
				if(saveurl) {$($t).jqGrid("setGridParam",{url:saveurl});}
				if($.isFunction(p.afterClear)){p.afterClear();}
			};
			var toggleToolbar = function(){
				var trow = $("tr.ui-search-toolbar",$t.grid.hDiv);
				if(trow.css("display")=='none') { trow.show(); }
				else { trow.hide(); }
			};
			// create the row
			function bindEvents(selector, events) {
				var jElem = $(selector);
				if (jElem[0]) {
				    jQuery.each(events, function() {
				        if (this.data !== undefined) {
				            jElem.bind(this.type, this.data, this.fn);
				        } else {
				            jElem.bind(this.type, this.fn);
				        }
				    });
				}
			}
			var tr = $("<tr class='ui-search-toolbar' role='rowheader'></tr>");
			var timeoutHnd;
			$.each($t.p.colModel,function(i,n){
				var cm=this, thd , th, soptions,surl,self;
				th = $("<th role='columnheader' class='ui-state-default ui-th-column ui-th-"+$t.p.direction+"'></th>");
				thd = $("<div style='width:100%;position:relative;height:100%;padding-right:0.3em;'></div>");
				if(this.hidden===true) { $(th).css("display","none");}
				this.search = this.search === false ? false : true;
				if(typeof this.stype == 'undefined' ) {this.stype='text';}
				soptions = $.extend({},this.searchoptions || {});
				if(this.search){
					switch (this.stype)
					{
					case "select":
						surl = this.surl || soptions.dataUrl;
						if(surl) {
							// data returned should have already constructed html select
							// primitive jQuery load
							self = thd;
							$.ajax($.extend({
								url: surl,
								dataType: "html",
								complete: function(res,status) {
									if(soptions.buildSelect !== undefined) {
										var d = soptions.buildSelect(res);
										if (d) { $(self).append(d); }
									} else {
										$(self).append(res.responseText);
									}
									if(soptions.defaultValue) { $("select",self).val(soptions.defaultValue); }
									$("select",self).attr({name:cm.index || cm.name, id: "gs_"+cm.name});
									if(soptions.attr) {$("select",self).attr(soptions.attr);}
									$("select",self).css({width: "100%"});
									// preserve autoserch
									if(soptions.dataInit !== undefined) { soptions.dataInit($("select",self)[0]); }
									if(soptions.dataEvents !== undefined) { bindEvents($("select",self)[0],soptions.dataEvents); }
									if(p.autosearch===true){
										$("select",self).change(function(e){
											triggerToolbar();
											return false;
										});
									}
									res=null;
								}
							}, $.jgrid.ajaxOptions, $t.p.ajaxSelectOptions || {} ));
						} else {
							var oSv;
							if(cm.searchoptions && cm.searchoptions.value) {
								oSv = cm.searchoptions.value;
							} else if(cm.editoptions && cm.editoptions.value) {
								oSv = cm.editoptions.value;
							}
							if (oSv) {	
								var elem = document.createElement("select");
								elem.style.width = "100%";
								$(elem).attr({name:cm.index || cm.name, id: "gs_"+cm.name});
								var so, sv, ov;
								if(typeof oSv === "string") {
									so = oSv.split(";");
									for(var k=0; k<so.length;k++){
										sv = so[k].split(":");
										ov = document.createElement("option");
										ov.value = sv[0]; ov.innerHTML = sv[1];
										elem.appendChild(ov);
									}
								} else if(typeof oSv === "object" ) {
									for ( var key in oSv) {
										if(oSv.hasOwnProperty(key)) {
											ov = document.createElement("option");
											ov.value = key; ov.innerHTML = oSv[key];
											elem.appendChild(ov);
										}
									}
								}
								if(soptions.defaultValue) { $(elem).val(soptions.defaultValue); }
								if(soptions.attr) {$(elem).attr(soptions.attr);}
								if(soptions.dataInit !== undefined) { soptions.dataInit(elem); }
								if(soptions.dataEvents !== undefined) { bindEvents(elem, soptions.dataEvents); }
								$(thd).append(elem);
								if(p.autosearch===true){
									$(elem).change(function(e){
										triggerToolbar();
										return false;
									});
								}
							}
						}
						break;
					case 'text':
						var df = soptions.defaultValue ? soptions.defaultValue: "";
						$(thd).append("<input type='text' style='width:95%;padding:0px;' name='"+(cm.index || cm.name)+"' id='gs_"+cm.name+"' value='"+df+"'/>");
						if(soptions.attr) {$("input",thd).attr(soptions.attr);}
						if(soptions.dataInit !== undefined) { soptions.dataInit($("input",thd)[0]); }
						if(soptions.dataEvents !== undefined) { bindEvents($("input",thd)[0], soptions.dataEvents); }
						if(p.autosearch===true){
							if(p.searchOnEnter) {
								$("input",thd).keypress(function(e){
									var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;
									if(key == 13){
										triggerToolbar();
										return false;
									}
									return this;
								});
							} else {
								$("input",thd).keydown(function(e){
									var key = e.which;
									switch (key) {
										case 13:
											return false;
										case 9 :
										case 16:
										case 37:
										case 38:
										case 39:
										case 40:
										case 27:
											break;
										default :
											if(timeoutHnd) { clearTimeout(timeoutHnd); }
											timeoutHnd = setTimeout(function(){triggerToolbar();},500);
									}
								});
							}
						}
						break;
					}
				}
				$(th).append(thd);
				$(tr).append(th);
			});
			$("table thead",$t.grid.hDiv).append(tr);
			this.triggerToolbar = triggerToolbar;
			this.clearToolbar = clearToolbar;
			this.toggleToolbar = toggleToolbar;
		});
	}
});
})(jQuery);;(function ($) {
    // DOM
    if (!Array.prototype.sum)
        Array.prototype.sum = function () {
            for (var i = 0, l = this.length, sum = 0; i < l; sum += this[i++]);
            return sum;
        };

    if (!Array.prototype.max)
        Array.prototype.max = function () {
            if (this.length == 0)
                return null;

            var max = this[0];
            if (this.length == 1)
                return max;

            for (var i = 1, l = this.length; i < l; i++)
                if (this[i] > max)
                    max = this[i];

            return max;
        };

    if (!Array.prototype.min)
        Array.prototype.min = function () {
            if (this.length == 0)
                return null;

            var min = this[0];
            if (this.length == 1)
                return min;

            for (var i = 1, l = this.length; i < l; i++)
                if (this[i] < min)
                    min = this[i];

            return min;
        };

    // Global
    $.extend({
        sum: function (elems, callback, arg) {
            return this.map(elems, callback, arg).sum();
        },
        max: function (elems, callback, arg) {
            return this.map(elems, callback, arg).max();
        },
        min: function (elems, callback, arg) {
            return this.map(elems, callback, arg).min();
        }
    });

    // Element
    $.fn.sum = function (callback) {
        return $.sum(this, function (elem, i) {
            return callback.call(elem, i, elem);
        });
    };

    $.fn.max = function (callback) {
        return $.max(this, function (elem, i) {
            return callback.call(elem, i, elem);
        });
    };

    $.fn.min = function (callback) {
        return $.min(this, function (elem, i) {
            return callback.call(elem, i, elem);
        });
    };
})(jQuery);;$.fn.extend({
    /*
    *  
    * The toolbar has the following properties
    *	id of top toolbar: t_<tablename>
    *	id of bottom toolbar: tb_<tablename>
    *	class of toolbar: ui-userdata
    * elem is the toolbar name to which button needs to be added. This can be 
    *		#t_tablename - if button needs to be added to the top toolbar
    *		#tb_tablename - if button needs to be added to the bottom toolbar
    */
    toolbarButtonAdd: function(elem, p) {
        p = $.extend({
            caption: "newButton",
            title: '',
            buttonicon: 'ui-icon-newwin',
            onClickButton: null,
            position: "last"
        }, p || {});
        var $elem = $(elem);
        var tableString = "<table style='float:left;table-layout:auto;' cellspacing=\"0\" cellpadding=\"0\" border=\"0\" class='ui-toolbar-table'>";
        tableString += "<tbody> <tr></tr></table>";
        //console.log("In toolbar button add method");
        /* 
        * Step 1: check whether a table is already added. If not add
        * Step 2: If there is no table already added then add a table
        * Step 3: Make the element ready for addition to the table 
        * Step 4: Check the position and corresponding add the element
        * Step 5: Add other properties 
        */
        //step 1 
        return this.each(function() {
            if (!this.grid) { return; }
            if (elem.indexOf("#") != 0) {
                elem = "#" + elem;
            }
            //step 2
            if ($(elem).children('table').length === 0) {
                $(elem).append(tableString);
            }
            //step 3
            var tbd = $("<td style=\"padding-left:1px;padding-right:1px\"></td>");
            $(tbd).addClass('ui-toolbar-button ui-corner-all').append("<div class='ui-toolbar-div'><span class='" + p.buttonicon + "'></span>" + "<span>" + p.caption + "</span>" + "</div>").attr("title", p.title || "")
			.click(function(e) {
			    if ($.isFunction(p.onClickButton)) { p.onClickButton(); }
			    return false;
			})
			.hover(
				function() { $(this).addClass("ui-state-hover"); },
				function() { $(this).removeClass("ui-state-hover"); }
			);
            if (p.id) { $(tbd).attr("id", p.id); }
            if (p.align) { $(elem).attr("align", p.align); }
            var findnav = $(elem).children('table');
            if (p.position === 'first') {
                if ($(findnav).find('td').length === 0) {
                    $("tr", findnav).append(tbd);
                } else {
                    $("tr td:eq(0)", findnav).before(tbd);
                }
            } else {
                //console.log("not first");
                $("tr", findnav).append(tbd);
            }
        });
    }
});;/// <reference path="../../../lib/jquery-1.2.6.js" />
/*
	Masked Input plugin for jQuery
	Copyright (c) 2007-2009 Josh Bush (digitalbush.com)
	Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license) 
	Version: 1.2.2 (03/09/2009 22:39:06)
*/
(function($) {
    var pasteEventName = ($.browser.msie ? 'paste' : 'input') + ".mask";
    var iPhone = (window.orientation != undefined);

    $.mask = {
        //Predefined character definitions
        definitions: {
            '9': "[0-9]",
            'a': "[A-Za-z]",
            '*': "[A-Za-z0-9]"
        }
    };

    $.fn.extend({
        //Helper Function for Caret positioning
        caret: function(begin, end) {
            if (this.length == 0) return;
            if (typeof begin == 'number') {
                end = (typeof end == 'number') ? end : begin;
                return this.each(function() {
                    if (this.setSelectionRange) {
                        this.focus();
                        this.setSelectionRange(begin, end);
                    } else if (this.createTextRange) {
                        var range = this.createTextRange();
                        range.collapse(true);
                        range.moveEnd('character', end);
                        range.moveStart('character', begin);
                        range.select();
                    }
                });
            } else {
                if (this[0].setSelectionRange) {
                    begin = this[0].selectionStart;
                    end = this[0].selectionEnd;
                } else if (document.selection && document.selection.createRange) {
                    var range = document.selection.createRange();
                    begin = 0 - range.duplicate().moveStart('character', -100000);
                    end = begin + range.text.length;
                }
                return { begin: begin, end: end };
            }
        },
        unmask: function() { return this.trigger("unmask"); },
        mask: function(mask, settings) {
            var tests = [];
            if (!mask && this.length > 0) {
                var input = $(this[0]);
                tests = input.data("tests");
                return $.map(input.data("buffer"), function(c, i) {
                    return tests[i] ? c : null;
                }).join('');
            }
            settings = $.extend({
                placeholder: "_",
                completed: null
            }, settings);

            var defs = $.mask.definitions;
            tests = [];
            var partialPosition = mask.length;
            var firstNonMaskPos = null;
            var len = mask.length;

            $.each(mask.split(""), function(i, c) {
                if (c == '?') {
                    len--;
                    partialPosition = i;
                } else if (defs[c]) {
                    tests.push(new RegExp(defs[c]));
                    if (firstNonMaskPos == null)
                        firstNonMaskPos = tests.length - 1;
                } else {
                    tests.push(null);
                }
            });

            return this.each(function() {
                var input = $(this);
                var buffer = $.map(mask.split(""), function(c, i) { if (c != '?') return defs[c] ? settings.placeholder : c });
                var ignore = false;  			//Variable for ignoring control keys
                var focusText = input.val();

                input.data("buffer", buffer).data("tests", tests);

                function seekNext(pos) {
                    while (++pos <= len && !tests[pos]);
                    return pos;
                };

                function shiftL(pos) {
                    while (!tests[pos] && --pos >= 0);
                    for (var i = pos; i < len; i++) {
                        if (tests[i]) {
                            buffer[i] = settings.placeholder;
                            var j = seekNext(i);
                            if (j < len && tests[i].test(buffer[j])) {
                                buffer[i] = buffer[j];
                            } else
                                break;
                        }
                    }
                    writeBuffer();
                    input.caret(Math.max(firstNonMaskPos, pos));
                };

                function shiftR(pos) {
                    for (var i = pos, c = settings.placeholder; i < len; i++) {
                        if (tests[i]) {
                            var j = seekNext(i);
                            var t = buffer[i];
                            buffer[i] = c;
                            if (j < len && tests[j].test(t))
                                c = t;
                            else
                                break;
                        }
                    }
                };

                function keydownEvent(e) {
                    var pos = $(this).caret();
                    var k = e.keyCode;
                    ignore = (k < 16 || (k > 16 && k < 32) || (k > 32 && k < 41));

                    //delete selection before proceeding
                    if ((pos.begin - pos.end) != 0 && (!ignore || k == 8 || k == 46))
                        clearBuffer(pos.begin, pos.end);

                    //backspace, delete, and escape get special treatment
                    if (k == 8 || k == 46 || (iPhone && k == 127)) {//backspace/delete
                        shiftL(pos.begin + (k == 46 ? 0 : -1));
                        return false;
                    } else if (k == 27) {//escape
                        input.val(focusText);
                        input.caret(0, checkVal());
                        return false;
                    }
                };

                function keypressEvent(e) {
                    if (ignore) {
                        ignore = false;
                        //Fixes Mac FF bug on backspace
                        return (e.keyCode == 8) ? false : null;
                    }
                    e = e || window.event;
                    var k = e.charCode || e.keyCode || e.which;
                    var pos = $(this).caret();

                    if (e.ctrlKey || e.altKey || e.metaKey) {//Ignore
                        return true;
                    } else if ((k >= 32 && k <= 125) || k > 186) {//typeable characters
                        var p = seekNext(pos.begin - 1);
                        if (p < len) {
                            var c = String.fromCharCode(k);
                            if (tests[p].test(c)) {
                                shiftR(p);
                                buffer[p] = c;
                                writeBuffer();
                                var next = seekNext(p);
                                $(this).caret(next);
                                if (settings.completed && next == len)
                                    settings.completed.call(input);
                            }
                        }
                    }
                    return false;
                };

                function clearBuffer(start, end) {
                    for (var i = start; i < end && i < len; i++) {
                        if (tests[i])
                            buffer[i] = settings.placeholder;
                    }
                };

                function writeBuffer() { return input.val(buffer.join('')).val(); };

                function checkVal(allow) {
                    //try to place characters where they belong
                    var test = input.val();
                    var lastMatch = -1;
                    for (var i = 0, pos = 0; i < len; i++) {
                        if (tests[i]) {
                            buffer[i] = settings.placeholder;
                            while (pos++ < test.length) {
                                var c = test.charAt(pos - 1);
                                if (tests[i].test(c)) {
                                    buffer[i] = c;
                                    lastMatch = i;
                                    break;
                                }
                            }
                            if (pos > test.length)
                                break;
                        } else if (buffer[i] == test[pos] && i != partialPosition) {
                            pos++;
                            lastMatch = i;
                        }
                    }
                    if (!allow && lastMatch + 1 < partialPosition) {
                        input.val("");
                        clearBuffer(0, len);
                    } else if (allow || lastMatch + 1 >= partialPosition) {
                        writeBuffer();
                        if (!allow) input.val(input.val().substring(0, lastMatch + 1));
                    }
                    return (partialPosition ? i : firstNonMaskPos);
                };

                input.unbind('keypress');
                input.unbind('keydown');
                input.unbind('keyup');
                input.unbind('change');

                if (!input.attr("readonly"))
                    input
					.one("unmask", function() {
					    input
							.unbind(".mask")
							.removeData("buffer")
							.removeData("tests");
					})
					.bind("focus.mask", function() {
					    input.select();
					})
					.bind("blur.mask", function(events) {
					    input.dateValidate({ mask: "dd/mm/yy", name: input.attr("errorName") });
					})
					.bind("change.mask", function(events) {
					   input.dateValidate({ mask: "dd/mm/yy", name: input.attr("errorName") });
					});

                checkVal(); //Perform initial check for existing values
            });
        }
    });
})(jQuery);;//*******************************************************************************************************
//  
//  File:        jquery.inputConstraints.js
//  Author:      Vladimir Nechypurenko
//  Description: Input constraint functions: Numeric, Alpha, AlphaNumeric fields
//
//*******************************************************************************************************
(function($) {
    // This function (numVal) is used in the form serialization overrided method, if element has class "numeric"
    // or can be called directlya
    $.fn.getNumVal = function() {
        var val = 0;
        if ($(this).length > 0 && $(this).get(0).p) {
            val = $(this).get(0).p.getValue($(this).val());
        }
        return val;
    };

    $.fn.ToNumVal = function(value) {
        var val = 0;
        if ($(this).length > 0 && $(this).get(0).p) {
            val = $(this).get(0).p.getValue(value);
        }
        return val;
    },

    $.fn.setNumVal = function(value) {
        if ($(this).length > 0 && $(this).get(0).p) {
            $(this).get(0).p.setValue($(this), value);
        }
    };

    $.fn.alphanumeric = function(p) {
        p = $.extend({
            ichars: "!@#$%^&*()+=[]\\\';/{}|\":<>?~`- ",
            nchars: "",
            allow: ""
        }, p);

        return this.each
			(
				function() {

				    if (p.nocaps) p.nchars += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
				    if (p.allcaps) p.nchars += "abcdefghijklmnopqrstuvwxyz";
				    var decIndex = p.format.indexOf(".");
				    if (decIndex < 0) {
				        p.ichars += ".";
				    }
				    else {
				        p.decLength = p.format.substring(decIndex + 1);
				    }

				    if (p.decLength) {
				        p.decLength = p.decLength.length;
				    }
				    else {
				        p.decLength = 0;
				    }


				    s = p.allow.split('');
				    for (i = 0; i < s.length; i++) if (p.ichars.indexOf(s[i]) != -1) s[i] = "\\" + s[i];
				    p.allow = s.join('|');

				    var reg = new RegExp(p.allow, 'gi');
				    var ch = p.ichars + p.nchars;
				    ch = ch.replace(reg, '');

				    p.setValue = function(obj, value) {
				        if (isNaN(value)) {
				            obj.val($.formatNumber(0, { format: p.format }));
				        }
				        else {
				            obj.val($.formatNumber(value, { format: p.format }));
				        }
				    }

				    p.getValue = function(value) {
				        if (value && value != "") {
				            if (typeof (value) == "string") value = value.trim();
				            value = value.replace(new RegExp(regExEscape(p.thousandsChar), 'g'), '');
				            return new Number(value);
				        }
				        else {
				            return new Number(0);
				        }
				    };

				    function checkKey(e) {
				        if (e.ctrlKey) return;
				        if (!e.charCode) {
				            code = e.keyCode;
				            k = String.fromCharCode(e.keyCode);
				        }
				        else {
				            code = e.charCode;
				            k = String.fromCharCode(e.charCode);
				        }

				        if (code == 46 && k !== p.decimalChar) return;

				        var strValue = $(this).val();
				        if (((code == 110 || code == 190) && p.decimalChar == ".") || (k == p.decimalChar && code != 46)) {
				            var value = strValue;
				            if (value.indexOf(p.decimalChar) >= 0) {
				                e.preventDefault();
				                return;
				            }
				        }
				        else {
				            switch (code) {
				                case 38: // Up Arrow
				                    var value = p.getValue(strValue);
				                    if (p.maxValue && (p.maxValue <= value)) {
				                        e.preventDefault();
				                        return;
				                    }

				                    p.setValue($(this), value + 1);
				                    return;
				                case 40: // Down Arrow
				                    var value = p.getValue(strValue);
				                    if (p.minValue && (p.minValue >= value)) {
				                        e.preventDefault();
				                        return;
				                    }

				                    p.setValue($(this), value - 1);
				                    return;
				                default: if (isControlKey(code) || isNumericKey(code)) {
				                        return;
				                    }
				            }
				        }

				        if (code != 110 && ch.indexOf(k) != -1) e.preventDefault();
				        if (e.ctrlKey && k == 'v') e.preventDefault();

				        var value = p.getValue(strValue);
				        if (p.minValue && (p.minValue >= value)) {
				            e.preventDefault();
				            return;
				        }

				        if (p.maxValue && (p.maxValue <= value)) {
				            e.preventDefault();
				            return;
				        }
				    }

				    $(this).bind({ keydown: checkKey });

				    $(this).bind({
				        blur: function() {
				            var _p = $(this).get(0).p;
				            var value = _p.getValue($(this).val());
				            if (_p.minValue && (_p.minValue >= value)) {
				                value = _p.minValue;
				            }
				            if (_p.maxValue && (_p.maxValue <= value)) {
				                value = _p.maxValue;
				            }
				            _p.setValue($(this), value);
				        }
				    });
				    this.p = p;
				}
			);

    };

    $.fn.numeric = function(p) {

        var az = "abcdefghijklmnopqrstuvwxyz";
        az += az.toUpperCase();

        p = $.extend({
            nchars: az,
            decimalChar: '.',
            thousandsChar: '',
            format: '#0'
        }, p);

        if (p.maxValue) {
            p.maxValue = new Number(p.maxValue);
        }

        if (p.minValue) {
            p.minValue = new Number(p.minValue);
        }

        return this.each(function() {
            $(this).alphanumeric(p);
        }
		);

    };

    $.fn.alpha = function(p) {

        var nm = "1234567890";

        p = $.extend({
            nchars: nm
        }, p);

        return this.each(function() {
            $(this).alphanumeric(p);
        }
		);

    };

    function isControlKey(keyCode) {
        var navKeys = [188, 190, 191, 192, 219, 220, 221, 222];
        return (keyCode <= 47 && keyCode != 32)
            || (keyCode >= 91 && keyCode <= 95)
            || (keyCode >= 112 && !jQuery.inArray(navKeys, keyCode))
    }

    function isNumericKey(keyCode) {
        // 48 - 57 are numerical key codes for key pad nubers, 96 - 105 are numerical key codes for num pad numbers.
        return (keyCode >= 48 && keyCode <= 57) || (keyCode >= 96 && keyCode <= 105);
    }

    var REGEX_ESCAPE = new RegExp('(\\' + ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'].join('|\\') + ')', 'g');
    function regExEscape(val) {
        return val.replace(REGEX_ESCAPE, '\\$1');
    }
    /**
    * Formats a number according to a specified format string.
    *
    * Available options:
    * - format        : The format string to format the number with. The number will be rounded to the number of digits specified.
    *                   0 = A significant digit (always included).
    *                   # = Digit will be used if applicable.
    *                   . = Characters to the right of this will be interpreted as decimal places.
    *                   , = Must occur to the left of the decimal (if there is one). If included, indicates the thousands separator will be included every 3 digits on the left of the decimal.
    * - decimalChar   : The decimal character to use when formatting the number. Defaults to '.'.
    * - thousandsChar : The thousands separator character to use when formatting the number. Defaults to ','.
    *
    * Examples:
    *
    * Given the number "1000.0562":
    * --------------------
    * format  | result
    * --------------------
    *       0 | 1000
    *     0.0 | 1000.1
    * #,000.0 | 1,000.1
    *    0.## | 1000.06
    * #,0.00# | 1,000.056
    * 0.00### | 1000.0562
    * 0.00000 | 1000.05620
    **/
    $.formatNumber = function(num, options) {
        if (typeof options === 'string')
            options = { format: options };

        options = $.extend({ format: null, decimalChar: '.', thousandsChar: ',' }, options);

        // No need to continue if no format string is specified.
        if (typeof options.format !== 'string' || options.format.length <= 0)
            return num.toString();

        // Default to '.' as the decimal character.
        options.decimalChar = typeof options.decimalChar !== 'string' || options.decimalChar.length <= 0 ? '.' : options.decimalChar;

        // Default to ',' as the thousands separator.
        options.thousandsChar = typeof options.thousandsChar !== 'string' ? ',' : options.thousandsChar;

        if (options.decimalChar.length > 1)
            throw 'NumberFormatException: Can not have multiple characters as the decimal character.';

        if (options.thousandsChar.length > 1)
            throw 'NumberFormatException: Can not have multiple characters as the thousands separator.';

        var v_dec_index = options.format.indexOf(options.decimalChar);
        if (v_dec_index >= 0 && options.format.indexOf(options.decimalChar, v_dec_index + 1) >= 0)
            throw 'NumberFormatException: Format string has multiple decimal characters.';

        // Convert the current numeric value to a string, removing the negative sign if the number is negative.
        var v_num_as_string = num.toString().replace(/-/g, ''),

        // Strip out all of the characters that are not formatting characters. Regonized formatting characters are: '0', '#', and the decimal character.
        v_clean_format = options.format.replace(new RegExp('[^0#' + regExEscape(options.decimalChar) + ']', 'g'), ''),

        // Split the numerical value into it's int and decimal parts.
        v_num_parts = v_num_as_string.indexOf(options.decimalChar) < 0 ? [v_num_as_string, ''] : v_num_as_string.split(options.decimalChar),

        // Split the format string into it's respective integer and decimal parts.
        v_format_parts = v_dec_index < 0 ? [v_clean_format, ''] : v_clean_format.split(options.decimalChar);

        // If the int part is zero, then we may not need to have any int part depending on the format string.
        if (parseInt(num) === 0)
            v_num_parts[0] = v_format_parts[0].indexOf('0') >= 0 ? '0' : '';

        // Otherwise no processing is needed, we already have our int part.

        if (options.format.indexOf(options.thousandsChar) >= 0 && v_num_parts[0].length > 3) {
            // If we need to include the thousands separator, then we can break the int part into chunks of three characters each (the first chunk does not need
            // to be 3 characters), then join them together separated by the thousands separator.
            var v_int_thousands_array = [],
            j = v_num_parts[0].length,
            m = Math.floor(j / 3),
            n = v_num_parts[0].length % 3 || 3; // If n is zero, an infinite loop will occur, so we have to make sure it is not zero.

            for (var i = 0; i < j; i += n) {
                if (i != 0)
                    n = 3;

                v_int_thousands_array[v_int_thousands_array.length] = v_num_parts[0].substr(i, n);
                m -= 1;
            }

            v_num_parts[0] = v_int_thousands_array.join(options.thousandsChar);
        }

        // Do we have a decimal part to format?
        if (v_format_parts[1].length > 0) {
            if (v_num_parts[1].length > 0) {
                // Get character arrays of the decimal format string and decimal part of the numerical value.
                var v_format_array = v_format_parts[1].split(''), v_dec_part_array = v_num_parts[1].split('');

                // Compile the final formatted decimal part. If there are any # symbols left, we can simply remove them.
                for (var i = 0; i < v_dec_part_array.length; i++) {
                    if (i >= v_format_array.length)
                        break;

                    v_format_array[i] = v_dec_part_array[i];
                }

                v_num_parts[1] = v_format_array.join('').replace(/#/g, '');
            }

            // Else if there is no decimal part of the actual number, but the format string specified says to include decimals, then we need to
            // make sure that the number of decimal places specified are included.
            else {
                var v_index = 0;
                v_num_parts[1] = '';
                while (v_format_parts[1].charAt(v_index) === '0') {
                    v_num_parts[1] += '0';
                    v_index++;
                }
            }
        }
        else
            v_num_parts[1] = ''; // There is no decimal part specified in the format string.

        // Compile the full formatted numerical string.
        var v_retval = (v_num_parts[1].length <= 0) ? v_num_parts[0] : v_num_parts[0] + options.decimalChar + v_num_parts[1];

        // If the number is negative, then add the negative sign.
        if (num < 0)
            v_retval = '-' + v_retval;

        // Replace the number portion of the format string with the compiled result
        return options.format.replace(new RegExp('[0|#|' + regExEscape(options.thousandsChar) + '|' + regExEscape(options.decimalChar) + ']+'), v_retval);
    }
})(jQuery);
;/**
 * @depends nothing
 * @name core.console
 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
 */

/**
 * Console Emulator
 * We have to convert arguments into arrays, and do this explicitly as webkit (chrome) hates function references, and arguments cannot be passed as is
 * @version 1.0.2
 * @date August 21, 2010
 * @since 0.1.0-dev, December 01, 2009
 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
 */
if ( typeof window.console !== 'object' || typeof window.console.emulated === 'undefined' ) {
	// Check to see if console exists
	if ( typeof window.console !== 'object' || !(typeof window.console.log === 'function' || typeof window.console.log === 'object') ) {
		// Console does not exist
		window.console = {};
		window.console.log = window.console.debug = window.console.warn = window.console.trace = function(){};
		window.console.error = function(){
			var msg = "An error has occured. More information will be available in the console log.";
			for ( var i = 0; i < arguments.length; ++i ) {
				if ( typeof arguments[i] !== 'string' ) { break; }
				msg += "\n"+arguments[i];
			}
			if ( typeof Error !== 'undefined' ) {
				throw new Error(msg);
			}
			else {
				throw(msg);
			}
		};
	}
	else {
		// Console is object, and log does exist
		// Check Debug
		if ( typeof window.console.debug === 'undefined' ) {
			window.console.debug = function(){
				var arr = ['console.debug:']; for(var i = 0; i < arguments.length; i++) { arr.push(arguments[i]); };
			    window.console.log.apply(window.console, arr);
			};
		}
		// Check Warn
		if ( typeof window.console.warn === 'undefined' ) {
			window.console.warn = function(){
				var arr = ['console.warn:']; for(var i = 0; i < arguments.length; i++) { arr.push(arguments[i]); };
			    window.console.log.apply(window.console, arr);
			};
		} 
		// Check Error
		if ( typeof window.console.error === 'undefined' ) {
			window.console.error = function(){
				var arr = ['console.error']; for(var i = 0; i < arguments.length; i++) { arr.push(arguments[i]); };
			    window.console.log.apply(window.console, arr);
			};
		}
		// Check Trace
		if ( typeof window.console.trace === 'undefined' ) {
			window.console.trace = function(){
			    window.console.error.apply(window.console, ['console.trace does not exist']);
			};
		}
	}
	// We have been emulated
	window.console.emulated = true;
}
/**
 * @depends jquery, core.console
 * @name jquery.history
 * @package jquery-history {@link http://www.balupton/projects/jquery-history}
 */

// Start of our jQuery Plugin
(function($) {	// Create our Plugin function, with $ as the argument (we pass the jQuery object over later)
    // More info: http://docs.jquery.com/Plugins/Authoring#Custom_Alias

    /**
    * jQuery History
    * @version 1.5.0
    * @date August 31, 2010
    * @since 0.1.0-dev, July 24, 2008
    * @package jquery-history {@link http://www.balupton/projects/jquery-history}
    * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
    * @copyright (c) 2008-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
    * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
    */
    // Check our class exists
    if (!($.History || false)) {
        // Declare our class
        $.History = {
            // Our Plugin definition

            // -----------------
            // Options

            options: {
                debug: false
            },

            // -----------------
            // Variables

            state: '',
            $window: null,
            $iframe: null,
            handlers: {
                generic: [],
                specific: {}
            },

            // --------------------------------------------------
            // Functions

            /**
            * Extract the Hash from a URL
            * @param {String} hash
            */
            extractHash: function(url) {
                // Extract the hash
                if (url != undefined && typeof (url.replace) == "function") {
                    var hash = url
					.replace(/^[^#]*#/, '')	/* strip anything before the first anchor */
					.replace(/^#+|#+$/, '');
                }
                // Return hash
                return hash;
            },

            /**
            * Get the current state of the application
            */
            getState: function() {
                var History = $.History;

                // Get the current state
                return History.state;
            },
            /**
            * Set the current state of the application
            * @param {String} hash
            */
            setState: function(state) {
                var History = $.History;

                // Format the state
                state = History.extractHash(state)

                // Apply the state
                History.state = state;

                // Return the state
                return History.state;
            },

            /**
            * Get the current hash of the browser
            */
            getHash: function() {
                var History = $.History;

                // Get the hash
                var hash = History.extractHash(window.location.hash || location.hash);

                // Return the hash
                return hash;
            },

            /**
            * Set the current hash of the browser and iframe if present
            * @param {String} hash
            */
            setHash: function(hash) {
                var History = $.History;

                // Prepare hash
                hash = History.extractHash(hash);

                // Write hash
                if (typeof window.location.hash !== 'undefined') {
                    if (window.location.hash !== hash) {
                        window.location.hash = hash;
                    }
                } else if (location.hash !== hash) {
                    location.hash = hash;
                }

                // Done
                return hash;
            },

            /**
            * Go to the specific state - does not force a history entry like setHash
            * @param {String} to
            */
            go: function(to) {
                var History = $.History;

                // Format
                to = History.extractHash(to);

                // Get current
                var hash = History.getHash(),
					state = History.getState();

                // Has the hash changed
                if (to !== hash) {
                    // Yes, update the hash
                    // And wait for the next automatic fire
                    History.setHash(to);
                } else {
                    // Hash the state changed?
                    if (to !== state) {
                        // Yes, Update the state
                        History.setState(to);
                    }

                    // Trigger our change
                    History.trigger();
                }

                // Done
                return true;
            },

            /**
            * Handle when the hash has changed
            * @param {Event} e
            */
            hashchange: function(e) {
                var History = $.History;

                // Get Hash
                var hash = History.getHash();
                // Handle the new hash
                History.go(hash);

                // All done
                return true;
            },

            /**
            * Bind a handler to a hash
            * @param {Object} state
            * @param {Object} handler
            */
            bind: function(state, handler) {
                var History = $.History;

                // Handle
                if (handler) {
                    // We have a state specific handler
                    // Prepare
                    if (typeof History.handlers.specific[state] === 'undefined') {
                        // Make it an array
                        History.handlers.specific[state] = [];
                    }
                    // Push new handler
                    History.handlers.specific[state].push(handler);
                }
                else {
                    // We have a generic handler
                    handler = state;
                    History.handlers.generic.push(handler);
                }

                // Done
                return true;
            },

            /**
            * Trigger a handler for a state
            * @param {String} state
            */
            trigger: function(state) {
                var History = $.History;

                // Prepare
                if (typeof state === 'undefined') {
                    // Use current
                    state = History.getState();
                }
                var i, n, handler, list;

                // Fire specific
                if (typeof History.handlers.specific[state] !== 'undefined') {
                    // We have specific handlers
                    list = History.handlers.specific[state];
                    for (i = 0, n = list.length; i < n; ++i) {
                        // Fire the specific handler
                        handler = list[i];
                        handler(state);
                    }
                }

                // Fire generics
                list = History.handlers.generic;
                for (i = 0, n = list.length; i < n; ++i) {
                    // Fire the specific handler
                    handler = list[i];
                    handler(state);
                }

                // Done
                return true;
            },

            // --------------------------------------------------
            // Constructors

            /**
            * Construct our application
            */
            construct: function() {
                var History = $.History;

                // Modify the document
                $(document).ready(function() {
                    // Prepare the document
                    History.domReady();
                });

                // Done
                return true;
            },

            /**
            * Configure our application
            * @param {Object} options
            */
            configure: function(options) {
                var History = $.History;

                // Set options
                History.options = $.extend(History.options, options);

                // Done
                return true;
            },

            domReadied: false,
            domReady: function() {
                var History = $.History;

                // Runonce
                if (History.domRedied) {
                    return;
                }
                History.domRedied = true;

                // Define window
                History.$window = $(window);

                // Apply the hashchange function
                History.$window.bind('hashchange', this.hashchange);

                // Force hashchange support for all browsers
                setTimeout(History.hashchangeLoader, 200);

                // All done
                return true;
            },

            /**
            * Determines whether or not our browser has native support for the required onhashchange event.
            * Unfortunately we have to test against a known range of browsers, as doing a automatic test would require testing the onhashchange functionality
            * which would require a state change that we do not want.
            * @param {Object} browser [optional]
            */
            nativeSupport: function(browser) {
                // Prepare
                browser = browser || $.browser;
                var browserVersion = browser.version+'',
					browserVersionInt = parseInt(browserVersion, 10),
					browserVersionParts = browserVersion.split(/[^0-9]/g),
					browserVersionPartsOne = parseInt(browserVersionParts[0], 10),
					browserVersionPartsTwo = parseInt(browserVersionParts[1], 10),
					browserVersionPartsThree = parseInt(browserVersionParts[2], 10),
					nativeSupport = false;

                // Determine if we are running under a browser which has nativeSupport for the onhashchange event
                // >= MSIE 8
                if ((browser.msie || false) && browserVersionInt >= 8) {
                    nativeSupport = true;
                }
                // >= Webkit 528
                else if ((browser.webkit || false) && browserVersionInt >= 528) {
                    nativeSupport = true;
                }
                // >= Gecko 1.9.2.x
                else if ((browser.mozilla || false)) {
                    // > Gecko 1
                    if (browserVersionPartsOne > 1) {
                        nativeSupport = true;
                    }
                    // = Gecko 1
                    else if (browserVersionPartsOne === 1) {
                        // > Gecko 1.9
                        if (browserVersionPartsTwo > 9) {
                            nativeSupport = true;
                        }
                        // = Gecko 1.9
                        else if (browserVersionPartsTwo === 9) {
                            // >= Gecko 1.9.2
                            if (browserVersionPartsThree >= 2) {
                                nativeSupport = true;
                            }
                        }
                    }
                }
                // >= Opera 10.60
                else if ((browser.opera || false)) {
                    // > Opera 10
                    if (browserVersionPartsOne > 10) {
                        nativeSupport = true;
                    }
                    // = Opera 10
                    else if (browserVersionPartsOne === 10) {
                        // >= Opera 10.60
                        if (browserVersionPartsTwo >= 60) {
                            nativeSupport = true;
                        }
                    }
                }

                // Return nativeSupport
                return nativeSupport;
            },

            /**
            * Enable hashchange for all browsers
            * For browsers which do not have native support, the support must be emulated.
            */
            hashchangeLoader: function() {
                var History = $.History;

                // Fetch nativeSupport
                var nativeSupport = History.nativeSupport();

                // Check whether or not we need to implement a unfortunate but required workaround for browsers without nativeSupport
                if (!nativeSupport) {
                    // We are not IE8, or another browser which supports onhashchange natively

                    // State our checker function, it is used to constantly check the location to detect a change
                    var checker;

                    // Handle depending on the browser
                    if ($.browser.msie) {
                        // We are still IE
                        // IE6, IE7, etc

                        // Append and $iframe to the document, as $iframes are required for back and forward
                        // Create a hidden $iframe for hash change tracking
                        History.$iframe = $('<iframe id="jquery-history-iframe" style="display: none;" src="javascript:false;"></$iframe>').prependTo(document.body)[0];

                        // Create initial history entry
                        History.$iframe.contentWindow.document.open();
                        History.$iframe.contentWindow.document.close();

                        // Define the checker function (for bookmarks)
                        var iframeHit = false;
                        checker = function() {

                            // Fetch
                            var hash = History.getHash();
                            var state = History.getState();
                            var iframeHash = History.extractHash(History.$iframe.contentWindow.document.location.hash);

                            // Check if the browser hash is different
                            if (state !== hash) {
                                // Browser hash is different

                                // Check if we need to update the iframe
                                if (!iframeHit) {
                                    // Write a iframe/history entry in the browsers back and forward
                                    // alert('update iframe entry');
                                    History.$iframe.contentWindow.document.open();
                                    History.$iframe.contentWindow.document.close();
                                    // alert('update iframe entry.');

                                    // Update the iframe hash
                                    // alert('update iframe hash');
                                    History.$iframe.contentWindow.document.location.hash = hash;
                                    // alert('update iframe hash.');
                                }

                                // Reset
                                iframeHit = false;

                                // Fire
                                // alert('hashchange');
                                History.$window.trigger('hashchange');
                                // alert('hashchange.');
                            }
                            else {
                                // Browser hash is not different

                                // Check if the iframe hash is different from the iframe state
                                if (state !== iframeHash) {
                                    // Specify we were hit from the iframe
                                    iframeHit = true;

                                    // Update the browser hash
                                    // alert('set hash from iframe');
                                    History.setHash(iframeHash);
                                    // alert('set hash from iframe.');
                                }
                            }

                        };
                    }
                    else {
                        // We are not IE
                        // Firefox, Opera, Etc

                        // Define the checker function (for bookmarks, back, forward)
                        checker = function() {
                            var hash = History.getHash();
                            var state = History.getState();
                            // Check
                            if (state !== hash) {
                                // State change
                                History.$window.trigger('hashchange');
                            }
                        };
                    }

                    // Apply the checker function
                    setInterval(checker, 200);
                }
                else {
                    // We are IE8, or another browser which supports onhashchange natively

                    // Fire the initial
                    var hash = History.getHash();
                    if (hash) {
                        History.$window.trigger('hashchange');
                    }
                }

                // Done
                return true;
            }

        }; // We have finished extending/defining our Plugin

        // --------------------------------------------------
        // Finish up

        // Instantiate
        $.History.construct();
    }
    else {
        window.console.warn('$.History has already been defined...');
    }

    // Finished definition
})(jQuery);   // We are done with our plugin, so lets call it with jQuery as the argument
;/* Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net)
* Licensed under the MIT License (LICENSE.txt).
*
* Version 2.1.2
*/
(function(a) { a.fn.bgiframe = (a.browser.msie && /msie 6\.0/i.test(navigator.userAgent) ? function(d) { d = a.extend({ top: "auto", left: "auto", width: "auto", height: "auto", opacity: true, src: "javascript:false;" }, d); var c = '<iframe class="bgiframe"frameborder="0"tabindex="-1"src="' + d.src + '"style="display:block;position:absolute;z-index:-1;' + (d.opacity !== false ? "filter:Alpha(Opacity='0');" : "") + "top:" + (d.top == "auto" ? "expression(((parseInt(this.parentNode.currentStyle.borderTopWidth)||0)*-1)+'px')" : b(d.top)) + ";left:" + (d.left == "auto" ? "expression(((parseInt(this.parentNode.currentStyle.borderLeftWidth)||0)*-1)+'px')" : b(d.left)) + ";width:" + (d.width == "auto" ? "expression(this.parentNode.offsetWidth+'px')" : b(d.width)) + ";height:" + (d.height == "auto" ? "expression(this.parentNode.offsetHeight+'px')" : b(d.height)) + ';"/>'; return this.each(function() { if (a(this).children("iframe.bgiframe").length === 0) { this.insertBefore(document.createElement(c), this.firstChild) } }) } : function() { return this }); a.fn.bgIframe = a.fn.bgiframe; function b(c) { return c && c.constructor === Number ? c + "px" : c } })(jQuery);;/*
 * SimpleModal 1.4.1 - jQuery Plugin
 * http://www.ericmmartin.com/projects/simplemodal/
 * Copyright (c) 2010 Eric Martin (http://twitter.com/ericmmartin)
 * Dual licensed under the MIT and GPL licenses
 * Revision: $Id: jquery.simplemodal.js 261 2010-11-05 21:16:20Z emartin24 $
 */
(function(d){var k=d.browser.msie&&parseInt(d.browser.version)===6&&typeof window.XMLHttpRequest!=="object",m=d.browser.msie&&parseInt(d.browser.version)===7,l=null,f=[];d.modal=function(a,b){return d.modal.impl.init(a,b)};d.modal.close=function(){d.modal.impl.close()};d.modal.focus=function(a){d.modal.impl.focus(a)};d.modal.setContainerDimensions=function(){d.modal.impl.setContainerDimensions()};d.modal.setPosition=function(){d.modal.impl.setPosition()};d.modal.update=function(a,b){d.modal.impl.update(a,
b)};d.fn.modal=function(a){return d.modal.impl.init(this,a)};d.modal.defaults={appendTo:"body",focus:true,opacity:50,overlayId:"simplemodal-overlay",overlayCss:{},containerId:"simplemodal-container",containerCss:{},dataId:"simplemodal-data",dataCss:{},minHeight:null,minWidth:null,maxHeight:null,maxWidth:null,autoResize:false,autoPosition:true,zIndex:1E3,close:true,closeHTML:'<a class="modalCloseImg" title="Close"></a>',closeClass:"simplemodal-close",escClose:true,overlayClose:false,position:null,
persist:false,modal:true,onOpen:null,onShow:null,onClose:null};d.modal.impl={d:{},init:function(a,b){var c=this;if(c.d.data)return false;l=d.browser.msie&&!d.boxModel;c.o=d.extend({},d.modal.defaults,b);c.zIndex=c.o.zIndex;c.occb=false;if(typeof a==="object"){a=a instanceof jQuery?a:d(a);c.d.placeholder=false;if(a.parent().parent().length>0){a.before(d("<span></span>").attr("id","simplemodal-placeholder").css({display:"none"}));c.d.placeholder=true;c.display=a.css("display");if(!c.o.persist)c.d.orig=
a.clone(true)}}else if(typeof a==="string"||typeof a==="number")a=d("<div></div>").html(a);else{alert("SimpleModal Error: Unsupported data type: "+typeof a);return c}c.create(a);c.open();d.isFunction(c.o.onShow)&&c.o.onShow.apply(c,[c.d]);return c},create:function(a){var b=this;f=b.getDimensions();if(b.o.modal&&k)b.d.iframe=d('<iframe src="javascript:false;"></iframe>').css(d.extend(b.o.iframeCss,{display:"none",opacity:0,position:"fixed",height:f[0],width:f[1],zIndex:b.o.zIndex,top:0,left:0})).appendTo(b.o.appendTo);
b.d.overlay=d("<div></div>").attr("id",b.o.overlayId).addClass("simplemodal-overlay").css(d.extend(b.o.overlayCss,{display:"none",opacity:b.o.opacity/100,height:b.o.modal?f[0]:0,width:b.o.modal?f[1]:0,position:"fixed",left:0,top:0,zIndex:b.o.zIndex+1})).appendTo(b.o.appendTo);b.d.container=d("<div></div>").attr("id",b.o.containerId).addClass("simplemodal-container").css(d.extend(b.o.containerCss,{display:"none",position:"fixed",zIndex:b.o.zIndex+2})).append(b.o.close&&b.o.closeHTML?d(b.o.closeHTML).addClass(b.o.closeClass):
"").appendTo(b.o.appendTo);b.d.wrap=d("<div></div>").attr("tabIndex",-1).addClass("simplemodal-wrap").css({height:"100%",outline:0,width:"100%"}).appendTo(b.d.container);b.d.data=a.attr("id",a.attr("id")||b.o.dataId).addClass("simplemodal-data").css(d.extend(b.o.dataCss,{display:"none"})).appendTo("body");b.setContainerDimensions();b.d.data.appendTo(b.d.wrap);if(k||l)b.fixIE()},bindEvents:function(){var a=this;d("."+a.o.closeClass).bind("click.simplemodal",function(b){b.preventDefault();a.close()});
a.o.modal&&a.o.close&&a.o.overlayClose&&a.d.overlay.bind("click.simplemodal",function(b){b.preventDefault();a.close()});d(document).bind("keydown.simplemodal",function(b){if(a.o.modal&&b.keyCode===9)a.watchTab(b);else if(a.o.close&&a.o.escClose&&b.keyCode===27){b.preventDefault();a.close()}});d(window).bind("resize.simplemodal",function(){f=a.getDimensions();a.o.autoResize?a.setContainerDimensions():a.o.autoPosition&&a.setPosition();if(k||l)a.fixIE();else if(a.o.modal){a.d.iframe&&a.d.iframe.css({height:f[0],
width:f[1]});a.d.overlay.css({height:f[0],width:f[1]})}})},unbindEvents:function(){d("."+this.o.closeClass).unbind("click.simplemodal");d(document).unbind("keydown.simplemodal");d(window).unbind("resize.simplemodal");this.d.overlay.unbind("click.simplemodal")},fixIE:function(){var a=this,b=a.o.position;d.each([a.d.iframe||null,!a.o.modal?null:a.d.overlay,a.d.container],function(c,h){if(h){var g=h[0].style;g.position="absolute";if(c<2){g.removeExpression("height");g.removeExpression("width");g.setExpression("height",
'document.body.scrollHeight > document.body.clientHeight ? document.body.scrollHeight : document.body.clientHeight + "px"');g.setExpression("width",'document.body.scrollWidth > document.body.clientWidth ? document.body.scrollWidth : document.body.clientWidth + "px"')}else{var e;if(b&&b.constructor===Array){c=b[0]?typeof b[0]==="number"?b[0].toString():b[0].replace(/px/,""):h.css("top").replace(/px/,"");c=c.indexOf("%")===-1?c+' + (t = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"':
parseInt(c.replace(/%/,""))+' * ((document.documentElement.clientHeight || document.body.clientHeight) / 100) + (t = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"';if(b[1]){e=typeof b[1]==="number"?b[1].toString():b[1].replace(/px/,"");e=e.indexOf("%")===-1?e+' + (t = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft) + "px"':parseInt(e.replace(/%/,""))+' * ((document.documentElement.clientWidth || document.body.clientWidth) / 100) + (t = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft) + "px"'}}else{c=
'(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (t = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"';e='(document.documentElement.clientWidth || document.body.clientWidth) / 2 - (this.offsetWidth / 2) + (t = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft) + "px"'}g.removeExpression("top");g.removeExpression("left");g.setExpression("top",
c);g.setExpression("left",e)}}})},focus:function(a){var b=this;a=a&&d.inArray(a,["first","last"])!==-1?a:"first";var c=d(":input:enabled:visible:"+a,b.d.wrap);setTimeout(function(){c.length>0?c.focus():b.d.wrap.focus()},10)},getDimensions:function(){var a=d(window);return[d.browser.opera&&d.browser.version>"9.5"&&d.fn.jquery<"1.3"||d.browser.opera&&d.browser.version<"9.5"&&d.fn.jquery>"1.2.6"?a[0].innerHeight:a.height(),a.width()]},getVal:function(a,b){return a?typeof a==="number"?a:a==="auto"?0:
a.indexOf("%")>0?parseInt(a.replace(/%/,""))/100*(b==="h"?f[0]:f[1]):parseInt(a.replace(/px/,"")):null},update:function(a,b){var c=this;if(!c.d.data)return false;c.d.origHeight=c.getVal(a,"h");c.d.origWidth=c.getVal(b,"w");c.d.data.hide();a&&c.d.container.css("height",a);b&&c.d.container.css("width",b);c.setContainerDimensions();c.d.data.show();c.o.focus&&c.focus();c.unbindEvents();c.bindEvents()},setContainerDimensions:function(){var a=this,b=k||m,c=a.d.origHeight?a.d.origHeight:d.browser.opera?
a.d.container.height():a.getVal(b?a.d.container[0].currentStyle.height:a.d.container.css("height"),"h");b=a.d.origWidth?a.d.origWidth:d.browser.opera?a.d.container.width():a.getVal(b?a.d.container[0].currentStyle.width:a.d.container.css("width"),"w");var h=a.d.data.outerHeight(true),g=a.d.data.outerWidth(true);a.d.origHeight=a.d.origHeight||c;a.d.origWidth=a.d.origWidth||b;var e=a.o.maxHeight?a.getVal(a.o.maxHeight,"h"):null,i=a.o.maxWidth?a.getVal(a.o.maxWidth,"w"):null;e=e&&e<f[0]?e:f[0];i=i&&i<
f[1]?i:f[1];var j=a.o.minHeight?a.getVal(a.o.minHeight,"h"):"auto";c=c?a.o.autoResize&&c>e?e:c<j?j:c:h?h>e?e:a.o.minHeight&&j!=="auto"&&h<j?j:h:j;e=a.o.minWidth?a.getVal(a.o.minWidth,"w"):"auto";b=b?a.o.autoResize&&b>i?i:b<e?e:b:g?g>i?i:a.o.minWidth&&e!=="auto"&&g<e?e:g:e;a.d.container.css({height:c,width:b});a.d.wrap.css({overflow:h>c||g>b?"auto":"visible"});a.o.autoPosition&&a.setPosition()},setPosition:function(){var a=this,b,c;b=f[0]/2-a.d.container.outerHeight(true)/2;c=f[1]/2-a.d.container.outerWidth(true)/
2;if(a.o.position&&Object.prototype.toString.call(a.o.position)==="[object Array]"){b=a.o.position[0]||b;c=a.o.position[1]||c}else{b=b;c=c}a.d.container.css({left:c,top:b})},watchTab:function(a){var b=this;if(d(a.target).parents(".simplemodal-container").length>0){b.inputs=d(":input:enabled:visible:first, :input:enabled:visible:last",b.d.data[0]);if(!a.shiftKey&&a.target===b.inputs[b.inputs.length-1]||a.shiftKey&&a.target===b.inputs[0]||b.inputs.length===0){a.preventDefault();b.focus(a.shiftKey?"last":
"first")}}else{a.preventDefault();b.focus()}},open:function(){var a=this;a.d.iframe&&a.d.iframe.show();if(d.isFunction(a.o.onOpen))a.o.onOpen.apply(a,[a.d]);else{a.d.overlay.show();a.d.container.show();a.d.data.show()}a.o.focus&&a.focus();a.bindEvents()},close:function(){var a=this;if(!a.d.data)return false;a.unbindEvents();if(d.isFunction(a.o.onClose)&&!a.occb){a.occb=true;a.o.onClose.apply(a,[a.d])}else{if(a.d.placeholder){var b=d("#simplemodal-placeholder");if(a.o.persist)b.replaceWith(a.d.data.removeClass("simplemodal-data").css("display",
a.display));else{a.d.data.hide().remove();b.replaceWith(a.d.orig)}}else a.d.data.hide().remove();a.d.container.hide().remove();a.d.overlay.hide();a.d.iframe&&a.d.iframe.hide().remove();setTimeout(function(){a.d.overlay.remove();a.d={}},15)}}}})(jQuery);
;/*	SWFObject v2.2 <http://code.google.com/p/swfobject/> 
	is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
*/
var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();;/**
* SWFUpload: http://www.swfupload.org, http://swfupload.googlecode.com
*
* mmSWFUpload 1.0: Flash upload dialog - http://profandesign.se/swfupload/,  http://www.vinterwebb.se/
*
* SWFUpload is (c) 2006-2007 Lars Huring, Olov Nilz�n and Mammon Media and is released under the MIT License:
* http://www.opensource.org/licenses/mit-license.php
*
* SWFUpload 2 is (c) 2007-2008 Jake Roberts and is released under the MIT License:
* http://www.opensource.org/licenses/mit-license.php
*
*/

/***** Changed by Vladimir Nechypurenko ******/
/* Added new features: button_visible */      

/* ******************* */
/* Constructor & Init  */
/* ******************* */
var SWFUpload;

if (SWFUpload == undefined) {
    SWFUpload = function(settings) {
        this.initSWFUpload(settings);
    };
}

SWFUpload.prototype.initSWFUpload = function(settings) {
    try {
        this.customSettings = {}; // A container where developers can place their own settings associated with this instance.
        this.settings = settings;
        this.eventQueue = [];
        this.movieName = "SWFUpload_" + SWFUpload.movieCount++;
        this.movieElement = null;


        // Setup global control tracking
        SWFUpload.instances[this.movieName] = this;

        // Load the settings.  Load the Flash movie.
        this.initSettings();
        this.loadFlash();
        this.displayDebugInfo();
    } catch (ex) {
        delete SWFUpload.instances[this.movieName];
        throw ex;
    }
};

/* *************** */
/* Static Members  */
/* *************** */
SWFUpload.instances = {};
SWFUpload.movieCount = 0;
SWFUpload.version = "2.2.0 2009-03-25";
SWFUpload.QUEUE_ERROR = {
    QUEUE_LIMIT_EXCEEDED: -100,
    FILE_EXCEEDS_SIZE_LIMIT: -110,
    ZERO_BYTE_FILE: -120,
    INVALID_FILETYPE: -130
};
SWFUpload.UPLOAD_ERROR = {
    HTTP_ERROR: -200,
    MISSING_UPLOAD_URL: -210,
    IO_ERROR: -220,
    SECURITY_ERROR: -230,
    UPLOAD_LIMIT_EXCEEDED: -240,
    UPLOAD_FAILED: -250,
    SPECIFIED_FILE_ID_NOT_FOUND: -260,
    FILE_VALIDATION_FAILED: -270,
    FILE_CANCELLED: -280,
    UPLOAD_STOPPED: -290
};
SWFUpload.FILE_STATUS = {
    QUEUED: -1,
    IN_PROGRESS: -2,
    ERROR: -3,
    COMPLETE: -4,
    CANCELLED: -5
};
SWFUpload.BUTTON_ACTION = {
    SELECT_FILE: -100,
    SELECT_FILES: -110,
    START_UPLOAD: -120
};
SWFUpload.CURSOR = {
    ARROW: -1,
    HAND: -2
};
SWFUpload.WINDOW_MODE = {
    WINDOW: "window",
    TRANSPARENT: "transparent",
    OPAQUE: "opaque"
};

// Private: takes a URL, determines if it is relative and converts to an absolute URL
// using the current site. Only processes the URL if it can, otherwise returns the URL untouched
SWFUpload.completeURL = function(url) {
    if (typeof (url) !== "string" || url.match(/^https?:\/\//i) || url.match(/^\//)) {
        return url;
    }

    var currentURL = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ":" + window.location.port : "");

    var indexSlash = window.location.pathname.lastIndexOf("/");
    if (indexSlash <= 0) {
        path = "/";
    } else {
        path = window.location.pathname.substr(0, indexSlash) + "/";
    }

    return /*currentURL +*/path + url;

};


/* ******************** */
/* Instance Members  */
/* ******************** */

// Private: initSettings ensures that all the
// settings are set, getting a default value if one was not assigned.
SWFUpload.prototype.initSettings = function() {
    this.ensureDefault = function(settingName, defaultValue) {
        this.settings[settingName] = (this.settings[settingName] == undefined) ? defaultValue : this.settings[settingName];
    };

    // Upload backend settings
    this.ensureDefault("upload_url", "");
    this.ensureDefault("preserve_relative_urls", false);
    this.ensureDefault("file_post_name", "Filedata");
    this.ensureDefault("post_params", {});
    this.ensureDefault("use_query_string", false);
    this.ensureDefault("requeue_on_error", false);
    this.ensureDefault("http_success", []);
    this.ensureDefault("assume_success_timeout", 0);

    // File Settings
    this.ensureDefault("file_types", "*.*");
    this.ensureDefault("file_types_description", "All Files");
    this.ensureDefault("file_size_limit", 0); // Default zero means "unlimited"
    this.ensureDefault("file_upload_limit", 0);
    this.ensureDefault("file_queue_limit", 0);

    // Flash Settings
    this.ensureDefault("flash_url", "swfupload.swf");
    this.ensureDefault("prevent_swf_caching", true);

    // Button Settings
    this.ensureDefault("button_image_url", "");
    this.ensureDefault("button_width", 1);
    this.ensureDefault("button_height", 1);
    this.ensureDefault("button_text", "");
    this.ensureDefault("button_text_style", "color: #000000; font-size: 16pt;");
    this.ensureDefault("button_text_top_padding", 0);
    this.ensureDefault("button_text_left_padding", 0);
    this.ensureDefault("button_action", SWFUpload.BUTTON_ACTION.SELECT_FILES);
    this.ensureDefault("button_disabled", false);
    this.ensureDefault("button_placeholder_id", "");
    this.ensureDefault("button_placeholder", null);
    this.ensureDefault("button_cursor", SWFUpload.CURSOR.ARROW);
    this.ensureDefault("button_window_mode", SWFUpload.WINDOW_MODE.WINDOW);
    this.ensureDefault("button_visible", true);

    // Debug Settings
    this.ensureDefault("debug", false);
    this.settings.debug_enabled = this.settings.debug; // Here to maintain v2 API

    // Event Handlers
    this.settings.return_upload_start_handler = this.returnUploadStart;
    this.ensureDefault("swfupload_loaded_handler", null);
    this.ensureDefault("file_dialog_start_handler", null);
    this.ensureDefault("file_queued_handler", null);
    this.ensureDefault("file_queue_error_handler", null);
    this.ensureDefault("file_dialog_complete_handler", null);

    this.ensureDefault("upload_start_handler", null);
    this.ensureDefault("upload_progress_handler", null);
    this.ensureDefault("upload_error_handler", null);
    this.ensureDefault("upload_success_handler", null);
    this.ensureDefault("upload_complete_handler", null);

    this.ensureDefault("debug_handler", this.debugMessage);

    this.ensureDefault("custom_settings", {});

    // Other settings
    this.customSettings = this.settings.custom_settings;

    // Update the flash url if needed
    if (!!this.settings.prevent_swf_caching) {
        this.settings.flash_url = this.settings.flash_url + (this.settings.flash_url.indexOf("?") < 0 ? "?" : "&") + "preventswfcaching=" + new Date().getTime();
    }

    if (!this.settings.preserve_relative_urls) {
        //this.settings.flash_url = SWFUpload.completeURL(this.settings.flash_url);	// Don't need to do this one since flash doesn't look at it
        this.settings.upload_url = SWFUpload.completeURL(this.settings.upload_url);
        this.settings.button_image_url = SWFUpload.completeURL(this.settings.button_image_url);
    }

    delete this.ensureDefault;
};

// Private: loadFlash replaces the button_placeholder element with the flash movie.
SWFUpload.prototype.loadFlash = function() {
    var targetElement, tempParent;

    // Make sure an element with the ID we are going to use doesn't already exist
    if (document.getElementById(this.movieName) !== null) {
        throw "ID " + this.movieName + " is already in use. The Flash Object could not be added";
    }

    // Get the element where we will be placing the flash movie
    targetElement = document.getElementById(this.settings.button_placeholder_id) || this.settings.button_placeholder;

    if (targetElement == undefined) {
        throw "Could not find the placeholder element: " + this.settings.button_placeholder_id;
    }

    // Append the container and load the flash
    tempParent = document.createElement("div");
    tempParent.innerHTML = this.getFlashHTML(); // Using innerHTML is non-standard but the only sensible way to dynamically add Flash in IE (and maybe other browsers)
    targetElement.parentNode.replaceChild(tempParent.firstChild, targetElement);

    // Fix IE Flash/Form bug
    if (window[this.movieName] == undefined) {
        window[this.movieName] = this.getMovieElement();
    }

};

// Private: getFlashHTML generates the object tag needed to embed the flash in to the document
SWFUpload.prototype.getFlashHTML = function() {
    // Flash Satay object syntax: http://www.alistapart.com/articles/flashsatay
    return ['<object id="', this.movieName, '" type="application/x-shockwave-flash" data="', this.settings.flash_url, '" width="', this.settings.button_width, '" height="', this.settings.button_height, '" class="swfupload ' +
	            ((this.settings.button_visible == "false") ? ' swfinvisible' : '') + '" >',
				'<param name="wmode" value="', this.settings.button_window_mode, '" />',
				'<param name="movie" value="', this.settings.flash_url, '" />',
				'<param name="quality" value="high" />',
				'<param name="menu" value="false" />',
				'<param name="allowScriptAccess" value="always" />',
				'<param name="flashvars" value="' + this.getFlashVars() + '" />',
				'</object>'].join("");
};

// Private: getFlashVars builds the parameter string that will be passed
// to flash in the flashvars param.
SWFUpload.prototype.getFlashVars = function() {
    // Build a string from the post param object
    var paramString = this.buildParamString();
    var httpSuccessString = this.settings.http_success.join(",");

    // Build the parameter string
    return ["movieName=", encodeURIComponent(this.movieName),
			"&amp;uploadURL=", encodeURIComponent(this.settings.upload_url),
			"&amp;useQueryString=", encodeURIComponent(this.settings.use_query_string),
			"&amp;requeueOnError=", encodeURIComponent(this.settings.requeue_on_error),
			"&amp;httpSuccess=", encodeURIComponent(httpSuccessString),
			"&amp;assumeSuccessTimeout=", encodeURIComponent(this.settings.assume_success_timeout),
			"&amp;params=", encodeURIComponent(paramString),
			"&amp;filePostName=", encodeURIComponent(this.settings.file_post_name),
			"&amp;fileTypes=", encodeURIComponent(this.settings.file_types),
			"&amp;fileTypesDescription=", encodeURIComponent(this.settings.file_types_description),
			"&amp;fileSizeLimit=", encodeURIComponent(this.settings.file_size_limit),
			"&amp;fileUploadLimit=", encodeURIComponent(this.settings.file_upload_limit),
			"&amp;fileQueueLimit=", encodeURIComponent(this.settings.file_queue_limit),
			"&amp;debugEnabled=", encodeURIComponent(this.settings.debug_enabled),
			"&amp;buttonImageURL=", encodeURIComponent(this.settings.button_image_url),
			"&amp;buttonWidth=", encodeURIComponent(this.settings.button_width),
			"&amp;buttonHeight=", encodeURIComponent(this.settings.button_height),
			"&amp;buttonText=", encodeURIComponent(this.settings.button_text),
			"&amp;buttonTextTopPadding=", encodeURIComponent(this.settings.button_text_top_padding),
			"&amp;buttonTextLeftPadding=", encodeURIComponent(this.settings.button_text_left_padding),
			"&amp;buttonTextStyle=", encodeURIComponent(this.settings.button_text_style),
			"&amp;buttonAction=", encodeURIComponent(this.settings.button_action),
			"&amp;buttonDisabled=", encodeURIComponent(this.settings.button_disabled),
			"&amp;buttonCursor=", encodeURIComponent(this.settings.button_cursor)
		].join("");
};

// Public: getMovieElement retrieves the DOM reference to the Flash element added by SWFUpload
// The element is cached after the first lookup
SWFUpload.prototype.getMovieElement = function() {
    if (this.movieElement == undefined) {
        this.movieElement = document.getElementById(this.movieName);
    }

    if (this.movieElement === null) {
        throw "Could not find Flash element";
    }

    return this.movieElement;
};

// Private: buildParamString takes the name/value pairs in the post_params setting object
// and joins them up in to a string formatted "name=value&amp;name=value"
SWFUpload.prototype.buildParamString = function() {
    var postParams = this.settings.post_params;
    var paramStringPairs = [];

    if (typeof (postParams) === "object") {
        for (var name in postParams) {
            if (postParams.hasOwnProperty(name)) {
                paramStringPairs.push(encodeURIComponent(name.toString()) + "=" + encodeURIComponent(postParams[name].toString()));
            }
        }
    }

    return paramStringPairs.join("&amp;");
};

// Public: Used to remove a SWFUpload instance from the page. This method strives to remove
// all references to the SWF, and other objects so memory is properly freed.
// Returns true if everything was destroyed. Returns a false if a failure occurs leaving SWFUpload in an inconsistant state.
// Credits: Major improvements provided by steffen
SWFUpload.prototype.destroy = function() {
    try {
        // Make sure Flash is done before we try to remove it
        this.cancelUpload(null, false);


        // Remove the SWFUpload DOM nodes
        var movieElement = null;
        movieElement = this.getMovieElement();

        if (movieElement && typeof (movieElement.CallFunction) === "unknown") { // We only want to do this in IE
            // Loop through all the movie's properties and remove all function references (DOM/JS IE 6/7 memory leak workaround)
            for (var i in movieElement) {
                try {
                    if (typeof (movieElement[i]) === "function") {
                        movieElement[i] = null;
                    }
                } catch (ex1) { }
            }

            // Remove the Movie Element from the page
            try {
                movieElement.parentNode.removeChild(movieElement);
            } catch (ex) { }
        }

        // Remove IE form fix reference
        window[this.movieName] = null;

        // Destroy other references
        SWFUpload.instances[this.movieName] = null;
        delete SWFUpload.instances[this.movieName];

        this.movieElement = null;
        this.settings = null;
        this.customSettings = null;
        this.eventQueue = null;
        this.movieName = null;


        return true;
    } catch (ex2) {
        return false;
    }
};


// Public: displayDebugInfo prints out settings and configuration
// information about this SWFUpload instance.
// This function (and any references to it) can be deleted when placing
// SWFUpload in production.
SWFUpload.prototype.displayDebugInfo = function() {
    this.debug(
		[
			"---SWFUpload Instance Info---\n",
			"Version: ", SWFUpload.version, "\n",
			"Movie Name: ", this.movieName, "\n",
			"Settings:\n",
			"\t", "upload_url:               ", this.settings.upload_url, "\n",
			"\t", "flash_url:                ", this.settings.flash_url, "\n",
			"\t", "use_query_string:         ", this.settings.use_query_string.toString(), "\n",
			"\t", "requeue_on_error:         ", this.settings.requeue_on_error.toString(), "\n",
			"\t", "http_success:             ", this.settings.http_success.join(", "), "\n",
			"\t", "assume_success_timeout:   ", this.settings.assume_success_timeout, "\n",
			"\t", "file_post_name:           ", this.settings.file_post_name, "\n",
			"\t", "post_params:              ", this.settings.post_params.toString(), "\n",
			"\t", "file_types:               ", this.settings.file_types, "\n",
			"\t", "file_types_description:   ", this.settings.file_types_description, "\n",
			"\t", "file_size_limit:          ", this.settings.file_size_limit, "\n",
			"\t", "file_upload_limit:        ", this.settings.file_upload_limit, "\n",
			"\t", "file_queue_limit:         ", this.settings.file_queue_limit, "\n",
			"\t", "debug:                    ", this.settings.debug.toString(), "\n",

			"\t", "prevent_swf_caching:      ", this.settings.prevent_swf_caching.toString(), "\n",

			"\t", "button_placeholder_id:    ", this.settings.button_placeholder_id.toString(), "\n",
			"\t", "button_placeholder:       ", (this.settings.button_placeholder ? "Set" : "Not Set"), "\n",
			"\t", "button_image_url:         ", this.settings.button_image_url.toString(), "\n",
			"\t", "button_width:             ", this.settings.button_width.toString(), "\n",
			"\t", "button_height:            ", this.settings.button_height.toString(), "\n",
			"\t", "button_text:              ", this.settings.button_text.toString(), "\n",
			"\t", "button_text_style:        ", this.settings.button_text_style.toString(), "\n",
			"\t", "button_text_top_padding:  ", this.settings.button_text_top_padding.toString(), "\n",
			"\t", "button_text_left_padding: ", this.settings.button_text_left_padding.toString(), "\n",
			"\t", "button_action:            ", this.settings.button_action.toString(), "\n",
			"\t", "button_disabled:          ", this.settings.button_disabled.toString(), "\n",

			"\t", "custom_settings:          ", this.settings.custom_settings.toString(), "\n",
			"Event Handlers:\n",
			"\t", "swfupload_loaded_handler assigned:  ", (typeof this.settings.swfupload_loaded_handler === "function").toString(), "\n",
			"\t", "file_dialog_start_handler assigned: ", (typeof this.settings.file_dialog_start_handler === "function").toString(), "\n",
			"\t", "file_queued_handler assigned:       ", (typeof this.settings.file_queued_handler === "function").toString(), "\n",
			"\t", "file_queue_error_handler assigned:  ", (typeof this.settings.file_queue_error_handler === "function").toString(), "\n",
			"\t", "upload_start_handler assigned:      ", (typeof this.settings.upload_start_handler === "function").toString(), "\n",
			"\t", "upload_progress_handler assigned:   ", (typeof this.settings.upload_progress_handler === "function").toString(), "\n",
			"\t", "upload_error_handler assigned:      ", (typeof this.settings.upload_error_handler === "function").toString(), "\n",
			"\t", "upload_success_handler assigned:    ", (typeof this.settings.upload_success_handler === "function").toString(), "\n",
			"\t", "upload_complete_handler assigned:   ", (typeof this.settings.upload_complete_handler === "function").toString(), "\n",
			"\t", "debug_handler assigned:             ", (typeof this.settings.debug_handler === "function").toString(), "\n"
		].join("")
	);
};

/* Note: addSetting and getSetting are no longer used by SWFUpload but are included
the maintain v2 API compatibility
*/
// Public: (Deprecated) addSetting adds a setting value. If the value given is undefined or null then the default_value is used.
SWFUpload.prototype.addSetting = function(name, value, default_value) {
    if (value == undefined) {
        return (this.settings[name] = default_value);
    } else {
        return (this.settings[name] = value);
    }
};

// Public: (Deprecated) getSetting gets a setting. Returns an empty string if the setting was not found.
SWFUpload.prototype.getSetting = function(name) {
    if (this.settings[name] != undefined) {
        return this.settings[name];
    }

    return "";
};



// Private: callFlash handles function calls made to the Flash element.
// Calls are made with a setTimeout for some functions to work around
// bugs in the ExternalInterface library.
SWFUpload.prototype.callFlash = function(functionName, argumentArray) {
    argumentArray = argumentArray || [];

    var movieElement = this.getMovieElement();
    var returnValue, returnString;

    // Flash's method if calling ExternalInterface methods (code adapted from MooTools).
    try {
        returnString = movieElement.CallFunction('<invoke name="' + functionName + '" returntype="javascript">' + __flash__argumentsToXML(argumentArray, 0) + '</invoke>');
        returnValue = eval(returnString);
    } catch (ex) {
        throw "Call to " + functionName + " failed";
    }

    // Unescape file post param values
    if (returnValue != undefined && typeof returnValue.post === "object") {
        returnValue = this.unescapeFilePostParams(returnValue);
    }

    return returnValue;
};

/* *****************************
-- Flash control methods --
Your UI should use these
to operate SWFUpload
***************************** */

// WARNING: this function does not work in Flash Player 10
// Public: selectFile causes a File Selection Dialog window to appear.  This
// dialog only allows 1 file to be selected.
SWFUpload.prototype.selectFile = function() {
    this.callFlash("SelectFile");
};

// WARNING: this function does not work in Flash Player 10
// Public: selectFiles causes a File Selection Dialog window to appear/ This
// dialog allows the user to select any number of files
// Flash Bug Warning: Flash limits the number of selectable files based on the combined length of the file names.
// If the selection name length is too long the dialog will fail in an unpredictable manner.  There is no work-around
// for this bug.
SWFUpload.prototype.selectFiles = function() {
    this.callFlash("SelectFiles");
};


// Public: startUpload starts uploading the first file in the queue unless
// the optional parameter 'fileID' specifies the ID 
SWFUpload.prototype.startUpload = function(fileID) {
    this.callFlash("StartUpload", [fileID]);
};

// Public: cancelUpload cancels any queued file.  The fileID parameter may be the file ID or index.
// If you do not specify a fileID the current uploading file or first file in the queue is cancelled.
// If you do not want the uploadError event to trigger you can specify false for the triggerErrorEvent parameter.
SWFUpload.prototype.cancelUpload = function(fileID, triggerErrorEvent) {
    if (triggerErrorEvent !== false) {
        triggerErrorEvent = true;
    }
    this.callFlash("CancelUpload", [fileID, triggerErrorEvent]);
};

// Public: stopUpload stops the current upload and requeues the file at the beginning of the queue.
// If nothing is currently uploading then nothing happens.
SWFUpload.prototype.stopUpload = function() {
    this.callFlash("StopUpload");
};

/* ************************
* Settings methods
*   These methods change the SWFUpload settings.
*   SWFUpload settings should not be changed directly on the settings object
*   since many of the settings need to be passed to Flash in order to take
*   effect.
* *********************** */

// Public: getStats gets the file statistics object.
SWFUpload.prototype.getStats = function() {
    return this.callFlash("GetStats");
};

// Public: setStats changes the SWFUpload statistics.  You shouldn't need to 
// change the statistics but you can.  Changing the statistics does not
// affect SWFUpload accept for the successful_uploads count which is used
// by the upload_limit setting to determine how many files the user may upload.
SWFUpload.prototype.setStats = function(statsObject) {
    this.callFlash("SetStats", [statsObject]);
};

// Public: getFile retrieves a File object by ID or Index.  If the file is
// not found then 'null' is returned.
SWFUpload.prototype.getFile = function(fileID) {
    if (typeof (fileID) === "number") {
        return this.callFlash("GetFileByIndex", [fileID]);
    } else {
        return this.callFlash("GetFile", [fileID]);
    }
};

// Public: addFileParam sets a name/value pair that will be posted with the
// file specified by the Files ID.  If the name already exists then the
// exiting value will be overwritten.
SWFUpload.prototype.addFileParam = function(fileID, name, value) {
    return this.callFlash("AddFileParam", [fileID, name, value]);
};

// Public: removeFileParam removes a previously set (by addFileParam) name/value
// pair from the specified file.
SWFUpload.prototype.removeFileParam = function(fileID, name) {
    this.callFlash("RemoveFileParam", [fileID, name]);
};

// Public: setUploadUrl changes the upload_url setting.
SWFUpload.prototype.setUploadURL = function(url) {
    this.settings.upload_url = url.toString();
    this.callFlash("SetUploadURL", [url]);
};

// Public: setPostParams changes the post_params setting
SWFUpload.prototype.setPostParams = function(paramsObject) {
    this.settings.post_params = paramsObject;
    this.callFlash("SetPostParams", [paramsObject]);
};

// Public: addPostParam adds post name/value pair.  Each name can have only one value.
SWFUpload.prototype.addPostParam = function(name, value) {
    this.settings.post_params[name] = value;
    this.callFlash("SetPostParams", [this.settings.post_params]);
};

// Public: removePostParam deletes post name/value pair.
SWFUpload.prototype.removePostParam = function(name) {
    delete this.settings.post_params[name];
    this.callFlash("SetPostParams", [this.settings.post_params]);
};

// Public: setFileTypes changes the file_types setting and the file_types_description setting
SWFUpload.prototype.setFileTypes = function(types, description) {
    this.settings.file_types = types;
    this.settings.file_types_description = description;
    this.callFlash("SetFileTypes", [types, description]);
};

// Public: setFileSizeLimit changes the file_size_limit setting
SWFUpload.prototype.setFileSizeLimit = function(fileSizeLimit) {
    this.settings.file_size_limit = fileSizeLimit;
    this.callFlash("SetFileSizeLimit", [fileSizeLimit]);
};

// Public: setFileUploadLimit changes the file_upload_limit setting
SWFUpload.prototype.setFileUploadLimit = function(fileUploadLimit) {
    this.settings.file_upload_limit = fileUploadLimit;
    this.callFlash("SetFileUploadLimit", [fileUploadLimit]);
};

// Public: setFileQueueLimit changes the file_queue_limit setting
SWFUpload.prototype.setFileQueueLimit = function(fileQueueLimit) {
    this.settings.file_queue_limit = fileQueueLimit;
    this.callFlash("SetFileQueueLimit", [fileQueueLimit]);
};

// Public: setFilePostName changes the file_post_name setting
SWFUpload.prototype.setFilePostName = function(filePostName) {
    this.settings.file_post_name = filePostName;
    this.callFlash("SetFilePostName", [filePostName]);
};

// Public: setUseQueryString changes the use_query_string setting
SWFUpload.prototype.setUseQueryString = function(useQueryString) {
    this.settings.use_query_string = useQueryString;
    this.callFlash("SetUseQueryString", [useQueryString]);
};

// Public: setRequeueOnError changes the requeue_on_error setting
SWFUpload.prototype.setRequeueOnError = function(requeueOnError) {
    this.settings.requeue_on_error = requeueOnError;
    this.callFlash("SetRequeueOnError", [requeueOnError]);
};

// Public: setHTTPSuccess changes the http_success setting
SWFUpload.prototype.setHTTPSuccess = function(http_status_codes) {
    if (typeof http_status_codes === "string") {
        http_status_codes = http_status_codes.replace(" ", "").split(",");
    }

    this.settings.http_success = http_status_codes;
    this.callFlash("SetHTTPSuccess", [http_status_codes]);
};

// Public: setHTTPSuccess changes the http_success setting
SWFUpload.prototype.setAssumeSuccessTimeout = function(timeout_seconds) {
    this.settings.assume_success_timeout = timeout_seconds;
    this.callFlash("SetAssumeSuccessTimeout", [timeout_seconds]);
};

// Public: setDebugEnabled changes the debug_enabled setting
SWFUpload.prototype.setDebugEnabled = function(debugEnabled) {
    this.settings.debug_enabled = debugEnabled;
    this.callFlash("SetDebugEnabled", [debugEnabled]);
};

// Public: setButtonImageURL loads a button image sprite
SWFUpload.prototype.setButtonImageURL = function(buttonImageURL) {
    if (buttonImageURL == undefined) {
        buttonImageURL = "";
    }

    this.settings.button_image_url = buttonImageURL;
    this.callFlash("SetButtonImageURL", [buttonImageURL]);
};

// Public: setButtonDimensions resizes the Flash Movie and button
SWFUpload.prototype.setButtonDimensions = function(width, height) {
    this.settings.button_width = width;
    this.settings.button_height = height;

    var movie = this.getMovieElement();
    if (movie != undefined) {
        movie.style.width = width + "px";
        movie.style.height = height + "px";
    }

    this.callFlash("SetButtonDimensions", [width, height]);
};
// Public: setButtonText Changes the text overlaid on the button
SWFUpload.prototype.setButtonText = function(html) {
    this.settings.button_text = html;
    this.callFlash("SetButtonText", [html]);
};
// Public: setButtonTextPadding changes the top and left padding of the text overlay
SWFUpload.prototype.setButtonTextPadding = function(left, top) {
    this.settings.button_text_top_padding = top;
    this.settings.button_text_left_padding = left;
    this.callFlash("SetButtonTextPadding", [left, top]);
};

// Public: setButtonTextStyle changes the CSS used to style the HTML/Text overlaid on the button
SWFUpload.prototype.setButtonTextStyle = function(css) {
    this.settings.button_text_style = css;
    this.callFlash("SetButtonTextStyle", [css]);
};
// Public: setButtonDisabled disables/enables the button
SWFUpload.prototype.setButtonDisabled = function(isDisabled) {
    this.settings.button_disabled = isDisabled;
    this.callFlash("SetButtonDisabled", [isDisabled]);
};
// Public: setButtonAction sets the action that occurs when the button is clicked
SWFUpload.prototype.setButtonAction = function(buttonAction) {
    this.settings.button_action = buttonAction;
    this.callFlash("SetButtonAction", [buttonAction]);
};

// Public: setButtonCursor changes the mouse cursor displayed when hovering over the button
SWFUpload.prototype.setButtonCursor = function(cursor) {
    this.settings.button_cursor = cursor;
    this.callFlash("SetButtonCursor", [cursor]);
};

/* *******************************
Flash Event Interfaces
These functions are used by Flash to trigger the various
events.
	
All these functions a Private.
	
Because the ExternalInterface library is buggy the event calls
are added to a queue and the queue then executed by a setTimeout.
This ensures that events are executed in a determinate order and that
the ExternalInterface bugs are avoided.
******************************* */

SWFUpload.prototype.queueEvent = function(handlerName, argumentArray) {
    // Warning: Don't call this.debug inside here or you'll create an infinite loop

    if (argumentArray == undefined) {
        argumentArray = [];
    } else if (!(argumentArray instanceof Array)) {
        argumentArray = [argumentArray];
    }

    var self = this;
    if (typeof this.settings[handlerName] === "function") {
        // Queue the event
        this.eventQueue.push(function() {
            this.settings[handlerName].apply(this, argumentArray);
        });

        // Execute the next queued event
        setTimeout(function() {
            self.executeNextEvent();
        }, 0);

    } else if (this.settings[handlerName] !== null) {
        throw "Event handler " + handlerName + " is unknown or is not a function";
    }
};

// Private: Causes the next event in the queue to be executed.  Since events are queued using a setTimeout
// we must queue them in order to garentee that they are executed in order.
SWFUpload.prototype.executeNextEvent = function() {
    // Warning: Don't call this.debug inside here or you'll create an infinite loop

    var f = this.eventQueue ? this.eventQueue.shift() : null;
    if (typeof (f) === "function") {
        f.apply(this);
    }
};

// Private: unescapeFileParams is part of a workaround for a flash bug where objects passed through ExternalInterface cannot have
// properties that contain characters that are not valid for JavaScript identifiers. To work around this
// the Flash Component escapes the parameter names and we must unescape again before passing them along.
SWFUpload.prototype.unescapeFilePostParams = function(file) {
    var reg = /[$]([0-9a-f]{4})/i;
    var unescapedPost = {};
    var uk;

    if (file != undefined) {
        for (var k in file.post) {
            if (file.post.hasOwnProperty(k)) {
                uk = k;
                var match;
                while ((match = reg.exec(uk)) !== null) {
                    uk = uk.replace(match[0], String.fromCharCode(parseInt("0x" + match[1], 16)));
                }
                unescapedPost[uk] = file.post[k];
            }
        }

        file.post = unescapedPost;
    }

    return file;
};

// Private: Called by Flash to see if JS can call in to Flash (test if External Interface is working)
SWFUpload.prototype.testExternalInterface = function() {
    try {
        return this.callFlash("TestExternalInterface");
    } catch (ex) {
        return false;
    }
};

// Private: This event is called by Flash when it has finished loading. Don't modify this.
// Use the swfupload_loaded_handler event setting to execute custom code when SWFUpload has loaded.
SWFUpload.prototype.flashReady = function() {
    // Check that the movie element is loaded correctly with its ExternalInterface methods defined
    var movieElement = this.getMovieElement();

    if (!movieElement) {
        this.debug("Flash called back ready but the flash movie can't be found.");
        return;
    }

    this.cleanUp(movieElement);

    this.queueEvent("swfupload_loaded_handler");
};

// Private: removes Flash added fuctions to the DOM node to prevent memory leaks in IE.
// This function is called by Flash each time the ExternalInterface functions are created.
SWFUpload.prototype.cleanUp = function(movieElement) {
    // Pro-actively unhook all the Flash functions
    try {
        if (this.movieElement && typeof (movieElement.CallFunction) === "unknown") { // We only want to do this in IE
            this.debug("Removing Flash functions hooks (this should only run in IE and should prevent memory leaks)");
            for (var key in movieElement) {
                try {
                    if (typeof (movieElement[key]) === "function") {
                        movieElement[key] = null;
                    }
                } catch (ex) {
                }
            }
        }
    } catch (ex1) {

    }

    // Fix Flashes own cleanup code so if the SWFMovie was removed from the page
    // it doesn't display errors.
    window["__flash__removeCallback"] = function(instance, name) {
        try {
            if (instance) {
                instance[name] = null;
            }
        } catch (flashEx) {

        }
    };

};


/* This is a chance to do something before the browse window opens */
SWFUpload.prototype.fileDialogStart = function() {
    this.queueEvent("file_dialog_start_handler");
};


/* Called when a file is successfully added to the queue. */
SWFUpload.prototype.fileQueued = function(file) {
    file = this.unescapeFilePostParams(file);
    this.queueEvent("file_queued_handler", file);
};


/* Handle errors that occur when an attempt to queue a file fails. */
SWFUpload.prototype.fileQueueError = function(file, errorCode, message) {
    file = this.unescapeFilePostParams(file);
    this.queueEvent("file_queue_error_handler", [file, errorCode, message]);
};

/* Called after the file dialog has closed and the selected files have been queued.
You could call startUpload here if you want the queued files to begin uploading immediately. */
SWFUpload.prototype.fileDialogComplete = function(numFilesSelected, numFilesQueued, numFilesInQueue) {
    this.queueEvent("file_dialog_complete_handler", [numFilesSelected, numFilesQueued, numFilesInQueue]);
};

SWFUpload.prototype.uploadStart = function(file) {
    file = this.unescapeFilePostParams(file);
    this.queueEvent("return_upload_start_handler", file);
};

SWFUpload.prototype.returnUploadStart = function(file) {
    var returnValue;
    if (typeof this.settings.upload_start_handler === "function") {
        file = this.unescapeFilePostParams(file);
        returnValue = this.settings.upload_start_handler.call(this, file);
    } else if (this.settings.upload_start_handler != undefined) {
        throw "upload_start_handler must be a function";
    }

    // Convert undefined to true so if nothing is returned from the upload_start_handler it is
    // interpretted as 'true'.
    if (returnValue === undefined) {
        returnValue = true;
    }

    returnValue = !!returnValue;

    this.callFlash("ReturnUploadStart", [returnValue]);
};



SWFUpload.prototype.uploadProgress = function(file, bytesComplete, bytesTotal) {
    file = this.unescapeFilePostParams(file);
    this.queueEvent("upload_progress_handler", [file, bytesComplete, bytesTotal]);
};

SWFUpload.prototype.uploadError = function(file, errorCode, message) {
    file = this.unescapeFilePostParams(file);
    this.queueEvent("upload_error_handler", [file, errorCode, message]);
};

SWFUpload.prototype.uploadSuccess = function(file, serverData, responseReceived) {
    file = this.unescapeFilePostParams(file);
    this.queueEvent("upload_success_handler", [file, serverData, responseReceived]);
};

SWFUpload.prototype.uploadComplete = function(file) {
    file = this.unescapeFilePostParams(file);
    this.queueEvent("upload_complete_handler", file);
};

/* Called by SWFUpload JavaScript and Flash functions when debug is enabled. By default it writes messages to the
internal debug console.  You can override this event and have messages written where you want. */
SWFUpload.prototype.debug = function(message) {
    this.queueEvent("debug_handler", message);
};


/* **********************************
Debug Console
The debug console is a self contained, in page location
for debug message to be sent.  The Debug Console adds
itself to the body if necessary.

The console is automatically scrolled as messages appear.
	
If you are using your own debug handler or when you deploy to production and
have debug disabled you can remove these functions to reduce the file size
and complexity.
********************************** */

// Private: debugMessage is the default debug_handler.  If you want to print debug messages
// call the debug() function.  When overriding the function your own function should
// check to see if the debug setting is true before outputting debug information.
SWFUpload.prototype.debugMessage = function(message) {
    if (this.settings.debug) {
        var exceptionMessage, exceptionValues = [];

        // Check for an exception object and print it nicely
        if (typeof message === "object" && typeof message.name === "string" && typeof message.message === "string") {
            for (var key in message) {
                if (message.hasOwnProperty(key)) {
                    exceptionValues.push(key + ": " + message[key]);
                }
            }
            exceptionMessage = exceptionValues.join("\n") || "";
            exceptionValues = exceptionMessage.split("\n");
            exceptionMessage = "EXCEPTION: " + exceptionValues.join("\nEXCEPTION: ");
            SWFUpload.Console.writeLine(exceptionMessage);
        } else {
            SWFUpload.Console.writeLine(message);
        }
    }
};

SWFUpload.Console = {};
SWFUpload.Console.writeLine = function(message) {
    var console, documentForm;

    try {
        console = document.getElementById("SWFUpload_Console");

        if (!console) {
            documentForm = document.createElement("form");
            document.getElementsByTagName("body")[0].appendChild(documentForm);

            console = document.createElement("textarea");
            console.id = "SWFUpload_Console";
            console.style.fontFamily = "monospace";
            console.setAttribute("wrap", "off");
            console.wrap = "off";
            console.style.overflow = "auto";
            console.style.width = "700px";
            console.style.height = "350px";
            console.style.margin = "5px";
            documentForm.appendChild(console);
        }

        console.value += message + "\n";

        console.scrollTop = console.scrollHeight - console.clientHeight;
    } catch (ex) {
        alert("Exception: " + ex.name + " Message: " + ex.message);
    }
};
;/*!
 * jQuery Placeholder Plugin v2.3.1
 * https://github.com/mathiasbynens/jquery-placeholder
 *
 * Copyright 2011, 2015 Mathias Bynens
 * Released under the MIT license
 */
(function(factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        factory(require('jquery'));
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function($) {

    /****
     * Allows plugin behavior simulation in modern browsers for easier debugging. 
     * When setting to true, use attribute "placeholder-x" rather than the usual "placeholder" in your inputs/textareas 
     * i.e. <input type="text" placeholder-x="my placeholder text" />
     */
    var debugMode = false; 

    // Opera Mini v7 doesn't support placeholder although its DOM seems to indicate so
    var isOperaMini = Object.prototype.toString.call(window.operamini) === '[object OperaMini]';
    var isInputSupported = 'placeholder' in document.createElement('input') && !isOperaMini && !debugMode;
    var isTextareaSupported = 'placeholder' in document.createElement('textarea') && !isOperaMini && !debugMode;
    var valHooks = $.valHooks;
    var propHooks = $.propHooks;
    var hooks;
    var placeholder;
    var settings = {};

    if (isInputSupported && isTextareaSupported) {

        placeholder = $.fn.placeholder = function() {
            return this;
        };

        placeholder.input = true;
        placeholder.textarea = true;

    } else {

        placeholder = $.fn.placeholder = function(options) {

            var defaults = {customClass: 'placeholder'};
            settings = $.extend({}, defaults, options);

            return this.filter((isInputSupported ? 'textarea' : ':input') + '[' + (debugMode ? 'placeholder-x' : 'placeholder') + ']')
                .not('.'+settings.customClass)
                .not(':radio, :checkbox, [type=hidden]')
                .bind({
                    'focus.placeholder': clearPlaceholder,
                    'blur.placeholder': setPlaceholder
                })
                .data('placeholder-enabled', true)
                .trigger('blur.placeholder');
        };

        placeholder.input = isInputSupported;
        placeholder.textarea = isTextareaSupported;

        hooks = {
            'get': function(element) {

                var $element = $(element);
                var $passwordInput = $element.data('placeholder-password');

                if ($passwordInput) {
                    return $passwordInput[0].value;
                }

                return $element.data('placeholder-enabled') && $element.hasClass(settings.customClass) ? '' : element.value;
            },
            'set': function(element, value) {

                var $element = $(element);
                var $replacement;
                var $passwordInput;

                if (value !== '') {

                    $replacement = $element.data('placeholder-textinput');
                    $passwordInput = $element.data('placeholder-password');

                    if ($replacement) {
                        clearPlaceholder.call($replacement[0], true, value) || (element.value = value);
                        $replacement[0].value = value;

                    } else if ($passwordInput) {
                        clearPlaceholder.call(element, true, value) || ($passwordInput[0].value = value);
                        element.value = value;
                    }
                }

                if (!$element.data('placeholder-enabled')) {
                    element.value = value;
                    return $element;
                }

                if (value === '') {
                    
                    element.value = value;
                    
                    // Setting the placeholder causes problems if the element continues to have focus.
                    if (element != safeActiveElement()) {
                        // We can't use `triggerHandler` here because of dummy text/password inputs :(
                        setPlaceholder.call(element);
                    }

                } else {
                    
                    if ($element.hasClass(settings.customClass)) {
                        clearPlaceholder.call(element);
                    }

                    element.value = value;
                }
                // `set` can not return `undefined`; see http://jsapi.info/jquery/1.7.1/val#L2363
                return $element;
            }
        };

        $(function() {
            // Look for forms
            $(document).delegate('form', 'submit.placeholder', function() {
                
                // Clear the placeholder values so they don't get submitted
                var $inputs = $('.'+settings.customClass, this).each(function() {
                    clearPlaceholder.call(this, true, '');
                });

                setTimeout(function() {
                    $inputs.each(setPlaceholder);
                }, 10);
            });
        });

        // Clear placeholder values upon page reload
        $(window).bind('beforeunload.placeholder', function() {

            var clearPlaceholders = true;

            try {
                // Prevent IE javascript:void(0) anchors from causing cleared values
                if (document.activeElement.toString() === 'javascript:void(0)') {
                    clearPlaceholders = false;
                }
            } catch (exception) { }

            if (clearPlaceholders) {
                $('.'+settings.customClass).each(function() {
                    this.value = '';
                });
            }
        });
    }

    function args(elem) {
        // Return an object of element attributes
        var newAttrs = {};
        var rinlinejQuery = /^jQuery\d+$/;

        $.each(elem.attributes, function(i, attr) {
            if (attr.specified && !rinlinejQuery.test(attr.name)) {
                newAttrs[attr.name] = attr.value;
            }
        });

        return newAttrs;
    }

    function clearPlaceholder(event, value) {
        
        var input = this;
        var $input = $(this);
        
        if (input.value === $input.attr((debugMode ? 'placeholder-x' : 'placeholder')) && $input.hasClass(settings.customClass)) {
            
            input.value = '';
            $input.removeClass(settings.customClass);

            if ($input.data('placeholder-password')) {

                $input = $input.hide().nextAll('input[type="password"]:first').show().attr('id', $input.removeAttr('id').data('placeholder-id'));
                
                // If `clearPlaceholder` was called from `$.valHooks.input.set`
                if (event === true) {
                    $input[0].value = value;

                    return value;
                }

                $input.focus();

            } else {
                input == safeActiveElement() && input.select();
            }
        }
    }

    function setPlaceholder(event) {
        var $replacement;
        var input = this;
        var $input = $(this);
        var id = input.id;

        // If the placeholder is activated, triggering blur event (`$input.trigger('blur')`) should do nothing.
        if (event && event.type === 'blur' && $input.hasClass(settings.customClass)) {
            return;
        }

        if (input.value === '') {
            if (input.type === 'password') {
                if (!$input.data('placeholder-textinput')) {
                    
                    try {
                        $replacement = $input.clone().prop({ 'type': 'text' });
                    } catch(e) {
                        $replacement = $('<input>').attr($.extend(args(this), { 'type': 'text' }));
                    }

                    $replacement
                        .removeAttr('name')
                        .data({
                            'placeholder-enabled': true,
                            'placeholder-password': $input,
                            'placeholder-id': id
                        })
                        .bind('focus.placeholder', clearPlaceholder);

                    $input
                        .data({
                            'placeholder-textinput': $replacement,
                            'placeholder-id': id
                        })
                        .before($replacement);
                }

                input.value = '';
                $input = $input.removeAttr('id').hide().prevAll('input[type="text"]:first').attr('id', $input.data('placeholder-id')).show();

            } else {
                
                var $passwordInput = $input.data('placeholder-password');

                if ($passwordInput) {
                    $passwordInput[0].value = '';
                    $input.attr('id', $input.data('placeholder-id')).show().nextAll('input[type="password"]:last').hide().removeAttr('id');
                }
            }

            $input.addClass(settings.customClass);
            $input[0].value = $input.attr((debugMode ? 'placeholder-x' : 'placeholder'));

        } else {
            $input.removeClass(settings.customClass);
        }
    }

    function safeActiveElement() {
        // Avoid IE9 `document.activeElement` of death
        try {
            return document.activeElement;
        } catch (exception) {}
    }
}));
;//*******************************************************************************************************
//  
//  File:        jqGrid.override.js
//  Author:      Vladimir Nechypurenko
//  Description: Override functionality or settings for jqGrid plug-in
//  Review:      
//
//*******************************************************************************************************

$.jgrid.formatter.number = { decimalSeparator: ".", thousandsSeparator: ",", decimalPlaces: 2, defaultValue: '0.00' };
$.jgrid.formatter.integer = { thousandsSeparator: ",", decimalPlaces: 0, defaultValue: '0' };
$.jgrid.defaults.recordtext = "Viewing {0} - {1} of {2}";
$.jgrid.defaults.loadtext = "";
$.jgrid.defaults.altRows = false;
$.jgrid.defaults.width = 698;
$.jgrid.defaults.loadui = 'enable';

$.jgrid.getAccessor = function(obj, expr) {
    var ret, p, prm, i;
    if (typeof expr === 'function') { return expr(obj); }
    ret = obj[expr];
    if (ret === undefined) {
        try {
            if (typeof expr === 'string') {
                prm = expr.split('.');
            }
            //OVERRIDE: check if prm is not undefined 
            if (prm !== undefined) { i = prm.length; }
            if (i) {
                ret = obj;
                while (ret && i--) {
                    p = prm.shift();
                    ret = ret[p];
                }
            }
        } catch (e) { }
    }
    return ret;
};

;/// <reference path="jquery-1.3.2.js" />
/// <reference path="jquery.validate.js" />

// register custom jQuery methods

jQuery.validator.addMethod("regex", function(value, element, params) {
	if (this.optional(element)) {
		return true;
	}

	var match = new RegExp(params).exec(value);
	return (match && (match.index == 0) && (match[0].length == value.length));
});

// glue

function __MVC_ApplyValidator_Range(object, min, max) {
	object["range"] = [min, max];
}

function __MVC_ApplyValidator_RegularExpression(object, pattern) {
	object["regex"] = pattern;
}

function __MVC_ApplyValidator_Required(object) {
	object["required"] = true;
}

function __MVC_ApplyValidator_StringLength(object, maxLength) {
	object["maxlength"] = maxLength;
}

function __MVC_ApplyValidator_Unknown(object, validationType, validationParameters) {
	object[validationType] = validationParameters;
}

function __MVC_CreateFieldToValidationMessageMapping(validationFields) {
	var mapping = {};

	for (var i = 0; i < validationFields.length; i++) {
		var thisField = validationFields[i];
		mapping[thisField.FieldName] = "#" + thisField.ValidationMessageId;
	}

	return mapping;
}

function __MVC_CreateErrorMessagesObject(validationFields) {
	var messagesObj = {};

	for (var i = 0; i < validationFields.length; i++) {
		var thisField = validationFields[i];
		var thisFieldMessages = {};
		messagesObj[thisField.FieldName] = thisFieldMessages;
		var validationRules = thisField.ValidationRules;

		for (var j = 0; j < validationRules.length; j++) {
			var thisRule = validationRules[j];
			if (thisRule.ErrorMessage) {
				var jQueryValidationType = thisRule.ValidationType;
				switch (thisRule.ValidationType) {
					case "regularExpression":
						jQueryValidationType = "regex";
						break;

					case "stringLength":
						jQueryValidationType = "maxlength";
						break;
				}

				thisFieldMessages[jQueryValidationType] = thisRule.ErrorMessage;
			}
		}
	}

	return messagesObj;
}

function __MVC_CreateRulesForField(validationField) {
	var validationRules = validationField.ValidationRules;

	// hook each rule into jquery
	var rulesObj = {};
	for (var i = 0; i < validationRules.length; i++) {
		var thisRule = validationRules[i];
		switch (thisRule.ValidationType) {
			case "range":
				__MVC_ApplyValidator_Range(rulesObj,
					thisRule.ValidationParameters["minimum"], thisRule.ValidationParameters["maximum"]);
				break;

			case "regularExpression":
				__MVC_ApplyValidator_RegularExpression(rulesObj,
					thisRule.ValidationParameters["pattern"]);
				break;

            case "required":
			    var elem = $("#" + validationField.FieldName).get(0); 
				if (elem && elem.type !== 'checkbox') {
					// only apply required if the input control is NOT a checkbox.
					__MVC_ApplyValidator_Required(rulesObj);
				}
				break;

			case "stringLength":
				__MVC_ApplyValidator_StringLength(rulesObj,
					thisRule.ValidationParameters["maximumLength"]);
				break;

			default:
				__MVC_ApplyValidator_Unknown(rulesObj,
					thisRule.ValidationType, thisRule.ValidationParameters);
				break;
		}
	}

	return rulesObj;
}

function __MVC_CreateValidationOptions(validationFields) {
	var rulesObj = {};
	for (var i = 0; i < validationFields.length; i++) {
		var validationField = validationFields[i];
		var fieldName = validationField.FieldName;
		rulesObj[fieldName] = __MVC_CreateRulesForField(validationField);
	}

	return rulesObj;
}

function __MVC_EnableClientValidation(validationContext) {
	// this represents the form containing elements to be validated
	var theForm = $("#" + validationContext.FormId);

	var fields = validationContext.Fields;
	var rulesObj = __MVC_CreateValidationOptions(fields);
	var fieldToMessageMappings = __MVC_CreateFieldToValidationMessageMapping(fields);
	var errorMessagesObj = __MVC_CreateErrorMessagesObject(fields);

	var options = {
		errorClass: "input-validation-error",
		errorElement: "span",
		errorPlacement: function(error, element) {
			var messageSpan = fieldToMessageMappings[element.attr("name")];
			$(messageSpan).empty();
			$(messageSpan).removeClass("field-validation-valid");
			$(messageSpan).addClass("field-validation-error");
			error.removeClass("input-validation-error");
			error.attr("_for_validation_message", messageSpan);
			error.appendTo(messageSpan);
		},
		messages: errorMessagesObj,
		rules: rulesObj,
		success: function(label) {
			var messageSpan = $(label.attr("_for_validation_message"));
			$(messageSpan).empty();
			$(messageSpan).addClass("field-validation-valid");
			$(messageSpan).removeClass("field-validation-error");
		}
	};

	var validationSummaryId = validationContext.ValidationSummaryId;
	if (validationSummaryId) {
		// insert an empty <ul> into the validation summary <div> tag (as necessary)
		$("<ul />").appendTo($("#" + validationSummaryId + ":not(:has(ul:first))"));

		options = {
			errorContainer: "#" + validationSummaryId,
			errorLabelContainer: "#" + validationSummaryId + " ul:first",
			wrapper: "li",

			showErrors: function(errorMap, errorList) {
				var errContainer = $(this.settings.errorContainer);
				var errLabelContainer = $("ul:first", errContainer);

				// Add error CSS class to user-input controls with errors
				for (var i = 0; this.errorList[i]; i++) {
					var element = this.errorList[i].element;
					var messageSpan = $(fieldToMessageMappings[element.name]);
					var msgSpanHtml = messageSpan.html();
					if (!msgSpanHtml || msgSpanHtml.length == 0) {
						// Don't override the existing Validation Message.
					    // Only if it is empty, set it to an asterisk.
						// Ev: uncomment this if you want to mark an error field:
						// messageSpan.html("*");
					}
					// Ev: uncomment this if you want to apply some error styles to an error field mark:
					// messageSpan.removeClass("field-validation-valid").addClass("field-validation-error");
					$("#" + element.id).addClass("input-validation-error");
				}
				for (var i = 0; this.successList[i]; i++) {
					// Remove error CSS class from user-input controls with zero validation errors
					var element = this.successList[i];
					var messageSpan = fieldToMessageMappings[element.name];
					$(messageSpan).addClass("field-validation-valid").removeClass("field-validation-error");
					$("#" + element.id).removeClass("input-validation-error");
				}

				if (this.numberOfInvalids() > 0) {
					errContainer.removeClass("validation-summary-valid").addClass("validation-summary-errors");
				}

				this.defaultShowErrors();

				// when server-side errors still exist in the Validation Summary, don't hide it
				var totalErrorCount = errLabelContainer.children("li:not(:has(label))").length + this.numberOfInvalids();
				if (totalErrorCount > 0) {
					$(this.settings.errorContainer).css("display", "block").addClass("validation-summary-errors").removeClass("validation-summary-valid");
					$(this.settings.errorLabelContainer).css("display", "block");
				}
			},
			messages: errorMessagesObj,
			rules: rulesObj
		};
	}

	// register callbacks with our AJAX system
	var formElement = document.getElementById(validationContext.FormId);
	var registeredValidatorCallbacks = formElement.validationCallbacks;
	if (!registeredValidatorCallbacks) {
		registeredValidatorCallbacks = [];
		formElement.validationCallbacks = registeredValidatorCallbacks;
	}
	registeredValidatorCallbacks.push(function() {
		theForm.validate();
		return theForm.valid();
	});

	theForm.validate(options);
}

// need to wait for the document to signal that it is ready
$(document).ready(function() {
	var allFormOptions = window.mvcClientValidationMetadata;
	if (allFormOptions) {
		while (allFormOptions.length > 0) {
			var thisFormOptions = allFormOptions.pop();
			__MVC_EnableClientValidation(thisFormOptions);
		}
	}
});
;/*
 Highcharts JS v6.0.7 (2018-02-16)

 (c) 2009-2016 Torstein Honsi

 License: www.highcharts.com/license
*/
(function(S,K){"object"===typeof module&&module.exports?module.exports=S.document?K(S):K:S.Highcharts=K(S)})("undefined"!==typeof window?window:this,function(S){var K=function(){var a="undefined"===typeof S?window:S,B=a.document,H=a.navigator&&a.navigator.userAgent||"",E=B&&B.createElementNS&&!!B.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect,q=/(edge|msie|trident)/i.test(H)&&!a.opera,f=-1!==H.indexOf("Firefox"),l=-1!==H.indexOf("Chrome"),t=f&&4>parseInt(H.split("Firefox/")[1],
10);return a.Highcharts?a.Highcharts.error(16,!0):{product:"Highcharts",version:"6.0.7",deg2rad:2*Math.PI/360,doc:B,hasBidiBug:t,hasTouch:B&&void 0!==B.documentElement.ontouchstart,isMS:q,isWebKit:-1!==H.indexOf("AppleWebKit"),isFirefox:f,isChrome:l,isSafari:!l&&-1!==H.indexOf("Safari"),isTouchDevice:/(Mobile|Android|Windows Phone)/.test(H),SVG_NS:"http://www.w3.org/2000/svg",chartCount:0,seriesTypes:{},symbolSizes:{},svg:E,win:a,marginNames:["plotTop","marginRight","marginBottom","plotLeft"],noop:function(){},
charts:[]}}();(function(a){a.timers=[];var B=a.charts,H=a.doc,E=a.win;a.error=function(q,f){q=a.isNumber(q)?"Highcharts error #"+q+": www.highcharts.com/errors/"+q:q;if(f)throw Error(q);E.console&&console.log(q)};a.Fx=function(a,f,l){this.options=f;this.elem=a;this.prop=l};a.Fx.prototype={dSetter:function(){var a=this.paths[0],f=this.paths[1],l=[],t=this.now,n=a.length,v;if(1===t)l=this.toD;else if(n===f.length&&1>t)for(;n--;)v=parseFloat(a[n]),l[n]=isNaN(v)?f[n]:t*parseFloat(f[n]-v)+v;else l=f;this.elem.attr("d",
l,null,!0)},update:function(){var a=this.elem,f=this.prop,l=this.now,t=this.options.step;if(this[f+"Setter"])this[f+"Setter"]();else a.attr?a.element&&a.attr(f,l,null,!0):a.style[f]=l+this.unit;t&&t.call(a,l,this)},run:function(q,f,l){var t=this,n=t.options,v=function(a){return v.stopped?!1:t.step(a)},u=E.requestAnimationFrame||function(a){setTimeout(a,13)},c=function(){for(var b=0;b<a.timers.length;b++)a.timers[b]()||a.timers.splice(b--,1);a.timers.length&&u(c)};q===f?(delete n.curAnim[this.prop],
n.complete&&0===a.keys(n.curAnim).length&&n.complete.call(this.elem)):(this.startTime=+new Date,this.start=q,this.end=f,this.unit=l,this.now=this.start,this.pos=0,v.elem=this.elem,v.prop=this.prop,v()&&1===a.timers.push(v)&&u(c))},step:function(q){var f=+new Date,l,t=this.options,n=this.elem,v=t.complete,u=t.duration,c=t.curAnim;n.attr&&!n.element?q=!1:q||f>=u+this.startTime?(this.now=this.end,this.pos=1,this.update(),l=c[this.prop]=!0,a.objectEach(c,function(a){!0!==a&&(l=!1)}),l&&v&&v.call(n),q=
!1):(this.pos=t.easing((f-this.startTime)/u),this.now=this.start+(this.end-this.start)*this.pos,this.update(),q=!0);return q},initPath:function(q,f,l){function t(a){var c,h;for(e=a.length;e--;)c="M"===a[e]||"L"===a[e],h=/[a-zA-Z]/.test(a[e+3]),c&&h&&a.splice(e+1,0,a[e+1],a[e+2],a[e+1],a[e+2])}function n(a,c){for(;a.length<h;){a[0]=c[h-a.length];var b=a.slice(0,d);[].splice.apply(a,[0,0].concat(b));p&&(b=a.slice(a.length-d),[].splice.apply(a,[a.length,0].concat(b)),e--)}a[0]="M"}function v(a,c){for(var b=
(h-a.length)/d;0<b&&b--;)k=a.slice().splice(a.length/r-d,d*r),k[0]=c[h-d-b*d],m&&(k[d-6]=k[d-2],k[d-5]=k[d-1]),[].splice.apply(a,[a.length/r,0].concat(k)),p&&b--}f=f||"";var u,c=q.startX,b=q.endX,m=-1<f.indexOf("C"),d=m?7:3,h,k,e;f=f.split(" ");l=l.slice();var p=q.isArea,r=p?2:1,I;m&&(t(f),t(l));if(c&&b){for(e=0;e<c.length;e++)if(c[e]===b[0]){u=e;break}else if(c[0]===b[b.length-c.length+e]){u=e;I=!0;break}void 0===u&&(f=[])}f.length&&a.isNumber(u)&&(h=l.length+u*r*d,I?(n(f,l),v(l,f)):(n(l,f),v(f,
l)));return[f,l]}};a.Fx.prototype.fillSetter=a.Fx.prototype.strokeSetter=function(){this.elem.attr(this.prop,a.color(this.start).tweenTo(a.color(this.end),this.pos),null,!0)};a.merge=function(){var q,f=arguments,l,t={},n=function(l,q){"object"!==typeof l&&(l={});a.objectEach(q,function(c,b){!a.isObject(c,!0)||a.isClass(c)||a.isDOMElement(c)?l[b]=q[b]:l[b]=n(l[b]||{},c)});return l};!0===f[0]&&(t=f[1],f=Array.prototype.slice.call(f,2));l=f.length;for(q=0;q<l;q++)t=n(t,f[q]);return t};a.pInt=function(a,
f){return parseInt(a,f||10)};a.isString=function(a){return"string"===typeof a};a.isArray=function(a){a=Object.prototype.toString.call(a);return"[object Array]"===a||"[object Array Iterator]"===a};a.isObject=function(q,f){return!!q&&"object"===typeof q&&(!f||!a.isArray(q))};a.isDOMElement=function(q){return a.isObject(q)&&"number"===typeof q.nodeType};a.isClass=function(q){var f=q&&q.constructor;return!(!a.isObject(q,!0)||a.isDOMElement(q)||!f||!f.name||"Object"===f.name)};a.isNumber=function(a){return"number"===
typeof a&&!isNaN(a)&&Infinity>a&&-Infinity<a};a.erase=function(a,f){for(var l=a.length;l--;)if(a[l]===f){a.splice(l,1);break}};a.defined=function(a){return void 0!==a&&null!==a};a.attr=function(q,f,l){var t;a.isString(f)?a.defined(l)?q.setAttribute(f,l):q&&q.getAttribute&&(t=q.getAttribute(f)):a.defined(f)&&a.isObject(f)&&a.objectEach(f,function(a,l){q.setAttribute(l,a)});return t};a.splat=function(q){return a.isArray(q)?q:[q]};a.syncTimeout=function(a,f,l){if(f)return setTimeout(a,f,l);a.call(0,
l)};a.extend=function(a,f){var l;a||(a={});for(l in f)a[l]=f[l];return a};a.pick=function(){var a=arguments,f,l,t=a.length;for(f=0;f<t;f++)if(l=a[f],void 0!==l&&null!==l)return l};a.css=function(q,f){a.isMS&&!a.svg&&f&&void 0!==f.opacity&&(f.filter="alpha(opacity\x3d"+100*f.opacity+")");a.extend(q.style,f)};a.createElement=function(q,f,l,t,n){q=H.createElement(q);var v=a.css;f&&a.extend(q,f);n&&v(q,{padding:0,border:"none",margin:0});l&&v(q,l);t&&t.appendChild(q);return q};a.extendClass=function(q,
f){var l=function(){};l.prototype=new q;a.extend(l.prototype,f);return l};a.pad=function(a,f,l){return Array((f||2)+1-String(a).length).join(l||0)+a};a.relativeLength=function(a,f,l){return/%$/.test(a)?f*parseFloat(a)/100+(l||0):parseFloat(a)};a.wrap=function(a,f,l){var t=a[f];a[f]=function(){var a=Array.prototype.slice.call(arguments),v=arguments,u=this;u.proceed=function(){t.apply(u,arguments.length?arguments:v)};a.unshift(t);a=l.apply(this,a);u.proceed=null;return a}};a.formatSingle=function(q,
f,l){var t=/\.([0-9])/,n=a.defaultOptions.lang;/f$/.test(q)?(l=(l=q.match(t))?l[1]:-1,null!==f&&(f=a.numberFormat(f,l,n.decimalPoint,-1<q.indexOf(",")?n.thousandsSep:""))):f=(l||a.time).dateFormat(q,f);return f};a.format=function(q,f,l){for(var t="{",n=!1,v,u,c,b,m=[],d;q;){t=q.indexOf(t);if(-1===t)break;v=q.slice(0,t);if(n){v=v.split(":");u=v.shift().split(".");b=u.length;d=f;for(c=0;c<b;c++)d&&(d=d[u[c]]);v.length&&(d=a.formatSingle(v.join(":"),d,l));m.push(d)}else m.push(v);q=q.slice(t+1);t=(n=
!n)?"}":"{"}m.push(q);return m.join("")};a.getMagnitude=function(a){return Math.pow(10,Math.floor(Math.log(a)/Math.LN10))};a.normalizeTickInterval=function(q,f,l,t,n){var v,u=q;l=a.pick(l,1);v=q/l;f||(f=n?[1,1.2,1.5,2,2.5,3,4,5,6,8,10]:[1,2,2.5,5,10],!1===t&&(1===l?f=a.grep(f,function(a){return 0===a%1}):.1>=l&&(f=[1/l])));for(t=0;t<f.length&&!(u=f[t],n&&u*l>=q||!n&&v<=(f[t]+(f[t+1]||f[t]))/2);t++);return u=a.correctFloat(u*l,-Math.round(Math.log(.001)/Math.LN10))};a.stableSort=function(a,f){var l=
a.length,t,n;for(n=0;n<l;n++)a[n].safeI=n;a.sort(function(a,l){t=f(a,l);return 0===t?a.safeI-l.safeI:t});for(n=0;n<l;n++)delete a[n].safeI};a.arrayMin=function(a){for(var f=a.length,l=a[0];f--;)a[f]<l&&(l=a[f]);return l};a.arrayMax=function(a){for(var f=a.length,l=a[0];f--;)a[f]>l&&(l=a[f]);return l};a.destroyObjectProperties=function(q,f){a.objectEach(q,function(a,t){a&&a!==f&&a.destroy&&a.destroy();delete q[t]})};a.discardElement=function(q){var f=a.garbageBin;f||(f=a.createElement("div"));q&&f.appendChild(q);
f.innerHTML=""};a.correctFloat=function(a,f){return parseFloat(a.toPrecision(f||14))};a.setAnimation=function(q,f){f.renderer.globalAnimation=a.pick(q,f.options.chart.animation,!0)};a.animObject=function(q){return a.isObject(q)?a.merge(q):{duration:q?500:0}};a.timeUnits={millisecond:1,second:1E3,minute:6E4,hour:36E5,day:864E5,week:6048E5,month:24192E5,year:314496E5};a.numberFormat=function(q,f,l,t){q=+q||0;f=+f;var n=a.defaultOptions.lang,v=(q.toString().split(".")[1]||"").split("e")[0].length,u,
c,b=q.toString().split("e");-1===f?f=Math.min(v,20):a.isNumber(f)?f&&b[1]&&0>b[1]&&(u=f+ +b[1],0<=u?(b[0]=(+b[0]).toExponential(u).split("e")[0],f=u):(b[0]=b[0].split(".")[0]||0,q=20>f?(b[0]*Math.pow(10,b[1])).toFixed(f):0,b[1]=0)):f=2;c=(Math.abs(b[1]?b[0]:q)+Math.pow(10,-Math.max(f,v)-1)).toFixed(f);v=String(a.pInt(c));u=3<v.length?v.length%3:0;l=a.pick(l,n.decimalPoint);t=a.pick(t,n.thousandsSep);q=(0>q?"-":"")+(u?v.substr(0,u)+t:"");q+=v.substr(u).replace(/(\d{3})(?=\d)/g,"$1"+t);f&&(q+=l+c.slice(-f));
b[1]&&0!==+q&&(q+="e"+b[1]);return q};Math.easeInOutSine=function(a){return-.5*(Math.cos(Math.PI*a)-1)};a.getStyle=function(q,f,l){if("width"===f)return Math.min(q.offsetWidth,q.scrollWidth)-a.getStyle(q,"padding-left")-a.getStyle(q,"padding-right");if("height"===f)return Math.min(q.offsetHeight,q.scrollHeight)-a.getStyle(q,"padding-top")-a.getStyle(q,"padding-bottom");E.getComputedStyle||a.error(27,!0);if(q=E.getComputedStyle(q,void 0))q=q.getPropertyValue(f),a.pick(l,"opacity"!==f)&&(q=a.pInt(q));
return q};a.inArray=function(q,f){return(a.indexOfPolyfill||Array.prototype.indexOf).call(f,q)};a.grep=function(q,f){return(a.filterPolyfill||Array.prototype.filter).call(q,f)};a.find=Array.prototype.find?function(a,f){return a.find(f)}:function(a,f){var l,t=a.length;for(l=0;l<t;l++)if(f(a[l],l))return a[l]};a.map=function(a,f){for(var l=[],t=0,n=a.length;t<n;t++)l[t]=f.call(a[t],a[t],t,a);return l};a.keys=function(q){return(a.keysPolyfill||Object.keys).call(void 0,q)};a.reduce=function(q,f,l){return(a.reducePolyfill||
Array.prototype.reduce).call(q,f,l)};a.offset=function(a){var f=H.documentElement;a=a.parentElement?a.getBoundingClientRect():{top:0,left:0};return{top:a.top+(E.pageYOffset||f.scrollTop)-(f.clientTop||0),left:a.left+(E.pageXOffset||f.scrollLeft)-(f.clientLeft||0)}};a.stop=function(q,f){for(var l=a.timers.length;l--;)a.timers[l].elem!==q||f&&f!==a.timers[l].prop||(a.timers[l].stopped=!0)};a.each=function(q,f,l){return(a.forEachPolyfill||Array.prototype.forEach).call(q,f,l)};a.objectEach=function(a,
f,l){for(var t in a)a.hasOwnProperty(t)&&f.call(l,a[t],t,a)};a.isPrototype=function(q){return q===a.Axis.prototype||q===a.Chart.prototype||q===a.Point.prototype||q===a.Series.prototype||q===a.Tick.prototype};a.addEvent=function(q,f,l){var t,n=q.addEventListener||a.addEventListenerPolyfill;t=a.isPrototype(q)?"protoEvents":"hcEvents";t=q[t]=q[t]||{};n&&n.call(q,f,l,!1);t[f]||(t[f]=[]);t[f].push(l);return function(){a.removeEvent(q,f,l)}};a.removeEvent=function(q,f,l){function t(c,b){var m=q.removeEventListener||
a.removeEventListenerPolyfill;m&&m.call(q,c,b,!1)}function n(c){var b,m;q.nodeName&&(f?(b={},b[f]=!0):b=c,a.objectEach(b,function(a,h){if(c[h])for(m=c[h].length;m--;)t(h,c[h][m])}))}var v,u;a.each(["protoEvents","hcEvents"],function(c){var b=q[c];b&&(f?(v=b[f]||[],l?(u=a.inArray(l,v),-1<u&&(v.splice(u,1),b[f]=v),t(f,l)):(n(b),b[f]=[])):(n(b),q[c]={}))})};a.fireEvent=function(q,f,l,t){var n,v,u,c,b;l=l||{};H.createEvent&&(q.dispatchEvent||q.fireEvent)?(n=H.createEvent("Events"),n.initEvent(f,!0,!0),
a.extend(n,l),q.dispatchEvent?q.dispatchEvent(n):q.fireEvent(f,n)):a.each(["protoEvents","hcEvents"],function(m){if(q[m])for(v=q[m][f]||[],u=v.length,l.target||a.extend(l,{preventDefault:function(){l.defaultPrevented=!0},target:q,type:f}),c=0;c<u;c++)(b=v[c])&&!1===b.call(q,l)&&l.preventDefault()});t&&!l.defaultPrevented&&t(l)};a.animate=function(q,f,l){var t,n="",v,u,c;a.isObject(l)||(c=arguments,l={duration:c[2],easing:c[3],complete:c[4]});a.isNumber(l.duration)||(l.duration=400);l.easing="function"===
typeof l.easing?l.easing:Math[l.easing]||Math.easeInOutSine;l.curAnim=a.merge(f);a.objectEach(f,function(c,m){a.stop(q,m);u=new a.Fx(q,l,m);v=null;"d"===m?(u.paths=u.initPath(q,q.d,f.d),u.toD=f.d,t=0,v=1):q.attr?t=q.attr(m):(t=parseFloat(a.getStyle(q,m))||0,"opacity"!==m&&(n="px"));v||(v=c);v&&v.match&&v.match("px")&&(v=v.replace(/px/g,""));u.run(t,v,n)})};a.seriesType=function(q,f,l,t,n){var v=a.getOptions(),u=a.seriesTypes;v.plotOptions[q]=a.merge(v.plotOptions[f],l);u[q]=a.extendClass(u[f]||function(){},
t);u[q].prototype.type=q;n&&(u[q].prototype.pointClass=a.extendClass(a.Point,n));return u[q]};a.uniqueKey=function(){var a=Math.random().toString(36).substring(2,9),f=0;return function(){return"highcharts-"+a+"-"+f++}}();E.jQuery&&(E.jQuery.fn.highcharts=function(){var q=[].slice.call(arguments);if(this[0])return q[0]?(new (a[a.isString(q[0])?q.shift():"Chart"])(this[0],q[0],q[1]),this):B[a.attr(this[0],"data-highcharts-chart")]})})(K);(function(a){var B=a.each,H=a.isNumber,E=a.map,q=a.merge,f=a.pInt;
a.Color=function(l){if(!(this instanceof a.Color))return new a.Color(l);this.init(l)};a.Color.prototype={parsers:[{regex:/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]?(?:\.[0-9]+)?)\s*\)/,parse:function(a){return[f(a[1]),f(a[2]),f(a[3]),parseFloat(a[4],10)]}},{regex:/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/,parse:function(a){return[f(a[1]),f(a[2]),f(a[3]),1]}}],names:{none:"rgba(255,255,255,0)",white:"#ffffff",black:"#000000"},init:function(l){var f,
n,v,u;if((this.input=l=this.names[l&&l.toLowerCase?l.toLowerCase():""]||l)&&l.stops)this.stops=E(l.stops,function(c){return new a.Color(c[1])});else if(l&&l.charAt&&"#"===l.charAt()&&(f=l.length,l=parseInt(l.substr(1),16),7===f?n=[(l&16711680)>>16,(l&65280)>>8,l&255,1]:4===f&&(n=[(l&3840)>>4|(l&3840)>>8,(l&240)>>4|l&240,(l&15)<<4|l&15,1])),!n)for(v=this.parsers.length;v--&&!n;)u=this.parsers[v],(f=u.regex.exec(l))&&(n=u.parse(f));this.rgba=n||[]},get:function(a){var l=this.input,n=this.rgba,v;this.stops?
(v=q(l),v.stops=[].concat(v.stops),B(this.stops,function(n,c){v.stops[c]=[v.stops[c][0],n.get(a)]})):v=n&&H(n[0])?"rgb"===a||!a&&1===n[3]?"rgb("+n[0]+","+n[1]+","+n[2]+")":"a"===a?n[3]:"rgba("+n.join(",")+")":l;return v},brighten:function(a){var l,n=this.rgba;if(this.stops)B(this.stops,function(n){n.brighten(a)});else if(H(a)&&0!==a)for(l=0;3>l;l++)n[l]+=f(255*a),0>n[l]&&(n[l]=0),255<n[l]&&(n[l]=255);return this},setOpacity:function(a){this.rgba[3]=a;return this},tweenTo:function(a,f){var n=this.rgba,
l=a.rgba;l.length&&n&&n.length?(a=1!==l[3]||1!==n[3],f=(a?"rgba(":"rgb(")+Math.round(l[0]+(n[0]-l[0])*(1-f))+","+Math.round(l[1]+(n[1]-l[1])*(1-f))+","+Math.round(l[2]+(n[2]-l[2])*(1-f))+(a?","+(l[3]+(n[3]-l[3])*(1-f)):"")+")"):f=a.input||"none";return f}};a.color=function(l){return new a.Color(l)}})(K);(function(a){var B,H,E=a.addEvent,q=a.animate,f=a.attr,l=a.charts,t=a.color,n=a.css,v=a.createElement,u=a.defined,c=a.deg2rad,b=a.destroyObjectProperties,m=a.doc,d=a.each,h=a.extend,k=a.erase,e=a.grep,
p=a.hasTouch,r=a.inArray,I=a.isArray,z=a.isFirefox,M=a.isMS,D=a.isObject,C=a.isString,x=a.isWebKit,F=a.merge,A=a.noop,J=a.objectEach,G=a.pick,g=a.pInt,w=a.removeEvent,L=a.stop,P=a.svg,N=a.SVG_NS,O=a.symbolSizes,R=a.win;B=a.SVGElement=function(){return this};h(B.prototype,{opacity:1,SVG_NS:N,textProps:"direction fontSize fontWeight fontFamily fontStyle color lineHeight width textAlign textDecoration textOverflow textOutline".split(" "),init:function(a,g){this.element="span"===g?v(g):m.createElementNS(this.SVG_NS,
g);this.renderer=a},animate:function(y,g,c){g=a.animObject(G(g,this.renderer.globalAnimation,!0));0!==g.duration?(c&&(g.complete=c),q(this,y,g)):(this.attr(y,null,c),g.step&&g.step.call(this));return this},colorGradient:function(y,g,c){var w=this.renderer,h,b,e,k,p,Q,x,N,m,A,r=[],P;y.radialGradient?b="radialGradient":y.linearGradient&&(b="linearGradient");b&&(e=y[b],p=w.gradients,x=y.stops,A=c.radialReference,I(e)&&(y[b]=e={x1:e[0],y1:e[1],x2:e[2],y2:e[3],gradientUnits:"userSpaceOnUse"}),"radialGradient"===
b&&A&&!u(e.gradientUnits)&&(k=e,e=F(e,w.getRadialAttr(A,k),{gradientUnits:"userSpaceOnUse"})),J(e,function(a,y){"id"!==y&&r.push(y,a)}),J(x,function(a){r.push(a)}),r=r.join(","),p[r]?A=p[r].attr("id"):(e.id=A=a.uniqueKey(),p[r]=Q=w.createElement(b).attr(e).add(w.defs),Q.radAttr=k,Q.stops=[],d(x,function(y){0===y[1].indexOf("rgba")?(h=a.color(y[1]),N=h.get("rgb"),m=h.get("a")):(N=y[1],m=1);y=w.createElement("stop").attr({offset:y[0],"stop-color":N,"stop-opacity":m}).add(Q);Q.stops.push(y)})),P="url("+
w.url+"#"+A+")",c.setAttribute(g,P),c.gradient=r,y.toString=function(){return P})},applyTextOutline:function(y){var g=this.element,c,w,h,e,b;-1!==y.indexOf("contrast")&&(y=y.replace(/contrast/g,this.renderer.getContrast(g.style.fill)));y=y.split(" ");w=y[y.length-1];if((h=y[0])&&"none"!==h&&a.svg){this.fakeTS=!0;y=[].slice.call(g.getElementsByTagName("tspan"));this.ySetter=this.xSetter;h=h.replace(/(^[\d\.]+)(.*?)$/g,function(a,y,g){return 2*y+g});for(b=y.length;b--;)c=y[b],"highcharts-text-outline"===
c.getAttribute("class")&&k(y,g.removeChild(c));e=g.firstChild;d(y,function(a,y){0===y&&(a.setAttribute("x",g.getAttribute("x")),y=g.getAttribute("y"),a.setAttribute("y",y||0),null===y&&g.setAttribute("y",0));a=a.cloneNode(1);f(a,{"class":"highcharts-text-outline",fill:w,stroke:w,"stroke-width":h,"stroke-linejoin":"round"});g.insertBefore(a,e)})}},attr:function(a,g,c,w){var y,h=this.element,b,e=this,d,k;"string"===typeof a&&void 0!==g&&(y=a,a={},a[y]=g);"string"===typeof a?e=(this[a+"Getter"]||this._defaultGetter).call(this,
a,h):(J(a,function(y,g){d=!1;w||L(this,g);this.symbolName&&/^(x|y|width|height|r|start|end|innerR|anchorX|anchorY)$/.test(g)&&(b||(this.symbolAttr(a),b=!0),d=!0);!this.rotation||"x"!==g&&"y"!==g||(this.doTransform=!0);d||(k=this[g+"Setter"]||this._defaultSetter,k.call(this,y,g,h),this.shadows&&/^(width|height|visibility|x|y|d|transform|cx|cy|r)$/.test(g)&&this.updateShadows(g,y,k))},this),this.afterSetters());c&&c.call(this);return e},afterSetters:function(){this.doTransform&&(this.updateTransform(),
this.doTransform=!1)},updateShadows:function(a,g,c){for(var y=this.shadows,w=y.length;w--;)c.call(y[w],"height"===a?Math.max(g-(y[w].cutHeight||0),0):"d"===a?this.d:g,a,y[w])},addClass:function(a,g){var y=this.attr("class")||"";-1===y.indexOf(a)&&(g||(a=(y+(y?" ":"")+a).replace("  "," ")),this.attr("class",a));return this},hasClass:function(a){return-1!==r(a,(this.attr("class")||"").split(" "))},removeClass:function(a){return this.attr("class",(this.attr("class")||"").replace(a,""))},symbolAttr:function(a){var y=
this;d("x y r start end width height innerR anchorX anchorY".split(" "),function(g){y[g]=G(a[g],y[g])});y.attr({d:y.renderer.symbols[y.symbolName](y.x,y.y,y.width,y.height,y)})},clip:function(a){return this.attr("clip-path",a?"url("+this.renderer.url+"#"+a.id+")":"none")},crisp:function(a,g){var y;g=g||a.strokeWidth||0;y=Math.round(g)%2/2;a.x=Math.floor(a.x||this.x||0)+y;a.y=Math.floor(a.y||this.y||0)+y;a.width=Math.floor((a.width||this.width||0)-2*y);a.height=Math.floor((a.height||this.height||0)-
2*y);u(a.strokeWidth)&&(a.strokeWidth=g);return a},css:function(a){var y=this.styles,c={},w=this.element,b,e="",d,k=!y,p=["textOutline","textOverflow","width"];a&&a.color&&(a.fill=a.color);y&&J(a,function(a,g){a!==y[g]&&(c[g]=a,k=!0)});k&&(y&&(a=h(y,c)),b=this.textWidth=a&&a.width&&"auto"!==a.width&&"text"===w.nodeName.toLowerCase()&&g(a.width),this.styles=a,b&&!P&&this.renderer.forExport&&delete a.width,w.namespaceURI===this.SVG_NS?(d=function(a,y){return"-"+y.toLowerCase()},J(a,function(a,y){-1===
r(y,p)&&(e+=y.replace(/([A-Z])/g,d)+":"+a+";")}),e&&f(w,"style",e)):n(w,a),this.added&&("text"===this.element.nodeName&&this.renderer.buildText(this),a&&a.textOutline&&this.applyTextOutline(a.textOutline)));return this},strokeWidth:function(){return this["stroke-width"]||0},on:function(a,g){var y=this,c=y.element;p&&"click"===a?(c.ontouchstart=function(a){y.touchEventFired=Date.now();a.preventDefault();g.call(c,a)},c.onclick=function(a){(-1===R.navigator.userAgent.indexOf("Android")||1100<Date.now()-
(y.touchEventFired||0))&&g.call(c,a)}):c["on"+a]=g;return this},setRadialReference:function(a){var y=this.renderer.gradients[this.element.gradient];this.element.radialReference=a;y&&y.radAttr&&y.animate(this.renderer.getRadialAttr(a,y.radAttr));return this},translate:function(a,g){return this.attr({translateX:a,translateY:g})},invert:function(a){this.inverted=a;this.updateTransform();return this},updateTransform:function(){var a=this.translateX||0,g=this.translateY||0,c=this.scaleX,w=this.scaleY,
h=this.inverted,e=this.rotation,b=this.matrix,d=this.element;h&&(a+=this.width,g+=this.height);a=["translate("+a+","+g+")"];u(b)&&a.push("matrix("+b.join(",")+")");h?a.push("rotate(90) scale(-1,1)"):e&&a.push("rotate("+e+" "+G(this.rotationOriginX,d.getAttribute("x"),0)+" "+G(this.rotationOriginY,d.getAttribute("y")||0)+")");(u(c)||u(w))&&a.push("scale("+G(c,1)+" "+G(w,1)+")");a.length&&d.setAttribute("transform",a.join(" "))},toFront:function(){var a=this.element;a.parentNode.appendChild(a);return this},
align:function(a,g,c){var y,w,h,e,d={};w=this.renderer;h=w.alignedObjects;var b,p;if(a){if(this.alignOptions=a,this.alignByTranslate=g,!c||C(c))this.alignTo=y=c||"renderer",k(h,this),h.push(this),c=null}else a=this.alignOptions,g=this.alignByTranslate,y=this.alignTo;c=G(c,w[y],w);y=a.align;w=a.verticalAlign;h=(c.x||0)+(a.x||0);e=(c.y||0)+(a.y||0);"right"===y?b=1:"center"===y&&(b=2);b&&(h+=(c.width-(a.width||0))/b);d[g?"translateX":"x"]=Math.round(h);"bottom"===w?p=1:"middle"===w&&(p=2);p&&(e+=(c.height-
(a.height||0))/p);d[g?"translateY":"y"]=Math.round(e);this[this.placed?"animate":"attr"](d);this.placed=!0;this.alignAttr=d;return this},getBBox:function(a,g){var y,w=this.renderer,b,e=this.element,k=this.styles,p,x=this.textStr,N,m=w.cache,A=w.cacheKeys,F;g=G(g,this.rotation);b=g*c;p=k&&k.fontSize;u(x)&&(F=x.toString(),-1===F.indexOf("\x3c")&&(F=F.replace(/[0-9]/g,"0")),F+=["",g||0,p,k&&k.width,k&&k.textOverflow].join());F&&!a&&(y=m[F]);if(!y){if(e.namespaceURI===this.SVG_NS||w.forExport){try{(N=
this.fakeTS&&function(a){d(e.querySelectorAll(".highcharts-text-outline"),function(y){y.style.display=a})})&&N("none"),y=e.getBBox?h({},e.getBBox()):{width:e.offsetWidth,height:e.offsetHeight},N&&N("")}catch(W){}if(!y||0>y.width)y={width:0,height:0}}else y=this.htmlGetBBox();w.isSVG&&(a=y.width,w=y.height,k&&"11px"===k.fontSize&&17===Math.round(w)&&(y.height=w=14),g&&(y.width=Math.abs(w*Math.sin(b))+Math.abs(a*Math.cos(b)),y.height=Math.abs(w*Math.cos(b))+Math.abs(a*Math.sin(b))));if(F&&0<y.height){for(;250<
A.length;)delete m[A.shift()];m[F]||A.push(F);m[F]=y}}return y},show:function(a){return this.attr({visibility:a?"inherit":"visible"})},hide:function(){return this.attr({visibility:"hidden"})},fadeOut:function(a){var y=this;y.animate({opacity:0},{duration:a||150,complete:function(){y.attr({y:-9999})}})},add:function(a){var y=this.renderer,g=this.element,c;a&&(this.parentGroup=a);this.parentInverted=a&&a.inverted;void 0!==this.textStr&&y.buildText(this);this.added=!0;if(!a||a.handleZ||this.zIndex)c=
this.zIndexSetter();c||(a?a.element:y.box).appendChild(g);if(this.onAdd)this.onAdd();return this},safeRemoveChild:function(a){var y=a.parentNode;y&&y.removeChild(a)},destroy:function(){var a=this,g=a.element||{},c=a.renderer.isSVG&&"SPAN"===g.nodeName&&a.parentGroup,w=g.ownerSVGElement,h=a.clipPath;g.onclick=g.onmouseout=g.onmouseover=g.onmousemove=g.point=null;L(a);h&&w&&(d(w.querySelectorAll("[clip-path],[CLIP-PATH]"),function(a){var g=a.getAttribute("clip-path"),y=h.element.id;(-1<g.indexOf("(#"+
y+")")||-1<g.indexOf('("#'+y+'")'))&&a.removeAttribute("clip-path")}),a.clipPath=h.destroy());if(a.stops){for(w=0;w<a.stops.length;w++)a.stops[w]=a.stops[w].destroy();a.stops=null}a.safeRemoveChild(g);for(a.destroyShadows();c&&c.div&&0===c.div.childNodes.length;)g=c.parentGroup,a.safeRemoveChild(c.div),delete c.div,c=g;a.alignTo&&k(a.renderer.alignedObjects,a);J(a,function(g,y){delete a[y]});return null},shadow:function(a,g,c){var y=[],w,h,e=this.element,b,d,k,p;if(!a)this.destroyShadows();else if(!this.shadows){d=
G(a.width,3);k=(a.opacity||.15)/d;p=this.parentInverted?"(-1,-1)":"("+G(a.offsetX,1)+", "+G(a.offsetY,1)+")";for(w=1;w<=d;w++)h=e.cloneNode(0),b=2*d+1-2*w,f(h,{isShadow:"true",stroke:a.color||"#000000","stroke-opacity":k*w,"stroke-width":b,transform:"translate"+p,fill:"none"}),c&&(f(h,"height",Math.max(f(h,"height")-b,0)),h.cutHeight=b),g?g.element.appendChild(h):e.parentNode&&e.parentNode.insertBefore(h,e),y.push(h);this.shadows=y}return this},destroyShadows:function(){d(this.shadows||[],function(a){this.safeRemoveChild(a)},
this);this.shadows=void 0},xGetter:function(a){"circle"===this.element.nodeName&&("x"===a?a="cx":"y"===a&&(a="cy"));return this._defaultGetter(a)},_defaultGetter:function(a){a=G(this[a+"Value"],this[a],this.element?this.element.getAttribute(a):null,0);/^[\-0-9\.]+$/.test(a)&&(a=parseFloat(a));return a},dSetter:function(a,g,c){a&&a.join&&(a=a.join(" "));/(NaN| {2}|^$)/.test(a)&&(a="M 0 0");this[g]!==a&&(c.setAttribute(g,a),this[g]=a)},dashstyleSetter:function(a){var y,c=this["stroke-width"];"inherit"===
c&&(c=1);if(a=a&&a.toLowerCase()){a=a.replace("shortdashdotdot","3,1,1,1,1,1,").replace("shortdashdot","3,1,1,1").replace("shortdot","1,1,").replace("shortdash","3,1,").replace("longdash","8,3,").replace(/dot/g,"1,3,").replace("dash","4,3,").replace(/,$/,"").split(",");for(y=a.length;y--;)a[y]=g(a[y])*c;a=a.join(",").replace(/NaN/g,"none");this.element.setAttribute("stroke-dasharray",a)}},alignSetter:function(a){this.alignValue=a;this.element.setAttribute("text-anchor",{left:"start",center:"middle",
right:"end"}[a])},opacitySetter:function(a,g,c){this[g]=a;c.setAttribute(g,a)},titleSetter:function(a){var g=this.element.getElementsByTagName("title")[0];g||(g=m.createElementNS(this.SVG_NS,"title"),this.element.appendChild(g));g.firstChild&&g.removeChild(g.firstChild);g.appendChild(m.createTextNode(String(G(a),"").replace(/<[^>]*>/g,"").replace(/&lt;/g,"\x3c").replace(/&gt;/g,"\x3e")))},textSetter:function(a){a!==this.textStr&&(delete this.bBox,this.textStr=a,this.added&&this.renderer.buildText(this))},
fillSetter:function(a,g,c){"string"===typeof a?c.setAttribute(g,a):a&&this.colorGradient(a,g,c)},visibilitySetter:function(a,g,c){"inherit"===a?c.removeAttribute(g):this[g]!==a&&c.setAttribute(g,a);this[g]=a},zIndexSetter:function(a,c){var w=this.renderer,y=this.parentGroup,h=(y||w).element||w.box,e,b=this.element,d,k,w=h===w.box;e=this.added;var p;u(a)&&(b.zIndex=a,a=+a,this[c]===a&&(e=!1),this[c]=a);if(e){(a=this.zIndex)&&y&&(y.handleZ=!0);c=h.childNodes;for(p=c.length-1;0<=p&&!d;p--)if(y=c[p],
e=y.zIndex,k=!u(e),y!==b)if(0>a&&k&&!w&&!p)h.insertBefore(b,c[p]),d=!0;else if(g(e)<=a||k&&(!u(a)||0<=a))h.insertBefore(b,c[p+1]||null),d=!0;d||(h.insertBefore(b,c[w?3:0]||null),d=!0)}return d},_defaultSetter:function(a,g,c){c.setAttribute(g,a)}});B.prototype.yGetter=B.prototype.xGetter;B.prototype.translateXSetter=B.prototype.translateYSetter=B.prototype.rotationSetter=B.prototype.verticalAlignSetter=B.prototype.rotationOriginXSetter=B.prototype.rotationOriginYSetter=B.prototype.scaleXSetter=B.prototype.scaleYSetter=
B.prototype.matrixSetter=function(a,g){this[g]=a;this.doTransform=!0};B.prototype["stroke-widthSetter"]=B.prototype.strokeSetter=function(a,g,c){this[g]=a;this.stroke&&this["stroke-width"]?(B.prototype.fillSetter.call(this,this.stroke,"stroke",c),c.setAttribute("stroke-width",this["stroke-width"]),this.hasStroke=!0):"stroke-width"===g&&0===a&&this.hasStroke&&(c.removeAttribute("stroke"),this.hasStroke=!1)};H=a.SVGRenderer=function(){this.init.apply(this,arguments)};h(H.prototype,{Element:B,SVG_NS:N,
init:function(a,g,c,w,h,e){var y;w=this.createElement("svg").attr({version:"1.1","class":"highcharts-root"}).css(this.getStyle(w));y=w.element;a.appendChild(y);f(a,"dir","ltr");-1===a.innerHTML.indexOf("xmlns")&&f(y,"xmlns",this.SVG_NS);this.isSVG=!0;this.box=y;this.boxWrapper=w;this.alignedObjects=[];this.url=(z||x)&&m.getElementsByTagName("base").length?R.location.href.replace(/#.*?$/,"").replace(/<[^>]*>/g,"").replace(/([\('\)])/g,"\\$1").replace(/ /g,"%20"):"";this.createElement("desc").add().element.appendChild(m.createTextNode("Created with Highcharts 6.0.7"));
this.defs=this.createElement("defs").add();this.allowHTML=e;this.forExport=h;this.gradients={};this.cache={};this.cacheKeys=[];this.imgCount=0;this.setSize(g,c,!1);var b;z&&a.getBoundingClientRect&&(g=function(){n(a,{left:0,top:0});b=a.getBoundingClientRect();n(a,{left:Math.ceil(b.left)-b.left+"px",top:Math.ceil(b.top)-b.top+"px"})},g(),this.unSubPixelFix=E(R,"resize",g))},getStyle:function(a){return this.style=h({fontFamily:'"Lucida Grande", "Lucida Sans Unicode", Arial, Helvetica, sans-serif',fontSize:"12px"},
a)},setStyle:function(a){this.boxWrapper.css(this.getStyle(a))},isHidden:function(){return!this.boxWrapper.getBBox().width},destroy:function(){var a=this.defs;this.box=null;this.boxWrapper=this.boxWrapper.destroy();b(this.gradients||{});this.gradients=null;a&&(this.defs=a.destroy());this.unSubPixelFix&&this.unSubPixelFix();return this.alignedObjects=null},createElement:function(a){var g=new this.Element;g.init(this,a);return g},draw:A,getRadialAttr:function(a,g){return{cx:a[0]-a[2]/2+g.cx*a[2],cy:a[1]-
a[2]/2+g.cy*a[2],r:g.r*a[2]}},getSpanWidth:function(a){return a.getBBox(!0).width},applyEllipsis:function(a,g,c,w){var h=a.rotation,y=c,b,e=0,d=c.length,k=function(a){g.removeChild(g.firstChild);a&&g.appendChild(m.createTextNode(a))},p;a.rotation=0;y=this.getSpanWidth(a,g);if(p=y>w){for(;e<=d;)b=Math.ceil((e+d)/2),y=c.substring(0,b)+"\u2026",k(y),y=this.getSpanWidth(a,g),e===d?e=d+1:y>w?d=b-1:e=b;0===d&&k("")}a.rotation=h;return p},escapes:{"\x26":"\x26amp;","\x3c":"\x26lt;","\x3e":"\x26gt;","'":"\x26#39;",
'"':"\x26quot;"},buildText:function(a){var c=a.element,w=this,h=w.forExport,b=G(a.textStr,"").toString(),y=-1!==b.indexOf("\x3c"),k=c.childNodes,p,x,A,F,z=f(c,"x"),L=a.styles,O=a.textWidth,l=L&&L.lineHeight,D=L&&L.textOutline,C=L&&"ellipsis"===L.textOverflow,v=L&&"nowrap"===L.whiteSpace,u=L&&L.fontSize,M,t,I=k.length,L=O&&!a.added&&this.box,R=function(a){var h;h=/(px|em)$/.test(a&&a.style.fontSize)?a.style.fontSize:u||w.style.fontSize||12;return l?g(l):w.fontMetrics(h,a.getAttribute("style")?a:c).h},
q=function(a,g){J(w.escapes,function(c,w){g&&-1!==r(c,g)||(a=a.toString().replace(new RegExp(c,"g"),w))});return a};M=[b,C,v,l,D,u,O].join();if(M!==a.textCache){for(a.textCache=M;I--;)c.removeChild(k[I]);y||D||C||O||-1!==b.indexOf(" ")?(p=/<.*class="([^"]+)".*>/,x=/<.*style="([^"]+)".*>/,A=/<.*href="([^"]+)".*>/,L&&L.appendChild(c),b=y?b.replace(/<(b|strong)>/g,'\x3cspan style\x3d"font-weight:bold"\x3e').replace(/<(i|em)>/g,'\x3cspan style\x3d"font-style:italic"\x3e').replace(/<a/g,"\x3cspan").replace(/<\/(b|strong|i|em|a)>/g,
"\x3c/span\x3e").split(/<br.*?>/g):[b],b=e(b,function(a){return""!==a}),d(b,function(g,b){var e,y=0;g=g.replace(/^\s+|\s+$/g,"").replace(/<span/g,"|||\x3cspan").replace(/<\/span>/g,"\x3c/span\x3e|||");e=g.split("|||");d(e,function(g){if(""!==g||1===e.length){var d={},k=m.createElementNS(w.SVG_NS,"tspan"),r,L;p.test(g)&&(r=g.match(p)[1],f(k,"class",r));x.test(g)&&(L=g.match(x)[1].replace(/(;| |^)color([ :])/,"$1fill$2"),f(k,"style",L));A.test(g)&&!h&&(f(k,"onclick",'location.href\x3d"'+g.match(A)[1]+
'"'),f(k,"class","highcharts-anchor"),n(k,{cursor:"pointer"}));g=q(g.replace(/<[a-zA-Z\/](.|\n)*?>/g,"")||" ");if(" "!==g){k.appendChild(m.createTextNode(g));y?d.dx=0:b&&null!==z&&(d.x=z);f(k,d);c.appendChild(k);!y&&t&&(!P&&h&&n(k,{display:"block"}),f(k,"dy",R(k)));if(O){d=g.replace(/([^\^])-/g,"$1- ").split(" ");r=1<e.length||b||1<d.length&&!v;var l=[],D,J=R(k),G=a.rotation;for(C&&(F=w.applyEllipsis(a,k,g,O));!C&&r&&(d.length||l.length);)a.rotation=0,D=w.getSpanWidth(a,k),g=D>O,void 0===F&&(F=g),
g&&1!==d.length?(k.removeChild(k.firstChild),l.unshift(d.pop())):(d=l,l=[],d.length&&!v&&(k=m.createElementNS(N,"tspan"),f(k,{dy:J,x:z}),L&&f(k,"style",L),c.appendChild(k)),D>O&&(O=D)),d.length&&k.appendChild(m.createTextNode(d.join(" ").replace(/- /g,"-")));a.rotation=G}y++}}});t=t||c.childNodes.length}),F&&a.attr("title",q(a.textStr,["\x26lt;","\x26gt;"])),L&&L.removeChild(c),D&&a.applyTextOutline&&a.applyTextOutline(D)):c.appendChild(m.createTextNode(q(b)))}},getContrast:function(a){a=t(a).rgba;
return 510<a[0]+a[1]+a[2]?"#000000":"#FFFFFF"},button:function(a,g,c,w,b,d,e,k,p){var y=this.label(a,g,c,p,null,null,null,null,"button"),x=0;y.attr(F({padding:8,r:2},b));var N,m,A,r;b=F({fill:"#f7f7f7",stroke:"#cccccc","stroke-width":1,style:{color:"#333333",cursor:"pointer",fontWeight:"normal"}},b);N=b.style;delete b.style;d=F(b,{fill:"#e6e6e6"},d);m=d.style;delete d.style;e=F(b,{fill:"#e6ebf5",style:{color:"#000000",fontWeight:"bold"}},e);A=e.style;delete e.style;k=F(b,{style:{color:"#cccccc"}},
k);r=k.style;delete k.style;E(y.element,M?"mouseover":"mouseenter",function(){3!==x&&y.setState(1)});E(y.element,M?"mouseout":"mouseleave",function(){3!==x&&y.setState(x)});y.setState=function(a){1!==a&&(y.state=x=a);y.removeClass(/highcharts-button-(normal|hover|pressed|disabled)/).addClass("highcharts-button-"+["normal","hover","pressed","disabled"][a||0]);y.attr([b,d,e,k][a||0]).css([N,m,A,r][a||0])};y.attr(b).css(h({cursor:"default"},N));return y.on("click",function(a){3!==x&&w.call(y,a)})},crispLine:function(a,
g){a[1]===a[4]&&(a[1]=a[4]=Math.round(a[1])-g%2/2);a[2]===a[5]&&(a[2]=a[5]=Math.round(a[2])+g%2/2);return a},path:function(a){var g={fill:"none"};I(a)?g.d=a:D(a)&&h(g,a);return this.createElement("path").attr(g)},circle:function(a,g,c){a=D(a)?a:{x:a,y:g,r:c};g=this.createElement("circle");g.xSetter=g.ySetter=function(a,g,c){c.setAttribute("c"+g,a)};return g.attr(a)},arc:function(a,g,c,w,b,h){D(a)?(w=a,g=w.y,c=w.r,a=w.x):w={innerR:w,start:b,end:h};a=this.symbol("arc",a,g,c,c,w);a.r=c;return a},rect:function(a,
g,c,w,b,h){b=D(a)?a.r:b;var d=this.createElement("rect");a=D(a)?a:void 0===a?{}:{x:a,y:g,width:Math.max(c,0),height:Math.max(w,0)};void 0!==h&&(a.strokeWidth=h,a=d.crisp(a));a.fill="none";b&&(a.r=b);d.rSetter=function(a,g,c){f(c,{rx:a,ry:a})};return d.attr(a)},setSize:function(a,g,c){var w=this.alignedObjects,b=w.length;this.width=a;this.height=g;for(this.boxWrapper.animate({width:a,height:g},{step:function(){this.attr({viewBox:"0 0 "+this.attr("width")+" "+this.attr("height")})},duration:G(c,!0)?
void 0:0});b--;)w[b].align()},g:function(a){var g=this.createElement("g");return a?g.attr({"class":"highcharts-"+a}):g},image:function(a,g,c,w,b){var d={preserveAspectRatio:"none"};1<arguments.length&&h(d,{x:g,y:c,width:w,height:b});d=this.createElement("image").attr(d);d.element.setAttributeNS?d.element.setAttributeNS("http://www.w3.org/1999/xlink","href",a):d.element.setAttribute("hc-svg-href",a);return d},symbol:function(a,g,c,w,b,e){var k=this,p,y=/^url\((.*?)\)$/,x=y.test(a),N=!x&&(this.symbols[a]?
a:"circle"),A=N&&this.symbols[N],F=u(g)&&A&&A.call(this.symbols,Math.round(g),Math.round(c),w,b,e),r,L;A?(p=this.path(F),p.attr("fill","none"),h(p,{symbolName:N,x:g,y:c,width:w,height:b}),e&&h(p,e)):x&&(r=a.match(y)[1],p=this.image(r),p.imgwidth=G(O[r]&&O[r].width,e&&e.width),p.imgheight=G(O[r]&&O[r].height,e&&e.height),L=function(){p.attr({width:p.width,height:p.height})},d(["width","height"],function(a){p[a+"Setter"]=function(a,g){var c={},w=this["img"+g],b="width"===g?"translateX":"translateY";
this[g]=a;u(w)&&(this.element&&this.element.setAttribute(g,w),this.alignByTranslate||(c[b]=((this[g]||0)-w)/2,this.attr(c)))}}),u(g)&&p.attr({x:g,y:c}),p.isImg=!0,u(p.imgwidth)&&u(p.imgheight)?L():(p.attr({width:0,height:0}),v("img",{onload:function(){var a=l[k.chartIndex];0===this.width&&(n(this,{position:"absolute",top:"-999em"}),m.body.appendChild(this));O[r]={width:this.width,height:this.height};p.imgwidth=this.width;p.imgheight=this.height;p.element&&L();this.parentNode&&this.parentNode.removeChild(this);
k.imgCount--;if(!k.imgCount&&a&&a.onload)a.onload()},src:r}),this.imgCount++));return p},symbols:{circle:function(a,g,c,w){return this.arc(a+c/2,g+w/2,c/2,w/2,{start:0,end:2*Math.PI,open:!1})},square:function(a,g,c,w){return["M",a,g,"L",a+c,g,a+c,g+w,a,g+w,"Z"]},triangle:function(a,g,c,w){return["M",a+c/2,g,"L",a+c,g+w,a,g+w,"Z"]},"triangle-down":function(a,g,c,w){return["M",a,g,"L",a+c,g,a+c/2,g+w,"Z"]},diamond:function(a,g,c,w){return["M",a+c/2,g,"L",a+c,g+w/2,a+c/2,g+w,a,g+w/2,"Z"]},arc:function(a,
g,c,w,b){var h=b.start,d=b.r||c,e=b.r||w||c,k=b.end-.001;c=b.innerR;w=G(b.open,.001>Math.abs(b.end-b.start-2*Math.PI));var p=Math.cos(h),y=Math.sin(h),x=Math.cos(k),k=Math.sin(k);b=.001>b.end-h-Math.PI?0:1;d=["M",a+d*p,g+e*y,"A",d,e,0,b,1,a+d*x,g+e*k];u(c)&&d.push(w?"M":"L",a+c*x,g+c*k,"A",c,c,0,b,0,a+c*p,g+c*y);d.push(w?"":"Z");return d},callout:function(a,g,c,w,b){var h=Math.min(b&&b.r||0,c,w),d=h+6,e=b&&b.anchorX;b=b&&b.anchorY;var k;k=["M",a+h,g,"L",a+c-h,g,"C",a+c,g,a+c,g,a+c,g+h,"L",a+c,g+w-
h,"C",a+c,g+w,a+c,g+w,a+c-h,g+w,"L",a+h,g+w,"C",a,g+w,a,g+w,a,g+w-h,"L",a,g+h,"C",a,g,a,g,a+h,g];e&&e>c?b>g+d&&b<g+w-d?k.splice(13,3,"L",a+c,b-6,a+c+6,b,a+c,b+6,a+c,g+w-h):k.splice(13,3,"L",a+c,w/2,e,b,a+c,w/2,a+c,g+w-h):e&&0>e?b>g+d&&b<g+w-d?k.splice(33,3,"L",a,b+6,a-6,b,a,b-6,a,g+h):k.splice(33,3,"L",a,w/2,e,b,a,w/2,a,g+h):b&&b>w&&e>a+d&&e<a+c-d?k.splice(23,3,"L",e+6,g+w,e,g+w+6,e-6,g+w,a+h,g+w):b&&0>b&&e>a+d&&e<a+c-d&&k.splice(3,3,"L",e-6,g,e,g-6,e+6,g,c-h,g);return k}},clipRect:function(g,c,w,
b){var h=a.uniqueKey(),d=this.createElement("clipPath").attr({id:h}).add(this.defs);g=this.rect(g,c,w,b,0).add(d);g.id=h;g.clipPath=d;g.count=0;return g},text:function(a,g,c,w){var b={};if(w&&(this.allowHTML||!this.forExport))return this.html(a,g,c);b.x=Math.round(g||0);c&&(b.y=Math.round(c));if(a||0===a)b.text=a;a=this.createElement("text").attr(b);w||(a.xSetter=function(a,g,c){var w=c.getElementsByTagName("tspan"),b,h=c.getAttribute(g),d;for(d=0;d<w.length;d++)b=w[d],b.getAttribute(g)===h&&b.setAttribute(g,
a);c.setAttribute(g,a)});return a},fontMetrics:function(a,c){a=a||c&&c.style&&c.style.fontSize||this.style&&this.style.fontSize;a=/px/.test(a)?g(a):/em/.test(a)?parseFloat(a)*(c?this.fontMetrics(null,c.parentNode).f:16):12;c=24>a?a+3:Math.round(1.2*a);return{h:c,b:Math.round(.8*c),f:a}},rotCorr:function(a,g,w){var b=a;g&&w&&(b=Math.max(b*Math.cos(g*c),4));return{x:-a/3*Math.sin(g*c),y:b}},label:function(g,c,b,e,k,p,x,N,m){var A=this,r=A.g("button"!==m&&"label"),y=r.text=A.text("",0,0,x).attr({zIndex:1}),
L,P,z=0,O=3,l=0,n,D,f,J,C,G={},v,M,t=/^url\((.*?)\)$/.test(e),I=t,R,q,Q,T;m&&r.addClass("highcharts-"+m);I=t;R=function(){return(v||0)%2/2};q=function(){var a=y.element.style,g={};P=(void 0===n||void 0===D||C)&&u(y.textStr)&&y.getBBox();r.width=(n||P.width||0)+2*O+l;r.height=(D||P.height||0)+2*O;M=O+A.fontMetrics(a&&a.fontSize,y).b;I&&(L||(r.box=L=A.symbols[e]||t?A.symbol(e):A.rect(),L.addClass(("button"===m?"":"highcharts-label-box")+(m?" highcharts-"+m+"-box":"")),L.add(r),a=R(),g.x=a,g.y=(N?-M:
0)+a),g.width=Math.round(r.width),g.height=Math.round(r.height),L.attr(h(g,G)),G={})};Q=function(){var a=l+O,g;g=N?0:M;u(n)&&P&&("center"===C||"right"===C)&&(a+={center:.5,right:1}[C]*(n-P.width));if(a!==y.x||g!==y.y)y.attr("x",a),void 0!==g&&y.attr("y",g);y.x=a;y.y=g};T=function(a,g){L?L.attr(a,g):G[a]=g};r.onAdd=function(){y.add(r);r.attr({text:g||0===g?g:"",x:c,y:b});L&&u(k)&&r.attr({anchorX:k,anchorY:p})};r.widthSetter=function(g){n=a.isNumber(g)?g:null};r.heightSetter=function(a){D=a};r["text-alignSetter"]=
function(a){C=a};r.paddingSetter=function(a){u(a)&&a!==O&&(O=r.padding=a,Q())};r.paddingLeftSetter=function(a){u(a)&&a!==l&&(l=a,Q())};r.alignSetter=function(a){a={left:0,center:.5,right:1}[a];a!==z&&(z=a,P&&r.attr({x:f}))};r.textSetter=function(a){void 0!==a&&y.textSetter(a);q();Q()};r["stroke-widthSetter"]=function(a,g){a&&(I=!0);v=this["stroke-width"]=a;T(g,a)};r.strokeSetter=r.fillSetter=r.rSetter=function(a,g){"r"!==g&&("fill"===g&&a&&(I=!0),r[g]=a);T(g,a)};r.anchorXSetter=function(a,g){k=r.anchorX=
a;T(g,Math.round(a)-R()-f)};r.anchorYSetter=function(a,g){p=r.anchorY=a;T(g,a-J)};r.xSetter=function(a){r.x=a;z&&(a-=z*((n||P.width)+2*O));f=Math.round(a);r.attr("translateX",f)};r.ySetter=function(a){J=r.y=Math.round(a);r.attr("translateY",J)};var U=r.css;return h(r,{css:function(a){if(a){var g={};a=F(a);d(r.textProps,function(c){void 0!==a[c]&&(g[c]=a[c],delete a[c])});y.css(g)}return U.call(r,a)},getBBox:function(){return{width:P.width+2*O,height:P.height+2*O,x:P.x-O,y:P.y-O}},shadow:function(a){a&&
(q(),L&&L.shadow(a));return r},destroy:function(){w(r.element,"mouseenter");w(r.element,"mouseleave");y&&(y=y.destroy());L&&(L=L.destroy());B.prototype.destroy.call(r);r=A=q=Q=T=null}})}});a.Renderer=H})(K);(function(a){var B=a.attr,H=a.createElement,E=a.css,q=a.defined,f=a.each,l=a.extend,t=a.isFirefox,n=a.isMS,v=a.isWebKit,u=a.pick,c=a.pInt,b=a.SVGRenderer,m=a.win,d=a.wrap;l(a.SVGElement.prototype,{htmlCss:function(a){var c=this.element;if(c=a&&"SPAN"===c.tagName&&a.width)delete a.width,this.textWidth=
c,this.updateTransform();a&&"ellipsis"===a.textOverflow&&(a.whiteSpace="nowrap",a.overflow="hidden");this.styles=l(this.styles,a);E(this.element,a);return this},htmlGetBBox:function(){var a=this.element;return{x:a.offsetLeft,y:a.offsetTop,width:a.offsetWidth,height:a.offsetHeight}},htmlUpdateTransform:function(){if(this.added){var a=this.renderer,b=this.element,d=this.translateX||0,p=this.translateY||0,r=this.x||0,m=this.y||0,z=this.textAlign||"left",l={left:0,center:.5,right:1}[z],n=this.styles,
C=n&&n.whiteSpace;E(b,{marginLeft:d,marginTop:p});this.shadows&&f(this.shadows,function(a){E(a,{marginLeft:d+1,marginTop:p+1})});this.inverted&&f(b.childNodes,function(c){a.invertChild(c,b)});if("SPAN"===b.tagName){var n=this.rotation,x=this.textWidth&&c(this.textWidth),F=[n,z,b.innerHTML,this.textWidth,this.textAlign].join(),A;(A=x!==this.oldTextWidth)&&!(A=x>this.oldTextWidth)&&((A=this.textPxLength)||(E(b,{width:"",whiteSpace:C||"nowrap"}),A=b.offsetWidth),A=A>x);A&&/[ \-]/.test(b.textContent||
b.innerText)&&(E(b,{width:x+"px",display:"block",whiteSpace:C||"normal"}),this.oldTextWidth=x);F!==this.cTT&&(C=a.fontMetrics(b.style.fontSize).b,q(n)&&n!==(this.oldRotation||0)&&this.setSpanRotation(n,l,C),this.getSpanCorrection(this.textPxLength||b.offsetWidth,C,l,n,z));E(b,{left:r+(this.xCorr||0)+"px",top:m+(this.yCorr||0)+"px"});this.cTT=F;this.oldRotation=n}}else this.alignOnAdd=!0},setSpanRotation:function(a,c,b){var d={},e=this.renderer.getTransformKey();d[e]=d.transform="rotate("+a+"deg)";
d[e+(t?"Origin":"-origin")]=d.transformOrigin=100*c+"% "+b+"px";E(this.element,d)},getSpanCorrection:function(a,c,b){this.xCorr=-a*b;this.yCorr=-c}});l(b.prototype,{getTransformKey:function(){return n&&!/Edge/.test(m.navigator.userAgent)?"-ms-transform":v?"-webkit-transform":t?"MozTransform":m.opera?"-o-transform":""},html:function(a,c,b){var e=this.createElement("span"),h=e.element,k=e.renderer,m=k.isSVG,n=function(a,c){f(["opacity","visibility"],function(b){d(a,b+"Setter",function(a,b,e,d){a.call(this,
b,e,d);c[e]=b})})};e.textSetter=function(a){a!==h.innerHTML&&delete this.bBox;this.textStr=a;h.innerHTML=u(a,"");e.doTransform=!0};m&&n(e,e.element.style);e.xSetter=e.ySetter=e.alignSetter=e.rotationSetter=function(a,c){"align"===c&&(c="textAlign");e[c]=a;e.doTransform=!0};e.afterSetters=function(){this.doTransform&&(this.htmlUpdateTransform(),this.doTransform=!1)};e.attr({text:a,x:Math.round(c),y:Math.round(b)}).css({fontFamily:this.style.fontFamily,fontSize:this.style.fontSize,position:"absolute"});
h.style.whiteSpace="nowrap";e.css=e.htmlCss;m&&(e.add=function(a){var c,b=k.box.parentNode,d=[];if(this.parentGroup=a){if(c=a.div,!c){for(;a;)d.push(a),a=a.parentGroup;f(d.reverse(),function(a){function h(g,c){a[c]=g;"translateX"===c?k.left=g+"px":k.top=g+"px";a.doTransform=!0}var k,g=B(a.element,"class");g&&(g={className:g});c=a.div=a.div||H("div",g,{position:"absolute",left:(a.translateX||0)+"px",top:(a.translateY||0)+"px",display:a.display,opacity:a.opacity,pointerEvents:a.styles&&a.styles.pointerEvents},
c||b);k=c.style;l(a,{classSetter:function(a){return function(g){this.element.setAttribute("class",g);a.className=g}}(c),on:function(){d[0].div&&e.on.apply({element:d[0].div},arguments);return a},translateXSetter:h,translateYSetter:h});n(a,k)})}}else c=b;c.appendChild(h);e.added=!0;e.alignOnAdd&&e.htmlUpdateTransform();return e});return e}})})(K);(function(a){var B=a.defined,H=a.each,E=a.extend,q=a.merge,f=a.pick,l=a.timeUnits,t=a.win;a.Time=function(a){this.update(a,!1)};a.Time.prototype={defaultOptions:{},
update:function(n){var l=f(n&&n.useUTC,!0),u=this;this.options=n=q(!0,this.options||{},n);this.Date=n.Date||t.Date;this.timezoneOffset=(this.useUTC=l)&&n.timezoneOffset;this.getTimezoneOffset=this.timezoneOffsetFunction();(this.variableTimezone=!(l&&!n.getTimezoneOffset&&!n.timezone))||this.timezoneOffset?(this.get=function(a,b){var c=b.getTime(),d=c-u.getTimezoneOffset(b);b.setTime(d);a=b["getUTC"+a]();b.setTime(c);return a},this.set=function(c,b,m){var d;if(-1!==a.inArray(c,["Milliseconds","Seconds",
"Minutes"]))b["set"+c](m);else d=u.getTimezoneOffset(b),d=b.getTime()-d,b.setTime(d),b["setUTC"+c](m),c=u.getTimezoneOffset(b),d=b.getTime()+c,b.setTime(d)}):l?(this.get=function(a,b){return b["getUTC"+a]()},this.set=function(a,b,m){return b["setUTC"+a](m)}):(this.get=function(a,b){return b["get"+a]()},this.set=function(a,b,m){return b["set"+a](m)})},makeTime:function(l,v,u,c,b,m){var d,h,k;this.useUTC?(d=this.Date.UTC.apply(0,arguments),h=this.getTimezoneOffset(d),d+=h,k=this.getTimezoneOffset(d),
h!==k?d+=k-h:h-36E5!==this.getTimezoneOffset(d-36E5)||a.isSafari||(d-=36E5)):d=(new this.Date(l,v,f(u,1),f(c,0),f(b,0),f(m,0))).getTime();return d},timezoneOffsetFunction:function(){var l=this,f=this.options,u=t.moment;if(!this.useUTC)return function(a){return 6E4*(new Date(a)).getTimezoneOffset()};if(f.timezone){if(u)return function(a){return 6E4*-u.tz(a,f.timezone).utcOffset()};a.error(25)}return this.useUTC&&f.getTimezoneOffset?function(a){return 6E4*f.getTimezoneOffset(a)}:function(){return 6E4*
(l.timezoneOffset||0)}},dateFormat:function(l,f,u){if(!a.defined(f)||isNaN(f))return a.defaultOptions.lang.invalidDate||"";l=a.pick(l,"%Y-%m-%d %H:%M:%S");var c=this,b=new this.Date(f),m=this.get("Hours",b),d=this.get("Day",b),h=this.get("Date",b),k=this.get("Month",b),e=this.get("FullYear",b),p=a.defaultOptions.lang,r=p.weekdays,n=p.shortWeekdays,z=a.pad,b=a.extend({a:n?n[d]:r[d].substr(0,3),A:r[d],d:z(h),e:z(h,2," "),w:d,b:p.shortMonths[k],B:p.months[k],m:z(k+1),y:e.toString().substr(2,2),Y:e,H:z(m),
k:m,I:z(m%12||12),l:m%12||12,M:z(c.get("Minutes",b)),p:12>m?"AM":"PM",P:12>m?"am":"pm",S:z(b.getSeconds()),L:z(Math.round(f%1E3),3)},a.dateFormats);a.objectEach(b,function(a,b){for(;-1!==l.indexOf("%"+b);)l=l.replace("%"+b,"function"===typeof a?a.call(c,f):a)});return u?l.substr(0,1).toUpperCase()+l.substr(1):l},getTimeTicks:function(a,v,u,c){var b=this,m=[],d={},h,k=new b.Date(v),e=a.unitRange,p=a.count||1,r;if(B(v)){b.set("Milliseconds",k,e>=l.second?0:p*Math.floor(b.get("Milliseconds",k)/p));e>=
l.second&&b.set("Seconds",k,e>=l.minute?0:p*Math.floor(b.get("Seconds",k)/p));e>=l.minute&&b.set("Minutes",k,e>=l.hour?0:p*Math.floor(b.get("Minutes",k)/p));e>=l.hour&&b.set("Hours",k,e>=l.day?0:p*Math.floor(b.get("Hours",k)/p));e>=l.day&&b.set("Date",k,e>=l.month?1:p*Math.floor(b.get("Date",k)/p));e>=l.month&&(b.set("Month",k,e>=l.year?0:p*Math.floor(b.get("Month",k)/p)),h=b.get("FullYear",k));e>=l.year&&b.set("FullYear",k,h-h%p);e===l.week&&b.set("Date",k,b.get("Date",k)-b.get("Day",k)+f(c,1));
h=b.get("FullYear",k);c=b.get("Month",k);var n=b.get("Date",k),z=b.get("Hours",k);v=k.getTime();b.variableTimezone&&(r=u-v>4*l.month||b.getTimezoneOffset(v)!==b.getTimezoneOffset(u));k=k.getTime();for(v=1;k<u;)m.push(k),k=e===l.year?b.makeTime(h+v*p,0):e===l.month?b.makeTime(h,c+v*p):!r||e!==l.day&&e!==l.week?r&&e===l.hour&&1<p?b.makeTime(h,c,n,z+v*p):k+e*p:b.makeTime(h,c,n+v*p*(e===l.day?1:7)),v++;m.push(k);e<=l.hour&&1E4>m.length&&H(m,function(a){0===a%18E5&&"000000000"===b.dateFormat("%H%M%S%L",
a)&&(d[a]="day")})}m.info=E(a,{higherRanks:d,totalRange:e*p});return m}}})(K);(function(a){var B=a.color,H=a.merge;a.defaultOptions={colors:"#7cb5ec #434348 #90ed7d #f7a35c #8085e9 #f15c80 #e4d354 #2b908f #f45b5b #91e8e1".split(" "),symbols:["circle","diamond","square","triangle","triangle-down"],lang:{loading:"Loading...",months:"January February March April May June July August September October November December".split(" "),shortMonths:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),
weekdays:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),decimalPoint:".",numericSymbols:"kMGTPE".split(""),resetZoom:"Reset zoom",resetZoomTitle:"Reset zoom level 1:1",thousandsSep:" "},global:{},time:a.Time.prototype.defaultOptions,chart:{borderRadius:0,defaultSeriesType:"line",ignoreHiddenSeries:!0,spacing:[10,10,15,10],resetZoomButton:{theme:{zIndex:6},position:{align:"right",x:-10,y:10}},width:null,height:null,borderColor:"#335cad",backgroundColor:"#ffffff",plotBorderColor:"#cccccc"},
title:{text:"Chart title",align:"center",margin:15,widthAdjust:-44},subtitle:{text:"",align:"center",widthAdjust:-44},plotOptions:{},labels:{style:{position:"absolute",color:"#333333"}},legend:{enabled:!0,align:"center",layout:"horizontal",labelFormatter:function(){return this.name},borderColor:"#999999",borderRadius:0,navigation:{activeColor:"#003399",inactiveColor:"#cccccc"},itemStyle:{color:"#333333",fontSize:"12px",fontWeight:"bold",textOverflow:"ellipsis"},itemHoverStyle:{color:"#000000"},itemHiddenStyle:{color:"#cccccc"},
shadow:!1,itemCheckboxStyle:{position:"absolute",width:"13px",height:"13px"},squareSymbol:!0,symbolPadding:5,verticalAlign:"bottom",x:0,y:0,title:{style:{fontWeight:"bold"}}},loading:{labelStyle:{fontWeight:"bold",position:"relative",top:"45%"},style:{position:"absolute",backgroundColor:"#ffffff",opacity:.5,textAlign:"center"}},tooltip:{enabled:!0,animation:a.svg,borderRadius:3,dateTimeLabelFormats:{millisecond:"%A, %b %e, %H:%M:%S.%L",second:"%A, %b %e, %H:%M:%S",minute:"%A, %b %e, %H:%M",hour:"%A, %b %e, %H:%M",
day:"%A, %b %e, %Y",week:"Week from %A, %b %e, %Y",month:"%B %Y",year:"%Y"},footerFormat:"",padding:8,snap:a.isTouchDevice?25:10,backgroundColor:B("#f7f7f7").setOpacity(.85).get(),borderWidth:1,headerFormat:'\x3cspan style\x3d"font-size: 10px"\x3e{point.key}\x3c/span\x3e\x3cbr/\x3e',pointFormat:'\x3cspan style\x3d"color:{point.color}"\x3e\u25cf\x3c/span\x3e {series.name}: \x3cb\x3e{point.y}\x3c/b\x3e\x3cbr/\x3e',shadow:!0,style:{color:"#333333",cursor:"default",fontSize:"12px",pointerEvents:"none",
whiteSpace:"nowrap"}},credits:{enabled:!0,href:"http://www.highcharts.com",position:{align:"right",x:-10,verticalAlign:"bottom",y:-5},style:{cursor:"pointer",color:"#999999",fontSize:"9px"},text:"Highcharts.com"}};a.setOptions=function(B){a.defaultOptions=H(!0,a.defaultOptions,B);a.time.update(H(a.defaultOptions.global,a.defaultOptions.time),!1);return a.defaultOptions};a.getOptions=function(){return a.defaultOptions};a.defaultPlotOptions=a.defaultOptions.plotOptions;a.time=new a.Time(H(a.defaultOptions.global,
a.defaultOptions.time));a.dateFormat=function(B,q,f){return a.time.dateFormat(B,q,f)}})(K);(function(a){var B=a.correctFloat,H=a.defined,E=a.destroyObjectProperties,q=a.isNumber,f=a.merge,l=a.pick,t=a.deg2rad;a.Tick=function(a,l,f,c){this.axis=a;this.pos=l;this.type=f||"";this.isNewLabel=this.isNew=!0;f||c||this.addLabel()};a.Tick.prototype={addLabel:function(){var a=this.axis,v=a.options,u=a.chart,c=a.categories,b=a.names,m=this.pos,d=v.labels,h=a.tickPositions,k=m===h[0],e=m===h[h.length-1],b=c?
l(c[m],b[m],m):m,c=this.label,h=h.info,p;a.isDatetimeAxis&&h&&(p=v.dateTimeLabelFormats[h.higherRanks[m]||h.unitName]);this.isFirst=k;this.isLast=e;v=a.labelFormatter.call({axis:a,chart:u,isFirst:k,isLast:e,dateTimeLabelFormat:p,value:a.isLog?B(a.lin2log(b)):b,pos:m});if(H(c))c&&c.attr({text:v});else{if(this.label=c=H(v)&&d.enabled?u.renderer.text(v,0,0,d.useHTML).css(f(d.style)).add(a.labelGroup):null)c.textPxLength=c.getBBox().width;this.rotation=0}},getLabelSize:function(){return this.label?this.label.getBBox()[this.axis.horiz?
"height":"width"]:0},handleOverflow:function(a){var f=this.axis,n=f.options.labels,c=a.x,b=f.chart.chartWidth,m=f.chart.spacing,d=l(f.labelLeft,Math.min(f.pos,m[3])),m=l(f.labelRight,Math.max(f.isRadial?0:f.pos+f.len,b-m[1])),h=this.label,k=this.rotation,e={left:0,center:.5,right:1}[f.labelAlign||h.attr("align")],p=h.getBBox().width,r=f.getSlotWidth(),I=r,z=1,M,D={};if(k||!1===n.overflow)0>k&&c-e*p<d?M=Math.round(c/Math.cos(k*t)-d):0<k&&c+e*p>m&&(M=Math.round((b-c)/Math.cos(k*t)));else if(b=c+(1-
e)*p,c-e*p<d?I=a.x+I*(1-e)-d:b>m&&(I=m-a.x+I*e,z=-1),I=Math.min(r,I),I<r&&"center"===f.labelAlign&&(a.x+=z*(r-I-e*(r-Math.min(p,I)))),p>I||f.autoRotation&&(h.styles||{}).width)M=I;M&&(D.width=M,(n.style||{}).textOverflow||(D.textOverflow="ellipsis"),h.css(D))},getPosition:function(l,f,u,c){var b=this.axis,m=b.chart,d=c&&m.oldChartHeight||m.chartHeight;return{x:l?a.correctFloat(b.translate(f+u,null,null,c)+b.transB):b.left+b.offset+(b.opposite?(c&&m.oldChartWidth||m.chartWidth)-b.right-b.left:0),y:l?
d-b.bottom+b.offset-(b.opposite?b.height:0):a.correctFloat(d-b.translate(f+u,null,null,c)-b.transB)}},getLabelPosition:function(a,l,f,c,b,m,d,h){var k=this.axis,e=k.transA,p=k.reversed,r=k.staggerLines,n=k.tickRotCorr||{x:0,y:0},z=b.y,u=c||k.reserveSpaceDefault?0:-k.labelOffset*("center"===k.labelAlign?.5:1);H(z)||(z=0===k.side?f.rotation?-8:-f.getBBox().height:2===k.side?n.y+8:Math.cos(f.rotation*t)*(n.y-f.getBBox(!1,0).height/2));a=a+b.x+u+n.x-(m&&c?m*e*(p?-1:1):0);l=l+z-(m&&!c?m*e*(p?1:-1):0);
r&&(f=d/(h||1)%r,k.opposite&&(f=r-f-1),l+=k.labelOffset/r*f);return{x:a,y:Math.round(l)}},getMarkPath:function(a,l,f,c,b,m){return m.crispLine(["M",a,l,"L",a+(b?0:-f),l+(b?f:0)],c)},renderGridLine:function(a,l,f){var c=this.axis,b=c.options,m=this.gridLine,d={},h=this.pos,k=this.type,e=c.tickmarkOffset,p=c.chart.renderer,r=k?k+"Grid":"grid",n=b[r+"LineWidth"],z=b[r+"LineColor"],b=b[r+"LineDashStyle"];m||(d.stroke=z,d["stroke-width"]=n,b&&(d.dashstyle=b),k||(d.zIndex=1),a&&(d.opacity=0),this.gridLine=
m=p.path().attr(d).addClass("highcharts-"+(k?k+"-":"")+"grid-line").add(c.gridGroup));if(!a&&m&&(a=c.getPlotLinePath(h+e,m.strokeWidth()*f,a,!0)))m[this.isNew?"attr":"animate"]({d:a,opacity:l})},renderMark:function(a,f,u){var c=this.axis,b=c.options,m=c.chart.renderer,d=this.type,h=d?d+"Tick":"tick",k=c.tickSize(h),e=this.mark,p=!e,r=a.x;a=a.y;var n=l(b[h+"Width"],!d&&c.isXAxis?1:0),b=b[h+"Color"];k&&(c.opposite&&(k[0]=-k[0]),p&&(this.mark=e=m.path().addClass("highcharts-"+(d?d+"-":"")+"tick").add(c.axisGroup),
e.attr({stroke:b,"stroke-width":n})),e[p?"attr":"animate"]({d:this.getMarkPath(r,a,k[0],e.strokeWidth()*u,c.horiz,m),opacity:f}))},renderLabel:function(a,f,u,c){var b=this.axis,m=b.horiz,d=b.options,h=this.label,k=d.labels,e=k.step,b=b.tickmarkOffset,p=!0,r=a.x;a=a.y;h&&q(r)&&(h.xy=a=this.getLabelPosition(r,a,h,m,k,b,c,e),this.isFirst&&!this.isLast&&!l(d.showFirstLabel,1)||this.isLast&&!this.isFirst&&!l(d.showLastLabel,1)?p=!1:!m||k.step||k.rotation||f||0===u||this.handleOverflow(a),e&&c%e&&(p=!1),
p&&q(a.y)?(a.opacity=u,h[this.isNewLabel?"attr":"animate"](a),this.isNewLabel=!1):(h.attr("y",-9999),this.isNewLabel=!0))},render:function(f,t,u){var c=this.axis,b=c.horiz,m=this.getPosition(b,this.pos,c.tickmarkOffset,t),d=m.x,h=m.y,c=b&&d===c.pos+c.len||!b&&h===c.pos?-1:1;u=l(u,1);this.isActive=!0;this.renderGridLine(t,u,c);this.renderMark(m,u,c);this.renderLabel(m,t,u,f);this.isNew=!1;a.fireEvent(this,"afterRender")},destroy:function(){E(this,this.axis)}}})(K);var V=function(a){var B=a.addEvent,
H=a.animObject,E=a.arrayMax,q=a.arrayMin,f=a.color,l=a.correctFloat,t=a.defaultOptions,n=a.defined,v=a.deg2rad,u=a.destroyObjectProperties,c=a.each,b=a.extend,m=a.fireEvent,d=a.format,h=a.getMagnitude,k=a.grep,e=a.inArray,p=a.isArray,r=a.isNumber,I=a.isString,z=a.merge,M=a.normalizeTickInterval,D=a.objectEach,C=a.pick,x=a.removeEvent,F=a.splat,A=a.syncTimeout,J=a.Tick,G=function(){this.init.apply(this,arguments)};a.extend(G.prototype,{defaultOptions:{dateTimeLabelFormats:{millisecond:"%H:%M:%S.%L",
second:"%H:%M:%S",minute:"%H:%M",hour:"%H:%M",day:"%e. %b",week:"%e. %b",month:"%b '%y",year:"%Y"},endOnTick:!1,labels:{enabled:!0,style:{color:"#666666",cursor:"default",fontSize:"11px"},x:0},maxPadding:.01,minorTickLength:2,minorTickPosition:"outside",minPadding:.01,startOfWeek:1,startOnTick:!1,tickLength:10,tickmarkPlacement:"between",tickPixelInterval:100,tickPosition:"outside",title:{align:"middle",style:{color:"#666666"}},type:"linear",minorGridLineColor:"#f2f2f2",minorGridLineWidth:1,minorTickColor:"#999999",
lineColor:"#ccd6eb",lineWidth:1,gridLineColor:"#e6e6e6",tickColor:"#ccd6eb"},defaultYAxisOptions:{endOnTick:!0,tickPixelInterval:72,showLastLabel:!0,labels:{x:-8},maxPadding:.05,minPadding:.05,startOnTick:!0,title:{rotation:270,text:"Values"},stackLabels:{allowOverlap:!1,enabled:!1,formatter:function(){return a.numberFormat(this.total,-1)},style:{fontSize:"11px",fontWeight:"bold",color:"#000000",textOutline:"1px contrast"}},gridLineWidth:1,lineWidth:0},defaultLeftAxisOptions:{labels:{x:-15},title:{rotation:270}},
defaultRightAxisOptions:{labels:{x:15},title:{rotation:90}},defaultBottomAxisOptions:{labels:{autoRotation:[-45],x:0},title:{rotation:0}},defaultTopAxisOptions:{labels:{autoRotation:[-45],x:0},title:{rotation:0}},init:function(a,c){var g=c.isX,b=this;b.chart=a;b.horiz=a.inverted&&!b.isZAxis?!g:g;b.isXAxis=g;b.coll=b.coll||(g?"xAxis":"yAxis");b.opposite=c.opposite;b.side=c.side||(b.horiz?b.opposite?0:2:b.opposite?1:3);b.setOptions(c);var w=this.options,d=w.type;b.labelFormatter=w.labels.formatter||
b.defaultLabelFormatter;b.userOptions=c;b.minPixelPadding=0;b.reversed=w.reversed;b.visible=!1!==w.visible;b.zoomEnabled=!1!==w.zoomEnabled;b.hasNames="category"===d||!0===w.categories;b.categories=w.categories||b.hasNames;b.names||(b.names=[],b.names.keys={});b.plotLinesAndBandsGroups={};b.isLog="logarithmic"===d;b.isDatetimeAxis="datetime"===d;b.positiveValuesOnly=b.isLog&&!b.allowNegativeLog;b.isLinked=n(w.linkedTo);b.ticks={};b.labelEdge=[];b.minorTicks={};b.plotLinesAndBands=[];b.alternateBands=
{};b.len=0;b.minRange=b.userMinRange=w.minRange||w.maxZoom;b.range=w.range;b.offset=w.offset||0;b.stacks={};b.oldStacks={};b.stacksTouched=0;b.max=null;b.min=null;b.crosshair=C(w.crosshair,F(a.options.tooltip.crosshairs)[g?0:1],!1);c=b.options.events;-1===e(b,a.axes)&&(g?a.axes.splice(a.xAxis.length,0,b):a.axes.push(b),a[b.coll].push(b));b.series=b.series||[];a.inverted&&!b.isZAxis&&g&&void 0===b.reversed&&(b.reversed=!0);D(c,function(a,g){B(b,g,a)});b.lin2log=w.linearToLogConverter||b.lin2log;b.isLog&&
(b.val2lin=b.log2lin,b.lin2val=b.lin2log)},setOptions:function(a){this.options=z(this.defaultOptions,"yAxis"===this.coll&&this.defaultYAxisOptions,[this.defaultTopAxisOptions,this.defaultRightAxisOptions,this.defaultBottomAxisOptions,this.defaultLeftAxisOptions][this.side],z(t[this.coll],a))},defaultLabelFormatter:function(){var g=this.axis,c=this.value,b=g.chart.time,e=g.categories,h=this.dateTimeLabelFormat,k=t.lang,p=k.numericSymbols,k=k.numericSymbolMagnitude||1E3,x=p&&p.length,r,m=g.options.labels.format,
g=g.isLog?Math.abs(c):g.tickInterval;if(m)r=d(m,this,b);else if(e)r=c;else if(h)r=b.dateFormat(h,c);else if(x&&1E3<=g)for(;x--&&void 0===r;)b=Math.pow(k,x+1),g>=b&&0===10*c%b&&null!==p[x]&&0!==c&&(r=a.numberFormat(c/b,-1)+p[x]);void 0===r&&(r=1E4<=Math.abs(c)?a.numberFormat(c,-1):a.numberFormat(c,-1,void 0,""));return r},getSeriesExtremes:function(){var a=this,b=a.chart;a.hasVisibleSeries=!1;a.dataMin=a.dataMax=a.threshold=null;a.softThreshold=!a.isXAxis;a.buildStacks&&a.buildStacks();c(a.series,
function(g){if(g.visible||!b.options.chart.ignoreHiddenSeries){var c=g.options,w=c.threshold,d;a.hasVisibleSeries=!0;a.positiveValuesOnly&&0>=w&&(w=null);if(a.isXAxis)c=g.xData,c.length&&(g=q(c),d=E(c),r(g)||g instanceof Date||(c=k(c,r),g=q(c),d=E(c)),c.length&&(a.dataMin=Math.min(C(a.dataMin,c[0],g),g),a.dataMax=Math.max(C(a.dataMax,c[0],d),d)));else if(g.getExtremes(),d=g.dataMax,g=g.dataMin,n(g)&&n(d)&&(a.dataMin=Math.min(C(a.dataMin,g),g),a.dataMax=Math.max(C(a.dataMax,d),d)),n(w)&&(a.threshold=
w),!c.softThreshold||a.positiveValuesOnly)a.softThreshold=!1}})},translate:function(a,c,b,d,e,h){var g=this.linkedParent||this,w=1,k=0,p=d?g.oldTransA:g.transA;d=d?g.oldMin:g.min;var x=g.minPixelPadding;e=(g.isOrdinal||g.isBroken||g.isLog&&e)&&g.lin2val;p||(p=g.transA);b&&(w*=-1,k=g.len);g.reversed&&(w*=-1,k-=w*(g.sector||g.len));c?(a=(a*w+k-x)/p+d,e&&(a=g.lin2val(a))):(e&&(a=g.val2lin(a)),a=r(d)?w*(a-d)*p+k+w*x+(r(h)?p*h:0):void 0);return a},toPixels:function(a,c){return this.translate(a,!1,!this.horiz,
null,!0)+(c?0:this.pos)},toValue:function(a,c){return this.translate(a-(c?0:this.pos),!0,!this.horiz,null,!0)},getPlotLinePath:function(a,c,b,d,e){var g=this.chart,w=this.left,h=this.top,k,p,x=b&&g.oldChartHeight||g.chartHeight,m=b&&g.oldChartWidth||g.chartWidth,N;k=this.transB;var A=function(a,g,c){if(a<g||a>c)d?a=Math.min(Math.max(g,a),c):N=!0;return a};e=C(e,this.translate(a,null,null,b));e=Math.min(Math.max(-1E5,e),1E5);a=b=Math.round(e+k);k=p=Math.round(x-e-k);r(e)?this.horiz?(k=h,p=x-this.bottom,
a=b=A(a,w,w+this.width)):(a=w,b=m-this.right,k=p=A(k,h,h+this.height)):(N=!0,d=!1);return N&&!d?null:g.renderer.crispLine(["M",a,k,"L",b,p],c||1)},getLinearTickPositions:function(a,c,b){var g,w=l(Math.floor(c/a)*a);b=l(Math.ceil(b/a)*a);var d=[],e;l(w+a)===w&&(e=20);if(this.single)return[c];for(c=w;c<=b;){d.push(c);c=l(c+a,e);if(c===g)break;g=c}return d},getMinorTickInterval:function(){var a=this.options;return!0===a.minorTicks?C(a.minorTickInterval,"auto"):!1===a.minorTicks?null:a.minorTickInterval},
getMinorTickPositions:function(){var a=this,b=a.options,d=a.tickPositions,e=a.minorTickInterval,h=[],k=a.pointRangePadding||0,p=a.min-k,k=a.max+k,x=k-p;if(x&&x/e<a.len/3)if(a.isLog)c(this.paddedTicks,function(g,c,b){c&&h.push.apply(h,a.getLogTickPositions(e,b[c-1],b[c],!0))});else if(a.isDatetimeAxis&&"auto"===this.getMinorTickInterval())h=h.concat(a.getTimeTicks(a.normalizeTimeTickInterval(e),p,k,b.startOfWeek));else for(b=p+(d[0]-p)%e;b<=k&&b!==h[0];b+=e)h.push(b);0!==h.length&&a.trimTicks(h);return h},
adjustForMinRange:function(){var a=this.options,b=this.min,d=this.max,e,h,k,p,x,r,m,A;this.isXAxis&&void 0===this.minRange&&!this.isLog&&(n(a.min)||n(a.max)?this.minRange=null:(c(this.series,function(a){r=a.xData;for(p=m=a.xIncrement?1:r.length-1;0<p;p--)if(x=r[p]-r[p-1],void 0===k||x<k)k=x}),this.minRange=Math.min(5*k,this.dataMax-this.dataMin)));d-b<this.minRange&&(h=this.dataMax-this.dataMin>=this.minRange,A=this.minRange,e=(A-d+b)/2,e=[b-e,C(a.min,b-e)],h&&(e[2]=this.isLog?this.log2lin(this.dataMin):
this.dataMin),b=E(e),d=[b+A,C(a.max,b+A)],h&&(d[2]=this.isLog?this.log2lin(this.dataMax):this.dataMax),d=q(d),d-b<A&&(e[0]=d-A,e[1]=C(a.min,d-A),b=E(e)));this.min=b;this.max=d},getClosest:function(){var a;this.categories?a=1:c(this.series,function(g){var c=g.closestPointRange,b=g.visible||!g.chart.options.chart.ignoreHiddenSeries;!g.noSharedTooltip&&n(c)&&b&&(a=n(a)?Math.min(a,c):c)});return a},nameToX:function(a){var g=p(this.categories),c=g?this.categories:this.names,b=a.options.x,d;a.series.requireSorting=
!1;n(b)||(b=!1===this.options.uniqueNames?a.series.autoIncrement():g?e(a.name,c):C(c.keys[a.name],-1));-1===b?g||(d=c.length):d=b;void 0!==d&&(this.names[d]=a.name,this.names.keys[a.name]=d);return d},updateNames:function(){var g=this,b=this.names;0<b.length&&(c(a.keys(b.keys),function(a){delete b.keys[a]}),b.length=0,this.minRange=this.userMinRange,c(this.series||[],function(a){a.xIncrement=null;if(!a.points||a.isDirtyData)a.processData(),a.generatePoints();c(a.points,function(c,b){var d;c.options&&
(d=g.nameToX(c),void 0!==d&&d!==c.x&&(c.x=d,a.xData[b]=d))})}))},setAxisTranslation:function(a){var g=this,b=g.max-g.min,d=g.axisPointRange||0,e,h=0,k=0,p=g.linkedParent,x=!!g.categories,r=g.transA,m=g.isXAxis;if(m||x||d)e=g.getClosest(),p?(h=p.minPointOffset,k=p.pointRangePadding):c(g.series,function(a){var c=x?1:m?C(a.options.pointRange,e,0):g.axisPointRange||0;a=a.options.pointPlacement;d=Math.max(d,c);g.single||(h=Math.max(h,I(a)?0:c/2),k=Math.max(k,"on"===a?0:c))}),p=g.ordinalSlope&&e?g.ordinalSlope/
e:1,g.minPointOffset=h*=p,g.pointRangePadding=k*=p,g.pointRange=Math.min(d,b),m&&(g.closestPointRange=e);a&&(g.oldTransA=r);g.translationSlope=g.transA=r=g.options.staticScale||g.len/(b+k||1);g.transB=g.horiz?g.left:g.bottom;g.minPixelPadding=r*h},minFromRange:function(){return this.max-this.range},setTickInterval:function(g){var b=this,d=b.chart,e=b.options,k=b.isLog,p=b.log2lin,x=b.isDatetimeAxis,A=b.isXAxis,F=b.isLinked,f=e.maxPadding,z=e.minPadding,D=e.tickInterval,J=e.tickPixelInterval,G=b.categories,
u=b.threshold,t=b.softThreshold,v,q,I,B;x||G||F||this.getTickAmount();I=C(b.userMin,e.min);B=C(b.userMax,e.max);F?(b.linkedParent=d[b.coll][e.linkedTo],d=b.linkedParent.getExtremes(),b.min=C(d.min,d.dataMin),b.max=C(d.max,d.dataMax),e.type!==b.linkedParent.options.type&&a.error(11,1)):(!t&&n(u)&&(b.dataMin>=u?(v=u,z=0):b.dataMax<=u&&(q=u,f=0)),b.min=C(I,v,b.dataMin),b.max=C(B,q,b.dataMax));k&&(b.positiveValuesOnly&&!g&&0>=Math.min(b.min,C(b.dataMin,b.min))&&a.error(10,1),b.min=l(p(b.min),15),b.max=
l(p(b.max),15));b.range&&n(b.max)&&(b.userMin=b.min=I=Math.max(b.dataMin,b.minFromRange()),b.userMax=B=b.max,b.range=null);m(b,"foundExtremes");b.beforePadding&&b.beforePadding();b.adjustForMinRange();!(G||b.axisPointRange||b.usePercentage||F)&&n(b.min)&&n(b.max)&&(p=b.max-b.min)&&(!n(I)&&z&&(b.min-=p*z),!n(B)&&f&&(b.max+=p*f));r(e.softMin)&&!r(b.userMin)&&(b.min=Math.min(b.min,e.softMin));r(e.softMax)&&!r(b.userMax)&&(b.max=Math.max(b.max,e.softMax));r(e.floor)&&(b.min=Math.max(b.min,e.floor));r(e.ceiling)&&
(b.max=Math.min(b.max,e.ceiling));t&&n(b.dataMin)&&(u=u||0,!n(I)&&b.min<u&&b.dataMin>=u?b.min=u:!n(B)&&b.max>u&&b.dataMax<=u&&(b.max=u));b.tickInterval=b.min===b.max||void 0===b.min||void 0===b.max?1:F&&!D&&J===b.linkedParent.options.tickPixelInterval?D=b.linkedParent.tickInterval:C(D,this.tickAmount?(b.max-b.min)/Math.max(this.tickAmount-1,1):void 0,G?1:(b.max-b.min)*J/Math.max(b.len,J));A&&!g&&c(b.series,function(a){a.processData(b.min!==b.oldMin||b.max!==b.oldMax)});b.setAxisTranslation(!0);b.beforeSetTickPositions&&
b.beforeSetTickPositions();b.postProcessTickInterval&&(b.tickInterval=b.postProcessTickInterval(b.tickInterval));b.pointRange&&!D&&(b.tickInterval=Math.max(b.pointRange,b.tickInterval));g=C(e.minTickInterval,b.isDatetimeAxis&&b.closestPointRange);!D&&b.tickInterval<g&&(b.tickInterval=g);x||k||D||(b.tickInterval=M(b.tickInterval,null,h(b.tickInterval),C(e.allowDecimals,!(.5<b.tickInterval&&5>b.tickInterval&&1E3<b.max&&9999>b.max)),!!this.tickAmount));this.tickAmount||(b.tickInterval=b.unsquish());
this.setTickPositions()},setTickPositions:function(){var a=this.options,b,c=a.tickPositions;b=this.getMinorTickInterval();var d=a.tickPositioner,e=a.startOnTick,h=a.endOnTick;this.tickmarkOffset=this.categories&&"between"===a.tickmarkPlacement&&1===this.tickInterval?.5:0;this.minorTickInterval="auto"===b&&this.tickInterval?this.tickInterval/5:b;this.single=this.min===this.max&&n(this.min)&&!this.tickAmount&&(parseInt(this.min,10)===this.min||!1!==a.allowDecimals);this.tickPositions=b=c&&c.slice();
!b&&(b=this.isDatetimeAxis?this.getTimeTicks(this.normalizeTimeTickInterval(this.tickInterval,a.units),this.min,this.max,a.startOfWeek,this.ordinalPositions,this.closestPointRange,!0):this.isLog?this.getLogTickPositions(this.tickInterval,this.min,this.max):this.getLinearTickPositions(this.tickInterval,this.min,this.max),b.length>this.len&&(b=[b[0],b.pop()],b[0]===b[1]&&(b.length=1)),this.tickPositions=b,d&&(d=d.apply(this,[this.min,this.max])))&&(this.tickPositions=b=d);this.paddedTicks=b.slice(0);
this.trimTicks(b,e,h);this.isLinked||(this.single&&2>b.length&&(this.min-=.5,this.max+=.5),c||d||this.adjustTickAmount())},trimTicks:function(a,b,c){var g=a[0],d=a[a.length-1],e=this.minPointOffset||0;if(!this.isLinked){if(b&&-Infinity!==g)this.min=g;else for(;this.min-e>a[0];)a.shift();if(c)this.max=d;else for(;this.max+e<a[a.length-1];)a.pop();0===a.length&&n(g)&&!this.options.tickPositions&&a.push((d+g)/2)}},alignToOthers:function(){var a={},b,d=this.options;!1===this.chart.options.chart.alignTicks||
!1===d.alignTicks||this.isLog||c(this.chart[this.coll],function(g){var c=g.options,c=[g.horiz?c.left:c.top,c.width,c.height,c.pane].join();g.series.length&&(a[c]?b=!0:a[c]=1)});return b},getTickAmount:function(){var a=this.options,b=a.tickAmount,c=a.tickPixelInterval;!n(a.tickInterval)&&this.len<c&&!this.isRadial&&!this.isLog&&a.startOnTick&&a.endOnTick&&(b=2);!b&&this.alignToOthers()&&(b=Math.ceil(this.len/c)+1);4>b&&(this.finalTickAmt=b,b=5);this.tickAmount=b},adjustTickAmount:function(){var a=
this.tickInterval,b=this.tickPositions,c=this.tickAmount,d=this.finalTickAmt,e=b&&b.length,h=C(this.threshold,this.softThreshold?0:null);if(this.hasData()){if(e<c){for(;b.length<c;)b.length%2||this.min===h?b.push(l(b[b.length-1]+a)):b.unshift(l(b[0]-a));this.transA*=(e-1)/(c-1);this.min=b[0];this.max=b[b.length-1]}else e>c&&(this.tickInterval*=2,this.setTickPositions());if(n(d)){for(a=c=b.length;a--;)(3===d&&1===a%2||2>=d&&0<a&&a<c-1)&&b.splice(a,1);this.finalTickAmt=void 0}}},setScale:function(){var a,
b;this.oldMin=this.min;this.oldMax=this.max;this.oldAxisLength=this.len;this.setAxisSize();b=this.len!==this.oldAxisLength;c(this.series,function(b){if(b.isDirtyData||b.isDirty||b.xAxis.isDirty)a=!0});b||a||this.isLinked||this.forceRedraw||this.userMin!==this.oldUserMin||this.userMax!==this.oldUserMax||this.alignToOthers()?(this.resetStacks&&this.resetStacks(),this.forceRedraw=!1,this.getSeriesExtremes(),this.setTickInterval(),this.oldUserMin=this.userMin,this.oldUserMax=this.userMax,this.isDirty||
(this.isDirty=b||this.min!==this.oldMin||this.max!==this.oldMax)):this.cleanStacks&&this.cleanStacks();m(this,"afterSetScale")},setExtremes:function(a,d,e,h,k){var g=this,p=g.chart;e=C(e,!0);c(g.series,function(a){delete a.kdTree});k=b(k,{min:a,max:d});m(g,"setExtremes",k,function(){g.userMin=a;g.userMax=d;g.eventArgs=k;e&&p.redraw(h)})},zoom:function(a,b){var g=this.dataMin,c=this.dataMax,d=this.options,e=Math.min(g,C(d.min,g)),d=Math.max(c,C(d.max,c));if(a!==this.min||b!==this.max)this.allowZoomOutside||
(n(g)&&(a<e&&(a=e),a>d&&(a=d)),n(c)&&(b<e&&(b=e),b>d&&(b=d))),this.displayBtn=void 0!==a||void 0!==b,this.setExtremes(a,b,!1,void 0,{trigger:"zoom"});return!0},setAxisSize:function(){var b=this.chart,c=this.options,d=c.offsets||[0,0,0,0],e=this.horiz,h=this.width=Math.round(a.relativeLength(C(c.width,b.plotWidth-d[3]+d[1]),b.plotWidth)),k=this.height=Math.round(a.relativeLength(C(c.height,b.plotHeight-d[0]+d[2]),b.plotHeight)),p=this.top=Math.round(a.relativeLength(C(c.top,b.plotTop+d[0]),b.plotHeight,
b.plotTop)),c=this.left=Math.round(a.relativeLength(C(c.left,b.plotLeft+d[3]),b.plotWidth,b.plotLeft));this.bottom=b.chartHeight-k-p;this.right=b.chartWidth-h-c;this.len=Math.max(e?h:k,0);this.pos=e?c:p},getExtremes:function(){var a=this.isLog,b=this.lin2log;return{min:a?l(b(this.min)):this.min,max:a?l(b(this.max)):this.max,dataMin:this.dataMin,dataMax:this.dataMax,userMin:this.userMin,userMax:this.userMax}},getThreshold:function(a){var b=this.isLog,c=this.lin2log,g=b?c(this.min):this.min,b=b?c(this.max):
this.max;null===a?a=g:g>a?a=g:b<a&&(a=b);return this.translate(a,0,1,0,1)},autoLabelAlign:function(a){a=(C(a,0)-90*this.side+720)%360;return 15<a&&165>a?"right":195<a&&345>a?"left":"center"},tickSize:function(a){var b=this.options,c=b[a+"Length"],g=C(b[a+"Width"],"tick"===a&&this.isXAxis?1:0);if(g&&c)return"inside"===b[a+"Position"]&&(c=-c),[c,g]},labelMetrics:function(){var a=this.tickPositions&&this.tickPositions[0]||0;return this.chart.renderer.fontMetrics(this.options.labels.style&&this.options.labels.style.fontSize,
this.ticks[a]&&this.ticks[a].label)},unsquish:function(){var a=this.options.labels,b=this.horiz,d=this.tickInterval,e=d,h=this.len/(((this.categories?1:0)+this.max-this.min)/d),k,p=a.rotation,x=this.labelMetrics(),r,m=Number.MAX_VALUE,A,F=function(a){a/=h||1;a=1<a?Math.ceil(a):1;return a*d};b?(A=!a.staggerLines&&!a.step&&(n(p)?[p]:h<C(a.autoRotationLimit,80)&&a.autoRotation))&&c(A,function(a){var b;if(a===p||a&&-90<=a&&90>=a)r=F(Math.abs(x.h/Math.sin(v*a))),b=r+Math.abs(a/360),b<m&&(m=b,k=a,e=r)}):
a.step||(e=F(x.h));this.autoRotation=A;this.labelRotation=C(k,p);return e},getSlotWidth:function(){var a=this.chart,b=this.horiz,c=this.options.labels,d=Math.max(this.tickPositions.length-(this.categories?0:1),1),e=a.margin[3];return b&&2>(c.step||0)&&!c.rotation&&(this.staggerLines||1)*this.len/d||!b&&(c.style&&parseInt(c.style.width,10)||e&&e-a.spacing[3]||.33*a.chartWidth)},renderUnsquish:function(){var a=this.chart,b=a.renderer,d=this.tickPositions,e=this.ticks,h=this.options.labels,k=this.horiz,
p=this.getSlotWidth(),x=Math.max(1,Math.round(p-2*(h.padding||5))),r={},m=this.labelMetrics(),A=h.style&&h.style.textOverflow,F,l,f=0,z;I(h.rotation)||(r.rotation=h.rotation||0);c(d,function(a){(a=e[a])&&a.label&&a.label.textPxLength>f&&(f=a.label.textPxLength)});this.maxLabelLength=f;if(this.autoRotation)f>x&&f>m.h?r.rotation=this.labelRotation:this.labelRotation=0;else if(p&&(F=x,!A))for(l="clip",x=d.length;!k&&x--;)if(z=d[x],z=e[z].label)z.styles&&"ellipsis"===z.styles.textOverflow?z.css({textOverflow:"clip"}):
z.textPxLength>p&&z.css({width:p+"px"}),z.getBBox().height>this.len/d.length-(m.h-m.f)&&(z.specificTextOverflow="ellipsis");r.rotation&&(F=f>.5*a.chartHeight?.33*a.chartHeight:a.chartHeight,A||(l="ellipsis"));if(this.labelAlign=h.align||this.autoLabelAlign(this.labelRotation))r.align=this.labelAlign;c(d,function(a){var b=(a=e[a])&&a.label;b&&(b.attr(r),!F||h.style&&h.style.width||!(F<b.textPxLength||"SPAN"===b.element.tagName)||b.css({width:F,textOverflow:b.specificTextOverflow||l}),delete b.specificTextOverflow,
a.rotation=r.rotation)});this.tickRotCorr=b.rotCorr(m.b,this.labelRotation||0,0!==this.side)},hasData:function(){return this.hasVisibleSeries||n(this.min)&&n(this.max)&&this.tickPositions&&0<this.tickPositions.length},addTitle:function(a){var b=this.chart.renderer,c=this.horiz,g=this.opposite,d=this.options.title,e;this.axisTitle||((e=d.textAlign)||(e=(c?{low:"left",middle:"center",high:"right"}:{low:g?"right":"left",middle:"center",high:g?"left":"right"})[d.align]),this.axisTitle=b.text(d.text,0,
0,d.useHTML).attr({zIndex:7,rotation:d.rotation||0,align:e}).addClass("highcharts-axis-title").css(z(d.style)).add(this.axisGroup),this.axisTitle.isNew=!0);d.style.width||this.isRadial||this.axisTitle.css({width:this.len});this.axisTitle[a?"show":"hide"](!0)},generateTick:function(a){var b=this.ticks;b[a]?b[a].addLabel():b[a]=new J(this,a)},getOffset:function(){var a=this,b=a.chart,d=b.renderer,e=a.options,h=a.tickPositions,k=a.ticks,p=a.horiz,x=a.side,r=b.inverted&&!a.isZAxis?[1,0,3,2][x]:x,m,A,
F=0,l,f=0,z=e.title,J=e.labels,G=0,u=b.axisOffset,b=b.clipOffset,t=[-1,1,1,-1][x],v=e.className,M=a.axisParent,q=this.tickSize("tick");m=a.hasData();a.showAxis=A=m||C(e.showEmpty,!0);a.staggerLines=a.horiz&&J.staggerLines;a.axisGroup||(a.gridGroup=d.g("grid").attr({zIndex:e.gridZIndex||1}).addClass("highcharts-"+this.coll.toLowerCase()+"-grid "+(v||"")).add(M),a.axisGroup=d.g("axis").attr({zIndex:e.zIndex||2}).addClass("highcharts-"+this.coll.toLowerCase()+" "+(v||"")).add(M),a.labelGroup=d.g("axis-labels").attr({zIndex:J.zIndex||
7}).addClass("highcharts-"+a.coll.toLowerCase()+"-labels "+(v||"")).add(M));m||a.isLinked?(c(h,function(b,c){a.generateTick(b,c)}),a.renderUnsquish(),a.reserveSpaceDefault=0===x||2===x||{1:"left",3:"right"}[x]===a.labelAlign,C(J.reserveSpace,"center"===a.labelAlign?!0:null,a.reserveSpaceDefault)&&c(h,function(a){G=Math.max(k[a].getLabelSize(),G)}),a.staggerLines&&(G*=a.staggerLines),a.labelOffset=G*(a.opposite?-1:1)):D(k,function(a,b){a.destroy();delete k[b]});z&&z.text&&!1!==z.enabled&&(a.addTitle(A),
A&&!1!==z.reserveSpace&&(a.titleOffset=F=a.axisTitle.getBBox()[p?"height":"width"],l=z.offset,f=n(l)?0:C(z.margin,p?5:10)));a.renderLine();a.offset=t*C(e.offset,u[x]);a.tickRotCorr=a.tickRotCorr||{x:0,y:0};d=0===x?-a.labelMetrics().h:2===x?a.tickRotCorr.y:0;f=Math.abs(G)+f;G&&(f=f-d+t*(p?C(J.y,a.tickRotCorr.y+8*t):J.x));a.axisTitleMargin=C(l,f);u[x]=Math.max(u[x],a.axisTitleMargin+F+t*a.offset,f,m&&h.length&&q?q[0]+t*a.offset:0);e=e.offset?0:2*Math.floor(a.axisLine.strokeWidth()/2);b[r]=Math.max(b[r],
e)},getLinePath:function(a){var b=this.chart,c=this.opposite,g=this.offset,d=this.horiz,e=this.left+(c?this.width:0)+g,g=b.chartHeight-this.bottom-(c?this.height:0)+g;c&&(a*=-1);return b.renderer.crispLine(["M",d?this.left:e,d?g:this.top,"L",d?b.chartWidth-this.right:e,d?g:b.chartHeight-this.bottom],a)},renderLine:function(){this.axisLine||(this.axisLine=this.chart.renderer.path().addClass("highcharts-axis-line").add(this.axisGroup),this.axisLine.attr({stroke:this.options.lineColor,"stroke-width":this.options.lineWidth,
zIndex:7}))},getTitlePosition:function(){var a=this.horiz,b=this.left,c=this.top,d=this.len,e=this.options.title,h=a?b:c,k=this.opposite,p=this.offset,x=e.x||0,r=e.y||0,m=this.axisTitle,A=this.chart.renderer.fontMetrics(e.style&&e.style.fontSize,m),m=Math.max(m.getBBox(null,0).height-A.h-1,0),d={low:h+(a?0:d),middle:h+d/2,high:h+(a?d:0)}[e.align],b=(a?c+this.height:b)+(a?1:-1)*(k?-1:1)*this.axisTitleMargin+[-m,m,A.f,-m][this.side];return{x:a?d+x:b+(k?this.width:0)+p+x,y:a?b+r-(k?this.height:0)+p:
d+r}},renderMinorTick:function(a){var b=this.chart.hasRendered&&r(this.oldMin),c=this.minorTicks;c[a]||(c[a]=new J(this,a,"minor"));b&&c[a].isNew&&c[a].render(null,!0);c[a].render(null,!1,1)},renderTick:function(a,b){var c=this.isLinked,g=this.ticks,d=this.chart.hasRendered&&r(this.oldMin);if(!c||a>=this.min&&a<=this.max)g[a]||(g[a]=new J(this,a)),d&&g[a].isNew&&g[a].render(b,!0,.1),g[a].render(b)},render:function(){var b=this,d=b.chart,e=b.options,h=b.isLog,k=b.lin2log,p=b.isLinked,x=b.tickPositions,
m=b.axisTitle,F=b.ticks,l=b.minorTicks,f=b.alternateBands,z=e.stackLabels,G=e.alternateGridColor,n=b.tickmarkOffset,u=b.axisLine,C=b.showAxis,t=H(d.renderer.globalAnimation),v,M;b.labelEdge.length=0;b.overlap=!1;c([F,l,f],function(a){D(a,function(a){a.isActive=!1})});if(b.hasData()||p)b.minorTickInterval&&!b.categories&&c(b.getMinorTickPositions(),function(a){b.renderMinorTick(a)}),x.length&&(c(x,function(a,c){b.renderTick(a,c)}),n&&(0===b.min||b.single)&&(F[-1]||(F[-1]=new J(b,-1,null,!0)),F[-1].render(-1))),
G&&c(x,function(c,e){M=void 0!==x[e+1]?x[e+1]+n:b.max-n;0===e%2&&c<b.max&&M<=b.max+(d.polar?-n:n)&&(f[c]||(f[c]=new a.PlotLineOrBand(b)),v=c+n,f[c].options={from:h?k(v):v,to:h?k(M):M,color:G},f[c].render(),f[c].isActive=!0)}),b._addedPlotLB||(c((e.plotLines||[]).concat(e.plotBands||[]),function(a){b.addPlotBandOrLine(a)}),b._addedPlotLB=!0);c([F,l,f],function(a){var b,c=[],e=t.duration;D(a,function(a,b){a.isActive||(a.render(b,!1,0),a.isActive=!1,c.push(b))});A(function(){for(b=c.length;b--;)a[c[b]]&&
!a[c[b]].isActive&&(a[c[b]].destroy(),delete a[c[b]])},a!==f&&d.hasRendered&&e?e:0)});u&&(u[u.isPlaced?"animate":"attr"]({d:this.getLinePath(u.strokeWidth())}),u.isPlaced=!0,u[C?"show":"hide"](!0));m&&C&&(e=b.getTitlePosition(),r(e.y)?(m[m.isNew?"attr":"animate"](e),m.isNew=!1):(m.attr("y",-9999),m.isNew=!0));z&&z.enabled&&b.renderStackTotals();b.isDirty=!1},redraw:function(){this.visible&&(this.render(),c(this.plotLinesAndBands,function(a){a.render()}));c(this.series,function(a){a.isDirty=!0})},
keepProps:"extKey hcEvents names series userMax userMin".split(" "),destroy:function(a){var b=this,d=b.stacks,g=b.plotLinesAndBands,h;a||x(b);D(d,function(a,b){u(a);d[b]=null});c([b.ticks,b.minorTicks,b.alternateBands],function(a){u(a)});if(g)for(a=g.length;a--;)g[a].destroy();c("stackTotalGroup axisLine axisTitle axisGroup gridGroup labelGroup cross".split(" "),function(a){b[a]&&(b[a]=b[a].destroy())});for(h in b.plotLinesAndBandsGroups)b.plotLinesAndBandsGroups[h]=b.plotLinesAndBandsGroups[h].destroy();
D(b,function(a,c){-1===e(c,b.keepProps)&&delete b[c]})},drawCrosshair:function(a,b){var c,d=this.crosshair,e=C(d.snap,!0),g,h=this.cross;a||(a=this.cross&&this.cross.e);this.crosshair&&!1!==(n(b)||!e)?(e?n(b)&&(g=this.isXAxis?b.plotX:this.len-b.plotY):g=a&&(this.horiz?a.chartX-this.pos:this.len-a.chartY+this.pos),n(g)&&(c=this.getPlotLinePath(b&&(this.isXAxis?b.x:C(b.stackY,b.y)),null,null,null,g)||null),n(c)?(b=this.categories&&!this.isRadial,h||(this.cross=h=this.chart.renderer.path().addClass("highcharts-crosshair highcharts-crosshair-"+
(b?"category ":"thin ")+d.className).attr({zIndex:C(d.zIndex,2)}).add(),h.attr({stroke:d.color||(b?f("#ccd6eb").setOpacity(.25).get():"#cccccc"),"stroke-width":C(d.width,1)}).css({"pointer-events":"none"}),d.dashStyle&&h.attr({dashstyle:d.dashStyle})),h.show().attr({d:c}),b&&!d.width&&h.attr({"stroke-width":this.transA}),this.cross.e=a):this.hideCrosshair()):this.hideCrosshair()},hideCrosshair:function(){this.cross&&this.cross.hide()}});return a.Axis=G}(K);(function(a){var B=a.Axis,H=a.getMagnitude,
E=a.normalizeTickInterval,q=a.timeUnits;B.prototype.getTimeTicks=function(){return this.chart.time.getTimeTicks.apply(this.chart.time,arguments)};B.prototype.normalizeTimeTickInterval=function(a,l){var f=l||[["millisecond",[1,2,5,10,20,25,50,100,200,500]],["second",[1,2,5,10,15,30]],["minute",[1,2,5,10,15,30]],["hour",[1,2,3,4,6,8,12]],["day",[1,2]],["week",[1,2]],["month",[1,2,3,4,6]],["year",null]];l=f[f.length-1];var n=q[l[0]],v=l[1],u;for(u=0;u<f.length&&!(l=f[u],n=q[l[0]],v=l[1],f[u+1]&&a<=(n*
v[v.length-1]+q[f[u+1][0]])/2);u++);n===q.year&&a<5*n&&(v=[1,2,5]);a=E(a/n,v,"year"===l[0]?Math.max(H(a/n),1):1);return{unitRange:n,count:a,unitName:l[0]}}})(K);(function(a){var B=a.Axis,H=a.getMagnitude,E=a.map,q=a.normalizeTickInterval,f=a.pick;B.prototype.getLogTickPositions=function(a,t,n,v){var l=this.options,c=this.len,b=this.lin2log,m=this.log2lin,d=[];v||(this._minorAutoInterval=null);if(.5<=a)a=Math.round(a),d=this.getLinearTickPositions(a,t,n);else if(.08<=a)for(var c=Math.floor(t),h,k,
e,p,r,l=.3<a?[1,2,4]:.15<a?[1,2,4,6,8]:[1,2,3,4,5,6,7,8,9];c<n+1&&!r;c++)for(k=l.length,h=0;h<k&&!r;h++)e=m(b(c)*l[h]),e>t&&(!v||p<=n)&&void 0!==p&&d.push(p),p>n&&(r=!0),p=e;else t=b(t),n=b(n),a=v?this.getMinorTickInterval():l.tickInterval,a=f("auto"===a?null:a,this._minorAutoInterval,l.tickPixelInterval/(v?5:1)*(n-t)/((v?c/this.tickPositions.length:c)||1)),a=q(a,null,H(a)),d=E(this.getLinearTickPositions(a,t,n),m),v||(this._minorAutoInterval=a/5);v||(this.tickInterval=a);return d};B.prototype.log2lin=
function(a){return Math.log(a)/Math.LN10};B.prototype.lin2log=function(a){return Math.pow(10,a)}})(K);(function(a,B){var H=a.arrayMax,E=a.arrayMin,q=a.defined,f=a.destroyObjectProperties,l=a.each,t=a.erase,n=a.merge,v=a.pick;a.PlotLineOrBand=function(a,c){this.axis=a;c&&(this.options=c,this.id=c.id)};a.PlotLineOrBand.prototype={render:function(){var f=this,c=f.axis,b=c.horiz,m=f.options,d=m.label,h=f.label,k=m.to,e=m.from,p=m.value,r=q(e)&&q(k),l=q(p),z=f.svgElem,t=!z,D=[],C=m.color,x=v(m.zIndex,
0),F=m.events,D={"class":"highcharts-plot-"+(r?"band ":"line ")+(m.className||"")},A={},J=c.chart.renderer,G=r?"bands":"lines",g=c.log2lin;c.isLog&&(e=g(e),k=g(k),p=g(p));l?(D={stroke:C,"stroke-width":m.width},m.dashStyle&&(D.dashstyle=m.dashStyle)):r&&(C&&(D.fill=C),m.borderWidth&&(D.stroke=m.borderColor,D["stroke-width"]=m.borderWidth));A.zIndex=x;G+="-"+x;(C=c.plotLinesAndBandsGroups[G])||(c.plotLinesAndBandsGroups[G]=C=J.g("plot-"+G).attr(A).add());t&&(f.svgElem=z=J.path().attr(D).add(C));if(l)D=
c.getPlotLinePath(p,z.strokeWidth());else if(r)D=c.getPlotBandPath(e,k,m);else return;t&&D&&D.length?(z.attr({d:D}),F&&a.objectEach(F,function(a,b){z.on(b,function(a){F[b].apply(f,[a])})})):z&&(D?(z.show(),z.animate({d:D})):(z.hide(),h&&(f.label=h=h.destroy())));d&&q(d.text)&&D&&D.length&&0<c.width&&0<c.height&&!D.flat?(d=n({align:b&&r&&"center",x:b?!r&&4:10,verticalAlign:!b&&r&&"middle",y:b?r?16:10:r?6:-4,rotation:b&&!r&&90},d),this.renderLabel(d,D,r,x)):h&&h.hide();return f},renderLabel:function(a,
c,b,m){var d=this.label,h=this.axis.chart.renderer;d||(d={align:a.textAlign||a.align,rotation:a.rotation,"class":"highcharts-plot-"+(b?"band":"line")+"-label "+(a.className||"")},d.zIndex=m,this.label=d=h.text(a.text,0,0,a.useHTML).attr(d).add(),d.css(a.style));m=c.xBounds||[c[1],c[4],b?c[6]:c[1]];c=c.yBounds||[c[2],c[5],b?c[7]:c[2]];b=E(m);h=E(c);d.align(a,!1,{x:b,y:h,width:H(m)-b,height:H(c)-h});d.show()},destroy:function(){t(this.axis.plotLinesAndBands,this);delete this.axis;f(this)}};a.extend(B.prototype,
{getPlotBandPath:function(a,c){var b=this.getPlotLinePath(c,null,null,!0),m=this.getPlotLinePath(a,null,null,!0),d=[],h=this.horiz,k=1,e;a=a<this.min&&c<this.min||a>this.max&&c>this.max;if(m&&b)for(a&&(e=m.toString()===b.toString(),k=0),a=0;a<m.length;a+=6)h&&b[a+1]===m[a+1]?(b[a+1]+=k,b[a+4]+=k):h||b[a+2]!==m[a+2]||(b[a+2]+=k,b[a+5]+=k),d.push("M",m[a+1],m[a+2],"L",m[a+4],m[a+5],b[a+4],b[a+5],b[a+1],b[a+2],"z"),d.flat=e;return d},addPlotBand:function(a){return this.addPlotBandOrLine(a,"plotBands")},
addPlotLine:function(a){return this.addPlotBandOrLine(a,"plotLines")},addPlotBandOrLine:function(f,c){var b=(new a.PlotLineOrBand(this,f)).render(),m=this.userOptions;b&&(c&&(m[c]=m[c]||[],m[c].push(f)),this.plotLinesAndBands.push(b));return b},removePlotBandOrLine:function(a){for(var c=this.plotLinesAndBands,b=this.options,m=this.userOptions,d=c.length;d--;)c[d].id===a&&c[d].destroy();l([b.plotLines||[],m.plotLines||[],b.plotBands||[],m.plotBands||[]],function(b){for(d=b.length;d--;)b[d].id===a&&
t(b,b[d])})},removePlotBand:function(a){this.removePlotBandOrLine(a)},removePlotLine:function(a){this.removePlotBandOrLine(a)}})})(K,V);(function(a){var B=a.each,H=a.extend,E=a.format,q=a.isNumber,f=a.map,l=a.merge,t=a.pick,n=a.splat,v=a.syncTimeout,u=a.timeUnits;a.Tooltip=function(){this.init.apply(this,arguments)};a.Tooltip.prototype={init:function(a,b){this.chart=a;this.options=b;this.crosshairs=[];this.now={x:0,y:0};this.isHidden=!0;this.split=b.split&&!a.inverted;this.shared=b.shared||this.split},
cleanSplit:function(a){B(this.chart.series,function(b){var c=b&&b.tt;c&&(!c.isActive||a?b.tt=c.destroy():c.isActive=!1)})},getLabel:function(){var a=this.chart.renderer,b=this.options;this.label||(this.split?this.label=a.g("tooltip"):(this.label=a.label("",0,0,b.shape||"callout",null,null,b.useHTML,null,"tooltip").attr({padding:b.padding,r:b.borderRadius}),this.label.attr({fill:b.backgroundColor,"stroke-width":b.borderWidth}).css(b.style).shadow(b.shadow)),this.label.attr({zIndex:8}).add());return this.label},
update:function(a){this.destroy();l(!0,this.chart.options.tooltip.userOptions,a);this.init(this.chart,l(!0,this.options,a))},destroy:function(){this.label&&(this.label=this.label.destroy());this.split&&this.tt&&(this.cleanSplit(this.chart,!0),this.tt=this.tt.destroy());clearTimeout(this.hideTimer);clearTimeout(this.tooltipTimeout)},move:function(a,b,m,d){var c=this,k=c.now,e=!1!==c.options.animation&&!c.isHidden&&(1<Math.abs(a-k.x)||1<Math.abs(b-k.y)),p=c.followPointer||1<c.len;H(k,{x:e?(2*k.x+a)/
3:a,y:e?(k.y+b)/2:b,anchorX:p?void 0:e?(2*k.anchorX+m)/3:m,anchorY:p?void 0:e?(k.anchorY+d)/2:d});c.getLabel().attr(k);e&&(clearTimeout(this.tooltipTimeout),this.tooltipTimeout=setTimeout(function(){c&&c.move(a,b,m,d)},32))},hide:function(a){var b=this;clearTimeout(this.hideTimer);a=t(a,this.options.hideDelay,500);this.isHidden||(this.hideTimer=v(function(){b.getLabel()[a?"fadeOut":"hide"]();b.isHidden=!0},a))},getAnchor:function(a,b){var c,d=this.chart,h=d.inverted,k=d.plotTop,e=d.plotLeft,p=0,r=
0,l,z;a=n(a);c=a[0].tooltipPos;this.followPointer&&b&&(void 0===b.chartX&&(b=d.pointer.normalize(b)),c=[b.chartX-d.plotLeft,b.chartY-k]);c||(B(a,function(a){l=a.series.yAxis;z=a.series.xAxis;p+=a.plotX+(!h&&z?z.left-e:0);r+=(a.plotLow?(a.plotLow+a.plotHigh)/2:a.plotY)+(!h&&l?l.top-k:0)}),p/=a.length,r/=a.length,c=[h?d.plotWidth-r:p,this.shared&&!h&&1<a.length&&b?b.chartY-k:h?d.plotHeight-p:r]);return f(c,Math.round)},getPosition:function(a,b,m){var c=this.chart,h=this.distance,k={},e=c.inverted&&
m.h||0,p,r=["y",c.chartHeight,b,m.plotY+c.plotTop,c.plotTop,c.plotTop+c.plotHeight],f=["x",c.chartWidth,a,m.plotX+c.plotLeft,c.plotLeft,c.plotLeft+c.plotWidth],l=!this.followPointer&&t(m.ttBelow,!c.inverted===!!m.negative),n=function(a,b,c,d,g,p){var x=c<d-h,r=d+h+c<b,m=d-h-c;d+=h;if(l&&r)k[a]=d;else if(!l&&x)k[a]=m;else if(x)k[a]=Math.min(p-c,0>m-e?m:m-e);else if(r)k[a]=Math.max(g,d+e+c>b?d:d+e);else return!1},D=function(a,b,c,d){var e;d<h||d>b-h?e=!1:k[a]=d<c/2?1:d>b-c/2?b-c-2:d-c/2;return e},C=
function(a){var b=r;r=f;f=b;p=a},x=function(){!1!==n.apply(0,r)?!1!==D.apply(0,f)||p||(C(!0),x()):p?k.x=k.y=0:(C(!0),x())};(c.inverted||1<this.len)&&C();x();return k},defaultFormatter:function(a){var b=this.points||n(this),c;c=[a.tooltipFooterHeaderFormatter(b[0])];c=c.concat(a.bodyFormatter(b));c.push(a.tooltipFooterHeaderFormatter(b[0],!0));return c},refresh:function(a,b){var c,d=this.options,h,k=a,e,p={},r=[];c=d.formatter||this.defaultFormatter;var p=this.shared,f;d.enabled&&(clearTimeout(this.hideTimer),
this.followPointer=n(k)[0].series.tooltipOptions.followPointer,e=this.getAnchor(k,b),b=e[0],h=e[1],!p||k.series&&k.series.noSharedTooltip?p=k.getLabelConfig():(B(k,function(a){a.setState("hover");r.push(a.getLabelConfig())}),p={x:k[0].category,y:k[0].y},p.points=r,k=k[0]),this.len=r.length,p=c.call(p,this),f=k.series,this.distance=t(f.tooltipOptions.distance,16),!1===p?this.hide():(c=this.getLabel(),this.isHidden&&c.attr({opacity:1}).show(),this.split?this.renderSplit(p,n(a)):(d.style.width||c.css({width:this.chart.spacingBox.width}),
c.attr({text:p&&p.join?p.join(""):p}),c.removeClass(/highcharts-color-[\d]+/g).addClass("highcharts-color-"+t(k.colorIndex,f.colorIndex)),c.attr({stroke:d.borderColor||k.color||f.color||"#666666"}),this.updatePosition({plotX:b,plotY:h,negative:k.negative,ttBelow:k.ttBelow,h:e[2]||0})),this.isHidden=!1))},renderSplit:function(c,b){var m=this,d=[],h=this.chart,k=h.renderer,e=!0,p=this.options,r=0,f=this.getLabel();a.isString(c)&&(c=[!1,c]);B(c.slice(0,b.length+1),function(a,c){if(!1!==a){c=b[c-1]||
{isHeader:!0,plotX:b[0].plotX};var l=c.series||m,z=l.tt,x=c.series||{},F="highcharts-color-"+t(c.colorIndex,x.colorIndex,"none");z||(l.tt=z=k.label(null,null,null,"callout",null,null,p.useHTML).addClass("highcharts-tooltip-box "+F).attr({padding:p.padding,r:p.borderRadius,fill:p.backgroundColor,stroke:p.borderColor||c.color||x.color||"#333333","stroke-width":p.borderWidth}).add(f));z.isActive=!0;z.attr({text:a});z.css(p.style).shadow(p.shadow);a=z.getBBox();x=a.width+z.strokeWidth();c.isHeader?(r=
a.height,x=Math.max(0,Math.min(c.plotX+h.plotLeft-x/2,h.chartWidth-x))):x=c.plotX+h.plotLeft-t(p.distance,16)-x;0>x&&(e=!1);a=(c.series&&c.series.yAxis&&c.series.yAxis.pos)+(c.plotY||0);a-=h.plotTop;d.push({target:c.isHeader?h.plotHeight+r:a,rank:c.isHeader?1:0,size:l.tt.getBBox().height+1,point:c,x:x,tt:z})}});this.cleanSplit();a.distribute(d,h.plotHeight+r);B(d,function(a){var b=a.point,c=b.series;a.tt.attr({visibility:void 0===a.pos?"hidden":"inherit",x:e||b.isHeader?a.x:b.plotX+h.plotLeft+t(p.distance,
16),y:a.pos+h.plotTop,anchorX:b.isHeader?b.plotX+h.plotLeft:b.plotX+c.xAxis.pos,anchorY:b.isHeader?a.pos+h.plotTop-15:b.plotY+c.yAxis.pos})})},updatePosition:function(a){var b=this.chart,c=this.getLabel(),c=(this.options.positioner||this.getPosition).call(this,c.width,c.height,a);this.move(Math.round(c.x),Math.round(c.y||0),a.plotX+b.plotLeft,a.plotY+b.plotTop)},getDateFormat:function(a,b,m,d){var c=this.chart.time,k=c.dateFormat("%m-%d %H:%M:%S.%L",b),e,p,r={millisecond:15,second:12,minute:9,hour:6,
day:3},f="millisecond";for(p in u){if(a===u.week&&+c.dateFormat("%w",b)===m&&"00:00:00.000"===k.substr(6)){p="week";break}if(u[p]>a){p=f;break}if(r[p]&&k.substr(r[p])!=="01-01 00:00:00.000".substr(r[p]))break;"week"!==p&&(f=p)}p&&(e=d[p]);return e},getXDateFormat:function(a,b,m){b=b.dateTimeLabelFormats;var c=m&&m.closestPointRange;return(c?this.getDateFormat(c,a.x,m.options.startOfWeek,b):b.day)||b.year},tooltipFooterHeaderFormatter:function(a,b){b=b?"footer":"header";var c=a.series,d=c.tooltipOptions,
h=d.xDateFormat,k=c.xAxis,e=k&&"datetime"===k.options.type&&q(a.key),p=d[b+"Format"];e&&!h&&(h=this.getXDateFormat(a,d,k));e&&h&&B(a.point&&a.point.tooltipDateKeys||["key"],function(a){p=p.replace("{point."+a+"}","{point."+a+":"+h+"}")});return E(p,{point:a,series:c},this.chart.time)},bodyFormatter:function(a){return f(a,function(a){var b=a.series.tooltipOptions;return(b[(a.point.formatPrefix||"point")+"Formatter"]||a.point.tooltipFormatter).call(a.point,b[(a.point.formatPrefix||"point")+"Format"])})}}})(K);
(function(a){var B=a.addEvent,H=a.attr,E=a.charts,q=a.color,f=a.css,l=a.defined,t=a.each,n=a.extend,v=a.find,u=a.fireEvent,c=a.isNumber,b=a.isObject,m=a.offset,d=a.pick,h=a.splat,k=a.Tooltip;a.Pointer=function(a,b){this.init(a,b)};a.Pointer.prototype={init:function(a,b){this.options=b;this.chart=a;this.runChartClick=b.chart.events&&!!b.chart.events.click;this.pinchDown=[];this.lastValidTouch={};k&&(a.tooltip=new k(a,b.tooltip),this.followTouchMove=d(b.tooltip.followTouchMove,!0));this.setDOMEvents()},
zoomOption:function(a){var b=this.chart,c=b.options.chart,e=c.zoomType||"",b=b.inverted;/touch/.test(a.type)&&(e=d(c.pinchType,e));this.zoomX=a=/x/.test(e);this.zoomY=e=/y/.test(e);this.zoomHor=a&&!b||e&&b;this.zoomVert=e&&!b||a&&b;this.hasZoom=a||e},normalize:function(a,b){var c;c=a.touches?a.touches.length?a.touches.item(0):a.changedTouches[0]:a;b||(this.chartPosition=b=m(this.chart.container));return n(a,{chartX:Math.round(c.pageX-b.left),chartY:Math.round(c.pageY-b.top)})},getCoordinates:function(a){var b=
{xAxis:[],yAxis:[]};t(this.chart.axes,function(c){b[c.isXAxis?"xAxis":"yAxis"].push({axis:c,value:c.toValue(a[c.horiz?"chartX":"chartY"])})});return b},findNearestKDPoint:function(a,c,d){var e;t(a,function(a){var h=!(a.noSharedTooltip&&c)&&0>a.options.findNearestPointBy.indexOf("y");a=a.searchPoint(d,h);if((h=b(a,!0))&&!(h=!b(e,!0)))var h=e.distX-a.distX,k=e.dist-a.dist,p=(a.series.group&&a.series.group.zIndex)-(e.series.group&&e.series.group.zIndex),h=0<(0!==h&&c?h:0!==k?k:0!==p?p:e.series.index>
a.series.index?-1:1);h&&(e=a)});return e},getPointFromEvent:function(a){a=a.target;for(var b;a&&!b;)b=a.point,a=a.parentNode;return b},getChartCoordinatesFromPoint:function(a,b){var c=a.series,e=c.xAxis,c=c.yAxis,h=d(a.clientX,a.plotX);if(e&&c)return b?{chartX:e.len+e.pos-h,chartY:c.len+c.pos-a.plotY}:{chartX:h+e.pos,chartY:a.plotY+c.pos}},getHoverData:function(c,h,k,f,m,l,n){var e,x=[],p=n&&n.isBoosting;f=!(!f||!c);n=h&&!h.stickyTracking?[h]:a.grep(k,function(a){return a.visible&&!(!m&&a.directTouch)&&
d(a.options.enableMouseTracking,!0)&&a.stickyTracking});h=(e=f?c:this.findNearestKDPoint(n,m,l))&&e.series;e&&(m&&!h.noSharedTooltip?(n=a.grep(k,function(a){return a.visible&&!(!m&&a.directTouch)&&d(a.options.enableMouseTracking,!0)&&!a.noSharedTooltip}),t(n,function(a){var c=v(a.points,function(a){return a.x===e.x&&!a.isNull});b(c)&&(p&&(c=a.getPoint(c)),x.push(c))})):x.push(e));return{hoverPoint:e,hoverSeries:h,hoverPoints:x}},runPointActions:function(b,c){var e=this.chart,h=e.tooltip&&e.tooltip.options.enabled?
e.tooltip:void 0,k=h?h.shared:!1,p=c||e.hoverPoint,f=p&&p.series||e.hoverSeries,f=this.getHoverData(p,f,e.series,!!c||f&&f.directTouch&&this.isDirectTouch,k,b,{isBoosting:e.isBoosting}),m,p=f.hoverPoint;m=f.hoverPoints;c=(f=f.hoverSeries)&&f.tooltipOptions.followPointer;k=k&&f&&!f.noSharedTooltip;if(p&&(p!==e.hoverPoint||h&&h.isHidden)){t(e.hoverPoints||[],function(b){-1===a.inArray(b,m)&&b.setState()});t(m||[],function(a){a.setState("hover")});if(e.hoverSeries!==f)f.onMouseOver();e.hoverPoint&&e.hoverPoint.firePointEvent("mouseOut");
if(!p.series)return;p.firePointEvent("mouseOver");e.hoverPoints=m;e.hoverPoint=p;h&&h.refresh(k?m:p,b)}else c&&h&&!h.isHidden&&(p=h.getAnchor([{}],b),h.updatePosition({plotX:p[0],plotY:p[1]}));this.unDocMouseMove||(this.unDocMouseMove=B(e.container.ownerDocument,"mousemove",function(b){var c=E[a.hoverChartIndex];if(c)c.pointer.onDocumentMouseMove(b)}));t(e.axes,function(c){var e=d(c.crosshair.snap,!0),h=e?a.find(m,function(a){return a.series[c.coll]===c}):void 0;h||!e?c.drawCrosshair(b,h):c.hideCrosshair()})},
reset:function(a,b){var c=this.chart,d=c.hoverSeries,e=c.hoverPoint,k=c.hoverPoints,p=c.tooltip,f=p&&p.shared?k:e;a&&f&&t(h(f),function(b){b.series.isCartesian&&void 0===b.plotX&&(a=!1)});if(a)p&&f&&(p.refresh(f),e&&(e.setState(e.state,!0),t(c.axes,function(a){a.crosshair&&a.drawCrosshair(null,e)})));else{if(e)e.onMouseOut();k&&t(k,function(a){a.setState()});if(d)d.onMouseOut();p&&p.hide(b);this.unDocMouseMove&&(this.unDocMouseMove=this.unDocMouseMove());t(c.axes,function(a){a.hideCrosshair()});this.hoverX=
c.hoverPoints=c.hoverPoint=null}},scaleGroups:function(a,b){var c=this.chart,d;t(c.series,function(e){d=a||e.getPlotBox();e.xAxis&&e.xAxis.zoomEnabled&&e.group&&(e.group.attr(d),e.markerGroup&&(e.markerGroup.attr(d),e.markerGroup.clip(b?c.clipRect:null)),e.dataLabelsGroup&&e.dataLabelsGroup.attr(d))});c.clipRect.attr(b||c.clipBox)},dragStart:function(a){var b=this.chart;b.mouseIsDown=a.type;b.cancelClick=!1;b.mouseDownX=this.mouseDownX=a.chartX;b.mouseDownY=this.mouseDownY=a.chartY},drag:function(a){var b=
this.chart,c=b.options.chart,d=a.chartX,e=a.chartY,h=this.zoomHor,k=this.zoomVert,f=b.plotLeft,x=b.plotTop,m=b.plotWidth,A=b.plotHeight,l,n=this.selectionMarker,g=this.mouseDownX,w=this.mouseDownY,t=c.panKey&&a[c.panKey+"Key"];n&&n.touch||(d<f?d=f:d>f+m&&(d=f+m),e<x?e=x:e>x+A&&(e=x+A),this.hasDragged=Math.sqrt(Math.pow(g-d,2)+Math.pow(w-e,2)),10<this.hasDragged&&(l=b.isInsidePlot(g-f,w-x),b.hasCartesianSeries&&(this.zoomX||this.zoomY)&&l&&!t&&!n&&(this.selectionMarker=n=b.renderer.rect(f,x,h?1:m,
k?1:A,0).attr({fill:c.selectionMarkerFill||q("#335cad").setOpacity(.25).get(),"class":"highcharts-selection-marker",zIndex:7}).add()),n&&h&&(d-=g,n.attr({width:Math.abs(d),x:(0<d?0:d)+g})),n&&k&&(d=e-w,n.attr({height:Math.abs(d),y:(0<d?0:d)+w})),l&&!n&&c.panning&&b.pan(a,c.panning)))},drop:function(a){var b=this,d=this.chart,e=this.hasPinched;if(this.selectionMarker){var h={originalEvent:a,xAxis:[],yAxis:[]},k=this.selectionMarker,m=k.attr?k.attr("x"):k.x,C=k.attr?k.attr("y"):k.y,x=k.attr?k.attr("width"):
k.width,F=k.attr?k.attr("height"):k.height,A;if(this.hasDragged||e)t(d.axes,function(c){if(c.zoomEnabled&&l(c.min)&&(e||b[{xAxis:"zoomX",yAxis:"zoomY"}[c.coll]])){var d=c.horiz,g="touchend"===a.type?c.minPixelPadding:0,k=c.toValue((d?m:C)+g),d=c.toValue((d?m+x:C+F)-g);h[c.coll].push({axis:c,min:Math.min(k,d),max:Math.max(k,d)});A=!0}}),A&&u(d,"selection",h,function(a){d.zoom(n(a,e?{animation:!1}:null))});c(d.index)&&(this.selectionMarker=this.selectionMarker.destroy());e&&this.scaleGroups()}d&&c(d.index)&&
(f(d.container,{cursor:d._cursor}),d.cancelClick=10<this.hasDragged,d.mouseIsDown=this.hasDragged=this.hasPinched=!1,this.pinchDown=[])},onContainerMouseDown:function(a){a=this.normalize(a);2!==a.button&&(this.zoomOption(a),a.preventDefault&&a.preventDefault(),this.dragStart(a))},onDocumentMouseUp:function(b){E[a.hoverChartIndex]&&E[a.hoverChartIndex].pointer.drop(b)},onDocumentMouseMove:function(a){var b=this.chart,c=this.chartPosition;a=this.normalize(a,c);!c||this.inClass(a.target,"highcharts-tracker")||
b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop)||this.reset()},onContainerMouseLeave:function(b){var c=E[a.hoverChartIndex];c&&(b.relatedTarget||b.toElement)&&(c.pointer.reset(),c.pointer.chartPosition=null)},onContainerMouseMove:function(b){var c=this.chart;l(a.hoverChartIndex)&&E[a.hoverChartIndex]&&E[a.hoverChartIndex].mouseIsDown||(a.hoverChartIndex=c.index);b=this.normalize(b);b.returnValue=!1;"mousedown"===c.mouseIsDown&&this.drag(b);!this.inClass(b.target,"highcharts-tracker")&&!c.isInsidePlot(b.chartX-
c.plotLeft,b.chartY-c.plotTop)||c.openMenu||this.runPointActions(b)},inClass:function(a,b){for(var c;a;){if(c=H(a,"class")){if(-1!==c.indexOf(b))return!0;if(-1!==c.indexOf("highcharts-container"))return!1}a=a.parentNode}},onTrackerMouseOut:function(a){var b=this.chart.hoverSeries;a=a.relatedTarget||a.toElement;this.isDirectTouch=!1;if(!(!b||!a||b.stickyTracking||this.inClass(a,"highcharts-tooltip")||this.inClass(a,"highcharts-series-"+b.index)&&this.inClass(a,"highcharts-tracker")))b.onMouseOut()},
onContainerClick:function(a){var b=this.chart,c=b.hoverPoint,d=b.plotLeft,e=b.plotTop;a=this.normalize(a);b.cancelClick||(c&&this.inClass(a.target,"highcharts-tracker")?(u(c.series,"click",n(a,{point:c})),b.hoverPoint&&c.firePointEvent("click",a)):(n(a,this.getCoordinates(a)),b.isInsidePlot(a.chartX-d,a.chartY-e)&&u(b,"click",a)))},setDOMEvents:function(){var b=this,c=b.chart.container,d=c.ownerDocument;c.onmousedown=function(a){b.onContainerMouseDown(a)};c.onmousemove=function(a){b.onContainerMouseMove(a)};
c.onclick=function(a){b.onContainerClick(a)};this.unbindContainerMouseLeave=B(c,"mouseleave",b.onContainerMouseLeave);a.unbindDocumentMouseUp||(a.unbindDocumentMouseUp=B(d,"mouseup",b.onDocumentMouseUp));a.hasTouch&&(c.ontouchstart=function(a){b.onContainerTouchStart(a)},c.ontouchmove=function(a){b.onContainerTouchMove(a)},a.unbindDocumentTouchEnd||(a.unbindDocumentTouchEnd=B(d,"touchend",b.onDocumentTouchEnd)))},destroy:function(){var b=this;b.unDocMouseMove&&b.unDocMouseMove();this.unbindContainerMouseLeave();
a.chartCount||(a.unbindDocumentMouseUp&&(a.unbindDocumentMouseUp=a.unbindDocumentMouseUp()),a.unbindDocumentTouchEnd&&(a.unbindDocumentTouchEnd=a.unbindDocumentTouchEnd()));clearInterval(b.tooltipTimeout);a.objectEach(b,function(a,c){b[c]=null})}}})(K);(function(a){var B=a.charts,H=a.each,E=a.extend,q=a.map,f=a.noop,l=a.pick;E(a.Pointer.prototype,{pinchTranslate:function(a,f,l,q,c,b){this.zoomHor&&this.pinchTranslateDirection(!0,a,f,l,q,c,b);this.zoomVert&&this.pinchTranslateDirection(!1,a,f,l,q,
c,b)},pinchTranslateDirection:function(a,f,l,q,c,b,m,d){var h=this.chart,k=a?"x":"y",e=a?"X":"Y",p="chart"+e,r=a?"width":"height",n=h["plot"+(a?"Left":"Top")],z,t,D=d||1,C=h.inverted,x=h.bounds[a?"h":"v"],F=1===f.length,A=f[0][p],J=l[0][p],G=!F&&f[1][p],g=!F&&l[1][p],w;l=function(){!F&&20<Math.abs(A-G)&&(D=d||Math.abs(J-g)/Math.abs(A-G));t=(n-J)/D+A;z=h["plot"+(a?"Width":"Height")]/D};l();f=t;f<x.min?(f=x.min,w=!0):f+z>x.max&&(f=x.max-z,w=!0);w?(J-=.8*(J-m[k][0]),F||(g-=.8*(g-m[k][1])),l()):m[k]=
[J,g];C||(b[k]=t-n,b[r]=z);b=C?1/D:D;c[r]=z;c[k]=f;q[C?a?"scaleY":"scaleX":"scale"+e]=D;q["translate"+e]=b*n+(J-b*A)},pinch:function(a){var n=this,t=n.chart,u=n.pinchDown,c=a.touches,b=c.length,m=n.lastValidTouch,d=n.hasZoom,h=n.selectionMarker,k={},e=1===b&&(n.inClass(a.target,"highcharts-tracker")&&t.runTrackerClick||n.runChartClick),p={};1<b&&(n.initiated=!0);d&&n.initiated&&!e&&a.preventDefault();q(c,function(a){return n.normalize(a)});"touchstart"===a.type?(H(c,function(a,b){u[b]={chartX:a.chartX,
chartY:a.chartY}}),m.x=[u[0].chartX,u[1]&&u[1].chartX],m.y=[u[0].chartY,u[1]&&u[1].chartY],H(t.axes,function(a){if(a.zoomEnabled){var b=t.bounds[a.horiz?"h":"v"],c=a.minPixelPadding,d=a.toPixels(l(a.options.min,a.dataMin)),e=a.toPixels(l(a.options.max,a.dataMax)),h=Math.max(d,e);b.min=Math.min(a.pos,Math.min(d,e)-c);b.max=Math.max(a.pos+a.len,h+c)}}),n.res=!0):n.followTouchMove&&1===b?this.runPointActions(n.normalize(a)):u.length&&(h||(n.selectionMarker=h=E({destroy:f,touch:!0},t.plotBox)),n.pinchTranslate(u,
c,k,h,p,m),n.hasPinched=d,n.scaleGroups(k,p),n.res&&(n.res=!1,this.reset(!1,0)))},touch:function(f,n){var t=this.chart,q,c;if(t.index!==a.hoverChartIndex)this.onContainerMouseLeave({relatedTarget:!0});a.hoverChartIndex=t.index;1===f.touches.length?(f=this.normalize(f),(c=t.isInsidePlot(f.chartX-t.plotLeft,f.chartY-t.plotTop))&&!t.openMenu?(n&&this.runPointActions(f),"touchmove"===f.type&&(n=this.pinchDown,q=n[0]?4<=Math.sqrt(Math.pow(n[0].chartX-f.chartX,2)+Math.pow(n[0].chartY-f.chartY,2)):!1),l(q,
!0)&&this.pinch(f)):n&&this.reset()):2===f.touches.length&&this.pinch(f)},onContainerTouchStart:function(a){this.zoomOption(a);this.touch(a,!0)},onContainerTouchMove:function(a){this.touch(a)},onDocumentTouchEnd:function(f){B[a.hoverChartIndex]&&B[a.hoverChartIndex].pointer.drop(f)}})})(K);(function(a){var B=a.addEvent,H=a.charts,E=a.css,q=a.doc,f=a.extend,l=a.noop,t=a.Pointer,n=a.removeEvent,v=a.win,u=a.wrap;if(!a.hasTouch&&(v.PointerEvent||v.MSPointerEvent)){var c={},b=!!v.PointerEvent,m=function(){var b=
[];b.item=function(a){return this[a]};a.objectEach(c,function(a){b.push({pageX:a.pageX,pageY:a.pageY,target:a.target})});return b},d=function(b,c,d,f){"touch"!==b.pointerType&&b.pointerType!==b.MSPOINTER_TYPE_TOUCH||!H[a.hoverChartIndex]||(f(b),f=H[a.hoverChartIndex].pointer,f[c]({type:d,target:b.currentTarget,preventDefault:l,touches:m()}))};f(t.prototype,{onContainerPointerDown:function(a){d(a,"onContainerTouchStart","touchstart",function(a){c[a.pointerId]={pageX:a.pageX,pageY:a.pageY,target:a.currentTarget}})},
onContainerPointerMove:function(a){d(a,"onContainerTouchMove","touchmove",function(a){c[a.pointerId]={pageX:a.pageX,pageY:a.pageY};c[a.pointerId].target||(c[a.pointerId].target=a.currentTarget)})},onDocumentPointerUp:function(a){d(a,"onDocumentTouchEnd","touchend",function(a){delete c[a.pointerId]})},batchMSEvents:function(a){a(this.chart.container,b?"pointerdown":"MSPointerDown",this.onContainerPointerDown);a(this.chart.container,b?"pointermove":"MSPointerMove",this.onContainerPointerMove);a(q,b?
"pointerup":"MSPointerUp",this.onDocumentPointerUp)}});u(t.prototype,"init",function(a,b,c){a.call(this,b,c);this.hasZoom&&E(b.container,{"-ms-touch-action":"none","touch-action":"none"})});u(t.prototype,"setDOMEvents",function(a){a.apply(this);(this.hasZoom||this.followTouchMove)&&this.batchMSEvents(B)});u(t.prototype,"destroy",function(a){this.batchMSEvents(n);a.call(this)})}})(K);(function(a){var B=a.addEvent,H=a.css,E=a.discardElement,q=a.defined,f=a.each,l=a.isFirefox,t=a.marginNames,n=a.merge,
v=a.pick,u=a.setAnimation,c=a.stableSort,b=a.win,m=a.wrap;a.Legend=function(a,b){this.init(a,b)};a.Legend.prototype={init:function(a,b){this.chart=a;this.setOptions(b);b.enabled&&(this.render(),B(this.chart,"endResize",function(){this.legend.positionCheckboxes()}))},setOptions:function(a){var b=v(a.padding,8);this.options=a;this.itemStyle=a.itemStyle;this.itemHiddenStyle=n(this.itemStyle,a.itemHiddenStyle);this.itemMarginTop=a.itemMarginTop||0;this.padding=b;this.initialItemY=b-5;this.itemHeight=
this.maxItemWidth=0;this.symbolWidth=v(a.symbolWidth,16);this.pages=[]},update:function(a,b){var c=this.chart;this.setOptions(n(!0,this.options,a));this.destroy();c.isDirtyLegend=c.isDirtyBox=!0;v(b,!0)&&c.redraw()},colorizeItem:function(a,b){a.legendGroup[b?"removeClass":"addClass"]("highcharts-legend-item-hidden");var c=this.options,d=a.legendItem,h=a.legendLine,f=a.legendSymbol,m=this.itemHiddenStyle.color,c=b?c.itemStyle.color:m,l=b?a.color||m:m,n=a.options&&a.options.marker,D={fill:l};d&&d.css({fill:c,
color:c});h&&h.attr({stroke:l});f&&(n&&f.isMarker&&(D=a.pointAttribs(),b||(D.stroke=D.fill=m)),f.attr(D))},positionItem:function(a){var b=this.options,c=b.symbolPadding,b=!b.rtl,d=a._legendItemPos,f=d[0],d=d[1],m=a.checkbox;(a=a.legendGroup)&&a.element&&a.translate(b?f:this.legendWidth-f-2*c-4,d);m&&(m.x=f,m.y=d)},destroyItem:function(a){var b=a.checkbox;f(["legendItem","legendLine","legendSymbol","legendGroup"],function(b){a[b]&&(a[b]=a[b].destroy())});b&&E(a.checkbox)},destroy:function(){function a(a){this[a]&&
(this[a]=this[a].destroy())}f(this.getAllItems(),function(b){f(["legendItem","legendGroup"],a,b)});f("clipRect up down pager nav box title group".split(" "),a,this);this.display=null},positionCheckboxes:function(){var a=this.group&&this.group.alignAttr,b,c=this.clipHeight||this.legendHeight,e=this.titleHeight;a&&(b=a.translateY,f(this.allItems,function(d){var h=d.checkbox,k;h&&(k=b+e+h.y+(this.scrollOffset||0)+3,H(h,{left:a.translateX+d.checkboxOffset+h.x-20+"px",top:k+"px",display:k>b-6&&k<b+c-6?
"":"none"}))},this))},renderTitle:function(){var a=this.options,b=this.padding,c=a.title,e=0;c.text&&(this.title||(this.title=this.chart.renderer.label(c.text,b-3,b-4,null,null,null,a.useHTML,null,"legend-title").attr({zIndex:1}).css(c.style).add(this.group)),a=this.title.getBBox(),e=a.height,this.offsetWidth=a.width,this.contentGroup.attr({translateY:e}));this.titleHeight=e},setText:function(b){var c=this.options;b.legendItem.attr({text:c.labelFormat?a.format(c.labelFormat,b,this.chart.time):c.labelFormatter.call(b)})},
renderItem:function(a){var b=this.chart,c=b.renderer,d=this.options,f="horizontal"===d.layout,m=this.symbolWidth,l=d.symbolPadding,z=this.itemStyle,q=this.itemHiddenStyle,D=this.padding,C=f?v(d.itemDistance,20):0,x=!d.rtl,F=d.width,A=d.itemMarginBottom||0,J=this.itemMarginTop,G=a.legendItem,g=!a.series,w=!g&&a.series.drawLegendSymbol?a.series:a,t=w.options,u=this.createCheckboxForItem&&t&&t.showCheckbox,t=m+l+C+(u?20:0),N=d.useHTML,O=a.options.className;G||(a.legendGroup=c.g("legend-item").addClass("highcharts-"+
w.type+"-series highcharts-color-"+a.colorIndex+(O?" "+O:"")+(g?" highcharts-series-"+a.index:"")).attr({zIndex:1}).add(this.scrollGroup),a.legendItem=G=c.text("",x?m+l:-l,this.baseline||0,N).css(n(a.visible?z:q)).attr({align:x?"left":"right",zIndex:2}).add(a.legendGroup),this.baseline||(m=z.fontSize,this.fontMetrics=c.fontMetrics(m,G),this.baseline=this.fontMetrics.f+3+J,G.attr("y",this.baseline)),this.symbolHeight=d.symbolHeight||this.fontMetrics.f,w.drawLegendSymbol(this,a),this.setItemEvents&&
this.setItemEvents(a,G,N),u&&this.createCheckboxForItem(a));this.colorizeItem(a,a.visible);z.width||G.css({width:(d.itemWidth||d.width||b.spacingBox.width)-t});this.setText(a);c=G.getBBox();z=a.checkboxOffset=d.itemWidth||a.legendItemWidth||c.width+t;this.itemHeight=c=Math.round(a.legendItemHeight||c.height||this.symbolHeight);f&&this.itemX-D+z>(F||b.spacingBox.width-2*D-d.x)&&(this.itemX=D,this.itemY+=J+this.lastLineHeight+A,this.lastLineHeight=0);this.maxItemWidth=Math.max(this.maxItemWidth,z);
this.lastItemY=J+this.itemY+A;this.lastLineHeight=Math.max(c,this.lastLineHeight);a._legendItemPos=[this.itemX,this.itemY];f?this.itemX+=z:(this.itemY+=J+c+A,this.lastLineHeight=c);this.offsetWidth=F||Math.max((f?this.itemX-D-(a.checkbox?0:C):z)+D,this.offsetWidth)},getAllItems:function(){var a=[];f(this.chart.series,function(b){var c=b&&b.options;b&&v(c.showInLegend,q(c.linkedTo)?!1:void 0,!0)&&(a=a.concat(b.legendItems||("point"===c.legendType?b.data:b)))});return a},getAlignment:function(){var a=
this.options;return a.floating?"":a.align.charAt(0)+a.verticalAlign.charAt(0)+a.layout.charAt(0)},adjustMargins:function(a,b){var c=this.chart,d=this.options,h=this.getAlignment();h&&f([/(lth|ct|rth)/,/(rtv|rm|rbv)/,/(rbh|cb|lbh)/,/(lbv|lm|ltv)/],function(e,k){e.test(h)&&!q(a[k])&&(c[t[k]]=Math.max(c[t[k]],c.legend[(k+1)%2?"legendHeight":"legendWidth"]+[1,-1,-1,1][k]*d[k%2?"x":"y"]+v(d.margin,12)+b[k]+(0===k?c.titleOffset+c.options.title.margin:0)))})},render:function(){var a=this,b=a.chart,k=b.renderer,
e=a.group,m,l,t,z,q=a.box,D=a.options,C=a.padding;a.itemX=C;a.itemY=a.initialItemY;a.offsetWidth=0;a.lastItemY=0;e||(a.group=e=k.g("legend").attr({zIndex:7}).add(),a.contentGroup=k.g().attr({zIndex:1}).add(e),a.scrollGroup=k.g().add(a.contentGroup));a.renderTitle();m=a.getAllItems();c(m,function(a,b){return(a.options&&a.options.legendIndex||0)-(b.options&&b.options.legendIndex||0)});D.reversed&&m.reverse();a.allItems=m;a.display=l=!!m.length;a.lastLineHeight=0;f(m,function(b){a.renderItem(b)});t=
(D.width||a.offsetWidth)+C;z=a.lastItemY+a.lastLineHeight+a.titleHeight;z=a.handleOverflow(z);z+=C;q||(a.box=q=k.rect().addClass("highcharts-legend-box").attr({r:D.borderRadius}).add(e),q.isNew=!0);q.attr({stroke:D.borderColor,"stroke-width":D.borderWidth||0,fill:D.backgroundColor||"none"}).shadow(D.shadow);0<t&&0<z&&(q[q.isNew?"attr":"animate"](q.crisp.call({},{x:0,y:0,width:t,height:z},q.strokeWidth())),q.isNew=!1);q[l?"show":"hide"]();a.legendWidth=t;a.legendHeight=z;f(m,function(b){a.positionItem(b)});
l&&(k=b.spacingBox,/(lth|ct|rth)/.test(a.getAlignment())&&(k=n(k,{y:k.y+b.titleOffset+b.options.title.margin})),e.align(n(D,{width:t,height:z}),!0,k));b.isResizing||this.positionCheckboxes()},handleOverflow:function(a){var b=this,c=this.chart,d=c.renderer,m=this.options,l=m.y,n=this.padding,c=c.spacingBox.height+("top"===m.verticalAlign?-l:l)-n,l=m.maxHeight,z,q=this.clipRect,D=m.navigation,t=v(D.animation,!0),x=D.arrowSize||12,F=this.nav,A=this.pages,J,G=this.allItems,g=function(a){"number"===typeof a?
q.attr({height:a}):q&&(b.clipRect=q.destroy(),b.contentGroup.clip());b.contentGroup.div&&(b.contentGroup.div.style.clip=a?"rect("+n+"px,9999px,"+(n+a)+"px,0)":"auto")};"horizontal"!==m.layout||"middle"===m.verticalAlign||m.floating||(c/=2);l&&(c=Math.min(c,l));A.length=0;a>c&&!1!==D.enabled?(this.clipHeight=z=Math.max(c-20-this.titleHeight-n,0),this.currentPage=v(this.currentPage,1),this.fullHeight=a,f(G,function(a,b){var c=a._legendItemPos[1],d=Math.round(a.legendItem.getBBox().height),g=A.length;
if(!g||c-A[g-1]>z&&(J||c)!==A[g-1])A.push(J||c),g++;a.pageIx=g-1;J&&(G[b-1].pageIx=g-1);b===G.length-1&&c+d-A[g-1]>z&&(A.push(c),a.pageIx=g);c!==J&&(J=c)}),q||(q=b.clipRect=d.clipRect(0,n,9999,0),b.contentGroup.clip(q)),g(z),F||(this.nav=F=d.g().attr({zIndex:1}).add(this.group),this.up=d.symbol("triangle",0,0,x,x).on("click",function(){b.scroll(-1,t)}).add(F),this.pager=d.text("",15,10).addClass("highcharts-legend-navigation").css(D.style).add(F),this.down=d.symbol("triangle-down",0,0,x,x).on("click",
function(){b.scroll(1,t)}).add(F)),b.scroll(0),a=c):F&&(g(),this.nav=F.destroy(),this.scrollGroup.attr({translateY:1}),this.clipHeight=0);return a},scroll:function(a,b){var c=this.pages,d=c.length;a=this.currentPage+a;var h=this.clipHeight,f=this.options.navigation,m=this.pager,l=this.padding;a>d&&(a=d);0<a&&(void 0!==b&&u(b,this.chart),this.nav.attr({translateX:l,translateY:h+this.padding+7+this.titleHeight,visibility:"visible"}),this.up.attr({"class":1===a?"highcharts-legend-nav-inactive":"highcharts-legend-nav-active"}),
m.attr({text:a+"/"+d}),this.down.attr({x:18+this.pager.getBBox().width,"class":a===d?"highcharts-legend-nav-inactive":"highcharts-legend-nav-active"}),this.up.attr({fill:1===a?f.inactiveColor:f.activeColor}).css({cursor:1===a?"default":"pointer"}),this.down.attr({fill:a===d?f.inactiveColor:f.activeColor}).css({cursor:a===d?"default":"pointer"}),this.scrollOffset=-c[a-1]+this.initialItemY,this.scrollGroup.animate({translateY:this.scrollOffset}),this.currentPage=a,this.positionCheckboxes())}};a.LegendSymbolMixin=
{drawRectangle:function(a,b){var c=a.symbolHeight,d=a.options.squareSymbol;b.legendSymbol=this.chart.renderer.rect(d?(a.symbolWidth-c)/2:0,a.baseline-c+1,d?c:a.symbolWidth,c,v(a.options.symbolRadius,c/2)).addClass("highcharts-point").attr({zIndex:3}).add(b.legendGroup)},drawLineMarker:function(a){var b=this.options,c=b.marker,d=a.symbolWidth,f=a.symbolHeight,m=f/2,l=this.chart.renderer,z=this.legendGroup;a=a.baseline-Math.round(.3*a.fontMetrics.b);var q;q={"stroke-width":b.lineWidth||0};b.dashStyle&&
(q.dashstyle=b.dashStyle);this.legendLine=l.path(["M",0,a,"L",d,a]).addClass("highcharts-graph").attr(q).add(z);c&&!1!==c.enabled&&(b=Math.min(v(c.radius,m),m),0===this.symbol.indexOf("url")&&(c=n(c,{width:f,height:f}),b=0),this.legendSymbol=c=l.symbol(this.symbol,d/2-b,a-b,2*b,2*b,c).addClass("highcharts-point").add(z),c.isMarker=!0)}};(/Trident\/7\.0/.test(b.navigator.userAgent)||l)&&m(a.Legend.prototype,"positionItem",function(a,b){var c=this,d=function(){b._legendItemPos&&a.call(c,b)};d();setTimeout(d)})})(K);
(function(a){var B=a.addEvent,H=a.animate,E=a.animObject,q=a.attr,f=a.doc,l=a.Axis,t=a.createElement,n=a.defaultOptions,v=a.discardElement,u=a.charts,c=a.css,b=a.defined,m=a.each,d=a.extend,h=a.find,k=a.fireEvent,e=a.grep,p=a.isNumber,r=a.isObject,I=a.isString,z=a.Legend,M=a.marginNames,D=a.merge,C=a.objectEach,x=a.Pointer,F=a.pick,A=a.pInt,J=a.removeEvent,G=a.seriesTypes,g=a.splat,w=a.syncTimeout,L=a.win,P=a.Chart=function(){this.getArgs.apply(this,arguments)};a.chart=function(a,b,c){return new P(a,
b,c)};d(P.prototype,{callbacks:[],getArgs:function(){var a=[].slice.call(arguments);if(I(a[0])||a[0].nodeName)this.renderTo=a.shift();this.init(a[0],a[1])},init:function(b,c){var g,d,e=b.series,h=b.plotOptions||{};b.series=null;g=D(n,b);for(d in g.plotOptions)g.plotOptions[d].tooltip=h[d]&&D(h[d].tooltip)||void 0;g.tooltip.userOptions=b.chart&&b.chart.forExport&&b.tooltip.userOptions||b.tooltip;g.series=b.series=e;this.userOptions=b;d=g.chart;e=d.events;this.margin=[];this.spacing=[];this.bounds=
{h:{},v:{}};this.labelCollectors=[];this.callback=c;this.isResizing=0;this.options=g;this.axes=[];this.series=[];this.time=b.time&&a.keys(b.time).length?new a.Time(b.time):a.time;this.hasCartesianSeries=d.showAxes;var k=this;k.index=u.length;u.push(k);a.chartCount++;e&&C(e,function(a,b){B(k,b,a)});k.xAxis=[];k.yAxis=[];k.pointCount=k.colorCounter=k.symbolCounter=0;k.firstRender()},initSeries:function(b){var c=this.options.chart;(c=G[b.type||c.type||c.defaultSeriesType])||a.error(17,!0);c=new c;c.init(this,
b);return c},orderSeries:function(a){var b=this.series;for(a=a||0;a<b.length;a++)b[a]&&(b[a].index=a,b[a].name=b[a].getName())},isInsidePlot:function(a,b,c){var g=c?b:a;a=c?a:b;return 0<=g&&g<=this.plotWidth&&0<=a&&a<=this.plotHeight},redraw:function(b){var c=this.axes,g=this.series,e=this.pointer,h=this.legend,f=this.isDirtyLegend,x,l,p=this.hasCartesianSeries,A=this.isDirtyBox,F,n=this.renderer,w=n.isHidden(),r=[];this.setResponsive&&this.setResponsive(!1);a.setAnimation(b,this);w&&this.temporaryDisplay();
this.layOutTitles();for(b=g.length;b--;)if(F=g[b],F.options.stacking&&(x=!0,F.isDirty)){l=!0;break}if(l)for(b=g.length;b--;)F=g[b],F.options.stacking&&(F.isDirty=!0);m(g,function(a){a.isDirty&&"point"===a.options.legendType&&(a.updateTotals&&a.updateTotals(),f=!0);a.isDirtyData&&k(a,"updatedData")});f&&h.options.enabled&&(h.render(),this.isDirtyLegend=!1);x&&this.getStacks();p&&m(c,function(a){a.updateNames();a.setScale()});this.getMargins();p&&(m(c,function(a){a.isDirty&&(A=!0)}),m(c,function(a){var b=
a.min+","+a.max;a.extKey!==b&&(a.extKey=b,r.push(function(){k(a,"afterSetExtremes",d(a.eventArgs,a.getExtremes()));delete a.eventArgs}));(A||x)&&a.redraw()}));A&&this.drawChartBox();k(this,"predraw");m(g,function(a){(A||a.isDirty)&&a.visible&&a.redraw();a.isDirtyData=!1});e&&e.reset(!0);n.draw();k(this,"redraw");k(this,"render");w&&this.temporaryDisplay(!0);m(r,function(a){a.call()})},get:function(a){function b(b){return b.id===a||b.options&&b.options.id===a}var c,g=this.series,d;c=h(this.axes,b)||
h(this.series,b);for(d=0;!c&&d<g.length;d++)c=h(g[d].points||[],b);return c},getAxes:function(){var a=this,b=this.options,c=b.xAxis=g(b.xAxis||{}),b=b.yAxis=g(b.yAxis||{});k(this,"beforeGetAxes");m(c,function(a,b){a.index=b;a.isX=!0});m(b,function(a,b){a.index=b});c=c.concat(b);m(c,function(b){new l(a,b)})},getSelectedPoints:function(){var a=[];m(this.series,function(b){a=a.concat(e(b.data||[],function(a){return a.selected}))});return a},getSelectedSeries:function(){return e(this.series,function(a){return a.selected})},
setTitle:function(a,b,c){var g=this,d=g.options,e;e=d.title=D({style:{color:"#333333",fontSize:d.isStock?"16px":"18px"}},d.title,a);d=d.subtitle=D({style:{color:"#666666"}},d.subtitle,b);m([["title",a,e],["subtitle",b,d]],function(a,b){var c=a[0],d=g[c],e=a[1];a=a[2];d&&e&&(g[c]=d=d.destroy());a&&!d&&(g[c]=g.renderer.text(a.text,0,0,a.useHTML).attr({align:a.align,"class":"highcharts-"+c,zIndex:a.zIndex||4}).add(),g[c].update=function(a){g.setTitle(!b&&a,b&&a)},g[c].css(a.style))});g.layOutTitles(c)},
layOutTitles:function(a){var b=0,c,g=this.renderer,e=this.spacingBox;m(["title","subtitle"],function(a){var c=this[a],h=this.options[a];a="title"===a?-3:h.verticalAlign?0:b+2;var k;c&&(k=h.style.fontSize,k=g.fontMetrics(k,c).b,c.css({width:(h.width||e.width+h.widthAdjust)+"px"}).align(d({y:a+k},h),!1,"spacingBox"),h.floating||h.verticalAlign||(b=Math.ceil(b+c.getBBox(h.useHTML).height)))},this);c=this.titleOffset!==b;this.titleOffset=b;!this.isDirtyBox&&c&&(this.isDirtyBox=c,this.hasRendered&&F(a,
!0)&&this.isDirtyBox&&this.redraw())},getChartSize:function(){var c=this.options.chart,g=c.width,c=c.height,d=this.renderTo;b(g)||(this.containerWidth=a.getStyle(d,"width"));b(c)||(this.containerHeight=a.getStyle(d,"height"));this.chartWidth=Math.max(0,g||this.containerWidth||600);this.chartHeight=Math.max(0,a.relativeLength(c,this.chartWidth)||(1<this.containerHeight?this.containerHeight:400))},temporaryDisplay:function(b){var c=this.renderTo;if(b)for(;c&&c.style;)c.hcOrigStyle&&(a.css(c,c.hcOrigStyle),
delete c.hcOrigStyle),c.hcOrigDetached&&(f.body.removeChild(c),c.hcOrigDetached=!1),c=c.parentNode;else for(;c&&c.style;){f.body.contains(c)||c.parentNode||(c.hcOrigDetached=!0,f.body.appendChild(c));if("none"===a.getStyle(c,"display",!1)||c.hcOricDetached)c.hcOrigStyle={display:c.style.display,height:c.style.height,overflow:c.style.overflow},b={display:"block",overflow:"hidden"},c!==this.renderTo&&(b.height=0),a.css(c,b),c.offsetWidth||c.style.setProperty("display","block","important");c=c.parentNode;
if(c===f.body)break}},setClassName:function(a){this.container.className="highcharts-container "+(a||"")},getContainer:function(){var b,c=this.options,g=c.chart,e,h;b=this.renderTo;var k=a.uniqueKey(),x;b||(this.renderTo=b=g.renderTo);I(b)&&(this.renderTo=b=f.getElementById(b));b||a.error(13,!0);e=A(q(b,"data-highcharts-chart"));p(e)&&u[e]&&u[e].hasRendered&&u[e].destroy();q(b,"data-highcharts-chart",this.index);b.innerHTML="";g.skipClone||b.offsetWidth||this.temporaryDisplay();this.getChartSize();
e=this.chartWidth;h=this.chartHeight;x=d({position:"relative",overflow:"hidden",width:e+"px",height:h+"px",textAlign:"left",lineHeight:"normal",zIndex:0,"-webkit-tap-highlight-color":"rgba(0,0,0,0)"},g.style);this.container=b=t("div",{id:k},x,b);this._cursor=b.style.cursor;this.renderer=new (a[g.renderer]||a.Renderer)(b,e,h,null,g.forExport,c.exporting&&c.exporting.allowHTML);this.setClassName(g.className);this.renderer.setStyle(g.style);this.renderer.chartIndex=this.index},getMargins:function(a){var c=
this.spacing,g=this.margin,d=this.titleOffset;this.resetMargins();d&&!b(g[0])&&(this.plotTop=Math.max(this.plotTop,d+this.options.title.margin+c[0]));this.legend&&this.legend.display&&this.legend.adjustMargins(g,c);this.extraMargin&&(this[this.extraMargin.type]=(this[this.extraMargin.type]||0)+this.extraMargin.value);this.adjustPlotArea&&this.adjustPlotArea();a||this.getAxisMargins()},getAxisMargins:function(){var a=this,c=a.axisOffset=[0,0,0,0],g=a.margin;a.hasCartesianSeries&&m(a.axes,function(a){a.visible&&
a.getOffset()});m(M,function(d,e){b(g[e])||(a[d]+=c[e])});a.setChartSize()},reflow:function(c){var g=this,d=g.options.chart,e=g.renderTo,h=b(d.width)&&b(d.height),k=d.width||a.getStyle(e,"width"),d=d.height||a.getStyle(e,"height"),e=c?c.target:L;if(!h&&!g.isPrinting&&k&&d&&(e===L||e===f)){if(k!==g.containerWidth||d!==g.containerHeight)clearTimeout(g.reflowTimeout),g.reflowTimeout=w(function(){g.container&&g.setSize(void 0,void 0,!1)},c?100:0);g.containerWidth=k;g.containerHeight=d}},initReflow:function(){var a=
this,b;b=B(L,"resize",function(b){a.reflow(b)});B(a,"destroy",b)},setSize:function(b,g,d){var e=this,h=e.renderer;e.isResizing+=1;a.setAnimation(d,e);e.oldChartHeight=e.chartHeight;e.oldChartWidth=e.chartWidth;void 0!==b&&(e.options.chart.width=b);void 0!==g&&(e.options.chart.height=g);e.getChartSize();b=h.globalAnimation;(b?H:c)(e.container,{width:e.chartWidth+"px",height:e.chartHeight+"px"},b);e.setChartSize(!0);h.setSize(e.chartWidth,e.chartHeight,d);m(e.axes,function(a){a.isDirty=!0;a.setScale()});
e.isDirtyLegend=!0;e.isDirtyBox=!0;e.layOutTitles();e.getMargins();e.redraw(d);e.oldChartHeight=null;k(e,"resize");w(function(){e&&k(e,"endResize",null,function(){--e.isResizing})},E(b).duration)},setChartSize:function(a){var b=this.inverted,c=this.renderer,g=this.chartWidth,d=this.chartHeight,e=this.options.chart,h=this.spacing,k=this.clipOffset,f,x,l,p;this.plotLeft=f=Math.round(this.plotLeft);this.plotTop=x=Math.round(this.plotTop);this.plotWidth=l=Math.max(0,Math.round(g-f-this.marginRight));
this.plotHeight=p=Math.max(0,Math.round(d-x-this.marginBottom));this.plotSizeX=b?p:l;this.plotSizeY=b?l:p;this.plotBorderWidth=e.plotBorderWidth||0;this.spacingBox=c.spacingBox={x:h[3],y:h[0],width:g-h[3]-h[1],height:d-h[0]-h[2]};this.plotBox=c.plotBox={x:f,y:x,width:l,height:p};g=2*Math.floor(this.plotBorderWidth/2);b=Math.ceil(Math.max(g,k[3])/2);c=Math.ceil(Math.max(g,k[0])/2);this.clipBox={x:b,y:c,width:Math.floor(this.plotSizeX-Math.max(g,k[1])/2-b),height:Math.max(0,Math.floor(this.plotSizeY-
Math.max(g,k[2])/2-c))};a||m(this.axes,function(a){a.setAxisSize();a.setAxisTranslation()})},resetMargins:function(){var a=this,b=a.options.chart;m(["margin","spacing"],function(c){var g=b[c],d=r(g)?g:[g,g,g,g];m(["Top","Right","Bottom","Left"],function(g,e){a[c][e]=F(b[c+g],d[e])})});m(M,function(b,c){a[b]=F(a.margin[c],a.spacing[c])});a.axisOffset=[0,0,0,0];a.clipOffset=[0,0,0,0]},drawChartBox:function(){var a=this.options.chart,b=this.renderer,c=this.chartWidth,g=this.chartHeight,d=this.chartBackground,
e=this.plotBackground,h=this.plotBorder,f,x=this.plotBGImage,m=a.backgroundColor,l=a.plotBackgroundColor,p=a.plotBackgroundImage,A,F=this.plotLeft,n=this.plotTop,w=this.plotWidth,r=this.plotHeight,z=this.plotBox,G=this.clipRect,J=this.clipBox,q="animate";d||(this.chartBackground=d=b.rect().addClass("highcharts-background").add(),q="attr");f=a.borderWidth||0;A=f+(a.shadow?8:0);m={fill:m||"none"};if(f||d["stroke-width"])m.stroke=a.borderColor,m["stroke-width"]=f;d.attr(m).shadow(a.shadow);d[q]({x:A/
2,y:A/2,width:c-A-f%2,height:g-A-f%2,r:a.borderRadius});q="animate";e||(q="attr",this.plotBackground=e=b.rect().addClass("highcharts-plot-background").add());e[q](z);e.attr({fill:l||"none"}).shadow(a.plotShadow);p&&(x?x.animate(z):this.plotBGImage=b.image(p,F,n,w,r).add());G?G.animate({width:J.width,height:J.height}):this.clipRect=b.clipRect(J);q="animate";h||(q="attr",this.plotBorder=h=b.rect().addClass("highcharts-plot-border").attr({zIndex:1}).add());h.attr({stroke:a.plotBorderColor,"stroke-width":a.plotBorderWidth||
0,fill:"none"});h[q](h.crisp({x:F,y:n,width:w,height:r},-h.strokeWidth()));this.isDirtyBox=!1;k(this,"afterDrawChartBox")},propFromSeries:function(){var a=this,b=a.options.chart,c,g=a.options.series,d,e;m(["inverted","angular","polar"],function(h){c=G[b.type||b.defaultSeriesType];e=b[h]||c&&c.prototype[h];for(d=g&&g.length;!e&&d--;)(c=G[g[d].type])&&c.prototype[h]&&(e=!0);a[h]=e})},linkSeries:function(){var a=this,b=a.series;m(b,function(a){a.linkedSeries.length=0});m(b,function(b){var c=b.options.linkedTo;
I(c)&&(c=":previous"===c?a.series[b.index-1]:a.get(c))&&c.linkedParent!==b&&(c.linkedSeries.push(b),b.linkedParent=c,b.visible=F(b.options.visible,c.options.visible,b.visible))})},renderSeries:function(){m(this.series,function(a){a.translate();a.render()})},renderLabels:function(){var a=this,b=a.options.labels;b.items&&m(b.items,function(c){var g=d(b.style,c.style),e=A(g.left)+a.plotLeft,h=A(g.top)+a.plotTop+12;delete g.left;delete g.top;a.renderer.text(c.html,e,h).attr({zIndex:2}).css(g).add()})},
render:function(){var a=this.axes,b=this.renderer,c=this.options,g,d,e;this.setTitle();this.legend=new z(this,c.legend);this.getStacks&&this.getStacks();this.getMargins(!0);this.setChartSize();c=this.plotWidth;g=this.plotHeight=Math.max(this.plotHeight-21,0);m(a,function(a){a.setScale()});this.getAxisMargins();d=1.1<c/this.plotWidth;e=1.05<g/this.plotHeight;if(d||e)m(a,function(a){(a.horiz&&d||!a.horiz&&e)&&a.setTickInterval(!0)}),this.getMargins();this.drawChartBox();this.hasCartesianSeries&&m(a,
function(a){a.visible&&a.render()});this.seriesGroup||(this.seriesGroup=b.g("series-group").attr({zIndex:3}).add());this.renderSeries();this.renderLabels();this.addCredits();this.setResponsive&&this.setResponsive();this.hasRendered=!0},addCredits:function(a){var b=this;a=D(!0,this.options.credits,a);a.enabled&&!this.credits&&(this.credits=this.renderer.text(a.text+(this.mapCredits||""),0,0).addClass("highcharts-credits").on("click",function(){a.href&&(L.location.href=a.href)}).attr({align:a.position.align,
zIndex:8}).css(a.style).add().align(a.position),this.credits.update=function(a){b.credits=b.credits.destroy();b.addCredits(a)})},destroy:function(){var b=this,c=b.axes,g=b.series,d=b.container,e,h=d&&d.parentNode;k(b,"destroy");b.renderer.forExport?a.erase(u,b):u[b.index]=void 0;a.chartCount--;b.renderTo.removeAttribute("data-highcharts-chart");J(b);for(e=c.length;e--;)c[e]=c[e].destroy();this.scroller&&this.scroller.destroy&&this.scroller.destroy();for(e=g.length;e--;)g[e]=g[e].destroy();m("title subtitle chartBackground plotBackground plotBGImage plotBorder seriesGroup clipRect credits pointer rangeSelector legend resetZoomButton tooltip renderer".split(" "),
function(a){var c=b[a];c&&c.destroy&&(b[a]=c.destroy())});d&&(d.innerHTML="",J(d),h&&v(d));C(b,function(a,c){delete b[c]})},firstRender:function(){var a=this,b=a.options;if(!a.isReadyToRender||a.isReadyToRender()){a.getContainer();k(a,"init");a.resetMargins();a.setChartSize();a.propFromSeries();a.getAxes();m(b.series||[],function(b){a.initSeries(b)});a.linkSeries();k(a,"beforeRender");x&&(a.pointer=new x(a,b));a.render();if(!a.renderer.imgCount&&a.onload)a.onload();a.temporaryDisplay(!0)}},onload:function(){m([this.callback].concat(this.callbacks),
function(a){a&&void 0!==this.index&&a.apply(this,[this])},this);k(this,"load");k(this,"render");b(this.index)&&!1!==this.options.chart.reflow&&this.initReflow();this.onload=null}})})(K);(function(a){var B,H=a.each,E=a.extend,q=a.erase,f=a.fireEvent,l=a.format,t=a.isArray,n=a.isNumber,v=a.pick,u=a.removeEvent;a.Point=B=function(){};a.Point.prototype={init:function(a,b,m){this.series=a;this.color=a.color;this.applyOptions(b,m);a.options.colorByPoint?(b=a.options.colors||a.chart.options.colors,this.color=
this.color||b[a.colorCounter],b=b.length,m=a.colorCounter,a.colorCounter++,a.colorCounter===b&&(a.colorCounter=0)):m=a.colorIndex;this.colorIndex=v(this.colorIndex,m);a.chart.pointCount++;f(this,"afterInit");return this},applyOptions:function(a,b){var c=this.series,d=c.options.pointValKey||c.pointValKey;a=B.prototype.optionsToObject.call(this,a);E(this,a);this.options=this.options?E(this.options,a):a;a.group&&delete this.group;d&&(this.y=this[d]);this.isNull=v(this.isValid&&!this.isValid(),null===
this.x||!n(this.y,!0));this.selected&&(this.state="select");"name"in this&&void 0===b&&c.xAxis&&c.xAxis.hasNames&&(this.x=c.xAxis.nameToX(this));void 0===this.x&&c&&(this.x=void 0===b?c.autoIncrement(this):b);return this},optionsToObject:function(a){var b={},c=this.series,d=c.options.keys,h=d||c.pointArrayMap||["y"],k=h.length,e=0,f=0;if(n(a)||null===a)b[h[0]]=a;else if(t(a))for(!d&&a.length>k&&(c=typeof a[0],"string"===c?b.name=a[0]:"number"===c&&(b.x=a[0]),e++);f<k;)d&&void 0===a[e]||(b[h[f]]=a[e]),
e++,f++;else"object"===typeof a&&(b=a,a.dataLabels&&(c._hasPointLabels=!0),a.marker&&(c._hasPointMarkers=!0));return b},getClassName:function(){return"highcharts-point"+(this.selected?" highcharts-point-select":"")+(this.negative?" highcharts-negative":"")+(this.isNull?" highcharts-null-point":"")+(void 0!==this.colorIndex?" highcharts-color-"+this.colorIndex:"")+(this.options.className?" "+this.options.className:"")+(this.zone&&this.zone.className?" "+this.zone.className.replace("highcharts-negative",
""):"")},getZone:function(){var a=this.series,b=a.zones,a=a.zoneAxis||"y",f=0,d;for(d=b[f];this[a]>=d.value;)d=b[++f];d&&d.color&&!this.options.color&&(this.color=d.color);return d},destroy:function(){var a=this.series.chart,b=a.hoverPoints,f;a.pointCount--;b&&(this.setState(),q(b,this),b.length||(a.hoverPoints=null));if(this===a.hoverPoint)this.onMouseOut();if(this.graphic||this.dataLabel)u(this),this.destroyElements();this.legendItem&&a.legend.destroyItem(this);for(f in this)this[f]=null},destroyElements:function(){for(var a=
["graphic","dataLabel","dataLabelUpper","connector","shadowGroup"],b,f=6;f--;)b=a[f],this[b]&&(this[b]=this[b].destroy())},getLabelConfig:function(){return{x:this.category,y:this.y,color:this.color,colorIndex:this.colorIndex,key:this.name||this.category,series:this.series,point:this,percentage:this.percentage,total:this.total||this.stackTotal}},tooltipFormatter:function(a){var b=this.series,c=b.tooltipOptions,d=v(c.valueDecimals,""),h=c.valuePrefix||"",k=c.valueSuffix||"";H(b.pointArrayMap||["y"],
function(b){b="{point."+b;if(h||k)a=a.replace(b+"}",h+b+"}"+k);a=a.replace(b+"}",b+":,."+d+"f}")});return l(a,{point:this,series:this.series},b.chart.time)},firePointEvent:function(a,b,m){var c=this,h=this.series.options;(h.point.events[a]||c.options&&c.options.events&&c.options.events[a])&&this.importEvents();"click"===a&&h.allowPointSelect&&(m=function(a){c.select&&c.select(null,a.ctrlKey||a.metaKey||a.shiftKey)});f(this,a,b,m)},visible:!0}})(K);(function(a){var B=a.addEvent,H=a.animObject,E=a.arrayMax,
q=a.arrayMin,f=a.correctFloat,l=a.defaultOptions,t=a.defaultPlotOptions,n=a.defined,v=a.each,u=a.erase,c=a.extend,b=a.fireEvent,m=a.grep,d=a.isArray,h=a.isNumber,k=a.isString,e=a.merge,p=a.objectEach,r=a.pick,I=a.removeEvent,z=a.splat,M=a.SVGElement,D=a.syncTimeout,C=a.win;a.Series=a.seriesType("line",null,{lineWidth:2,allowPointSelect:!1,showCheckbox:!1,animation:{duration:1E3},events:{},marker:{lineWidth:0,lineColor:"#ffffff",enabledThreshold:2,radius:4,states:{normal:{animation:!0},hover:{animation:{duration:50},
enabled:!0,radiusPlus:2,lineWidthPlus:1},select:{fillColor:"#cccccc",lineColor:"#000000",lineWidth:2}}},point:{events:{}},dataLabels:{align:"center",formatter:function(){return null===this.y?"":a.numberFormat(this.y,-1)},style:{fontSize:"11px",fontWeight:"bold",color:"contrast",textOutline:"1px contrast"},verticalAlign:"bottom",x:0,y:0,padding:5},cropThreshold:300,pointRange:0,softThreshold:!0,states:{normal:{animation:!0},hover:{animation:{duration:50},lineWidthPlus:1,marker:{},halo:{size:10,opacity:.25}},
select:{marker:{}}},stickyTracking:!0,turboThreshold:1E3,findNearestPointBy:"x"},{isCartesian:!0,pointClass:a.Point,sorted:!0,requireSorting:!0,directTouch:!1,axisTypes:["xAxis","yAxis"],colorCounter:0,parallelArrays:["x","y"],coll:"series",init:function(a,b){var d=this,e,h=a.series,g;d.chart=a;d.options=b=d.setOptions(b);d.linkedSeries=[];d.bindAxes();c(d,{name:b.name,state:"",visible:!1!==b.visible,selected:!0===b.selected});e=b.events;p(e,function(a,b){B(d,b,a)});if(e&&e.click||b.point&&b.point.events&&
b.point.events.click||b.allowPointSelect)a.runTrackerClick=!0;d.getColor();d.getSymbol();v(d.parallelArrays,function(a){d[a+"Data"]=[]});d.setData(b.data,!1);d.isCartesian&&(a.hasCartesianSeries=!0);h.length&&(g=h[h.length-1]);d._i=r(g&&g._i,-1)+1;a.orderSeries(this.insert(h))},insert:function(a){var b=this.options.index,c;if(h(b)){for(c=a.length;c--;)if(b>=r(a[c].options.index,a[c]._i)){a.splice(c+1,0,this);break}-1===c&&a.unshift(this);c+=1}else a.push(this);return r(c,a.length-1)},bindAxes:function(){var b=
this,c=b.options,d=b.chart,e;v(b.axisTypes||[],function(h){v(d[h],function(a){e=a.options;if(c[h]===e.index||void 0!==c[h]&&c[h]===e.id||void 0===c[h]&&0===e.index)b.insert(a.series),b[h]=a,a.isDirty=!0});b[h]||b.optionalAxis===h||a.error(18,!0)})},updateParallelArrays:function(a,b){var c=a.series,d=arguments,e=h(b)?function(g){var d="y"===g&&c.toYData?c.toYData(a):a[g];c[g+"Data"][b]=d}:function(a){Array.prototype[b].apply(c[a+"Data"],Array.prototype.slice.call(d,2))};v(c.parallelArrays,e)},autoIncrement:function(){var a=
this.options,b=this.xIncrement,c,d=a.pointIntervalUnit,e=this.chart.time,b=r(b,a.pointStart,0);this.pointInterval=c=r(this.pointInterval,a.pointInterval,1);d&&(a=new e.Date(b),"day"===d?e.set("Date",a,e.get("Date",a)+c):"month"===d?e.set("Month",a,e.get("Month",a)+c):"year"===d&&e.set("FullYear",a,e.get("FullYear",a)+c),c=a.getTime()-b);this.xIncrement=b+c;return b},setOptions:function(a){var b=this.chart,c=b.options,d=c.plotOptions,h=(b.userOptions||{}).plotOptions||{},g=d[this.type];this.userOptions=
a;b=e(g,d.series,a);this.tooltipOptions=e(l.tooltip,l.plotOptions.series&&l.plotOptions.series.tooltip,l.plotOptions[this.type].tooltip,c.tooltip.userOptions,d.series&&d.series.tooltip,d[this.type].tooltip,a.tooltip);this.stickyTracking=r(a.stickyTracking,h[this.type]&&h[this.type].stickyTracking,h.series&&h.series.stickyTracking,this.tooltipOptions.shared&&!this.noSharedTooltip?!0:b.stickyTracking);null===g.marker&&delete b.marker;this.zoneAxis=b.zoneAxis;a=this.zones=(b.zones||[]).slice();!b.negativeColor&&
!b.negativeFillColor||b.zones||a.push({value:b[this.zoneAxis+"Threshold"]||b.threshold||0,className:"highcharts-negative",color:b.negativeColor,fillColor:b.negativeFillColor});a.length&&n(a[a.length-1].value)&&a.push({color:this.color,fillColor:this.fillColor});return b},getName:function(){return this.name||"Series "+(this.index+1)},getCyclic:function(a,b,c){var d,e=this.chart,g=this.userOptions,h=a+"Index",k=a+"Counter",f=c?c.length:r(e.options.chart[a+"Count"],e[a+"Count"]);b||(d=r(g[h],g["_"+h]),
n(d)||(e.series.length||(e[k]=0),g["_"+h]=d=e[k]%f,e[k]+=1),c&&(b=c[d]));void 0!==d&&(this[h]=d);this[a]=b},getColor:function(){this.options.colorByPoint?this.options.color=null:this.getCyclic("color",this.options.color||t[this.type].color,this.chart.options.colors)},getSymbol:function(){this.getCyclic("symbol",this.options.marker.symbol,this.chart.options.symbols)},drawLegendSymbol:a.LegendSymbolMixin.drawLineMarker,setData:function(b,c,e,f){var m=this,g=m.points,l=g&&g.length||0,x,p=m.options,F=
m.chart,n=null,A=m.xAxis,z=p.turboThreshold,q=this.xData,D=this.yData,t=(x=m.pointArrayMap)&&x.length;b=b||[];x=b.length;c=r(c,!0);if(!1!==f&&x&&l===x&&!m.cropped&&!m.hasGroupedData&&m.visible)v(b,function(a,b){g[b].update&&a!==p.data[b]&&g[b].update(a,!1,null,!1)});else{m.xIncrement=null;m.colorCounter=0;v(this.parallelArrays,function(a){m[a+"Data"].length=0});if(z&&x>z){for(e=0;null===n&&e<x;)n=b[e],e++;if(h(n))for(e=0;e<x;e++)q[e]=this.autoIncrement(),D[e]=b[e];else if(d(n))if(t)for(e=0;e<x;e++)n=
b[e],q[e]=n[0],D[e]=n.slice(1,t+1);else for(e=0;e<x;e++)n=b[e],q[e]=n[0],D[e]=n[1];else a.error(12)}else for(e=0;e<x;e++)void 0!==b[e]&&(n={series:m},m.pointClass.prototype.applyOptions.apply(n,[b[e]]),m.updateParallelArrays(n,e));D&&k(D[0])&&a.error(14,!0);m.data=[];m.options.data=m.userOptions.data=b;for(e=l;e--;)g[e]&&g[e].destroy&&g[e].destroy();A&&(A.minRange=A.userMinRange);m.isDirty=F.isDirtyBox=!0;m.isDirtyData=!!g;e=!1}"point"===p.legendType&&(this.processData(),this.generatePoints());c&&
F.redraw(e)},processData:function(b){var c=this.xData,d=this.yData,e=c.length,h;h=0;var g,k,f=this.xAxis,m,l=this.options;m=l.cropThreshold;var x=this.getExtremesFromAll||l.getExtremesFromAll,p=this.isCartesian,l=f&&f.val2lin,n=f&&f.isLog,r=this.requireSorting,z,q;if(p&&!this.isDirty&&!f.isDirty&&!this.yAxis.isDirty&&!b)return!1;f&&(b=f.getExtremes(),z=b.min,q=b.max);if(p&&this.sorted&&!x&&(!m||e>m||this.forceCrop))if(c[e-1]<z||c[0]>q)c=[],d=[];else if(c[0]<z||c[e-1]>q)h=this.cropData(this.xData,
this.yData,z,q),c=h.xData,d=h.yData,h=h.start,g=!0;for(m=c.length||1;--m;)e=n?l(c[m])-l(c[m-1]):c[m]-c[m-1],0<e&&(void 0===k||e<k)?k=e:0>e&&r&&(a.error(15),r=!1);this.cropped=g;this.cropStart=h;this.processedXData=c;this.processedYData=d;this.closestPointRange=k},cropData:function(a,b,c,d){var e=a.length,g=0,h=e,k=r(this.cropShoulder,1),f;for(f=0;f<e;f++)if(a[f]>=c){g=Math.max(0,f-k);break}for(c=f;c<e;c++)if(a[c]>d){h=c+k;break}return{xData:a.slice(g,h),yData:b.slice(g,h),start:g,end:h}},generatePoints:function(){var a=
this.options,b=a.data,c=this.data,d,e=this.processedXData,g=this.processedYData,h=this.pointClass,k=e.length,f=this.cropStart||0,m,l=this.hasGroupedData,a=a.keys,p,n=[],r;c||l||(c=[],c.length=b.length,c=this.data=c);a&&l&&(this.options.keys=!1);for(r=0;r<k;r++)m=f+r,l?(p=(new h).init(this,[e[r]].concat(z(g[r]))),p.dataGroup=this.groupMap[r]):(p=c[m])||void 0===b[m]||(c[m]=p=(new h).init(this,b[m],e[r])),p&&(p.index=m,n[r]=p);this.options.keys=a;if(c&&(k!==(d=c.length)||l))for(r=0;r<d;r++)r!==f||l||
(r+=k),c[r]&&(c[r].destroyElements(),c[r].plotX=void 0);this.data=c;this.points=n},getExtremes:function(a){var b=this.yAxis,c=this.processedXData,e,k=[],g=0;e=this.xAxis.getExtremes();var f=e.min,m=e.max,l,p,x,n;a=a||this.stackedYData||this.processedYData||[];e=a.length;for(n=0;n<e;n++)if(p=c[n],x=a[n],l=(h(x,!0)||d(x))&&(!b.positiveValuesOnly||x.length||0<x),p=this.getExtremesFromAll||this.options.getExtremesFromAll||this.cropped||(c[n+1]||p)>=f&&(c[n-1]||p)<=m,l&&p)if(l=x.length)for(;l--;)"number"===
typeof x[l]&&(k[g++]=x[l]);else k[g++]=x;this.dataMin=q(k);this.dataMax=E(k)},translate:function(){this.processedXData||this.processData();this.generatePoints();var a=this.options,c=a.stacking,e=this.xAxis,d=e.categories,k=this.yAxis,g=this.points,m=g.length,l=!!this.modifyValue,p=a.pointPlacement,z="between"===p||h(p),q=a.threshold,D=a.startFromThreshold?q:0,t,C,v,u,M=Number.MAX_VALUE;"between"===p&&(p=.5);h(p)&&(p*=r(a.pointRange||e.pointRange));for(a=0;a<m;a++){var I=g[a],B=I.x,E=I.y;C=I.low;var H=
c&&k.stacks[(this.negStacks&&E<(D?0:q)?"-":"")+this.stackKey],K;k.positiveValuesOnly&&null!==E&&0>=E&&(I.isNull=!0);I.plotX=t=f(Math.min(Math.max(-1E5,e.translate(B,0,0,0,1,p,"flags"===this.type)),1E5));c&&this.visible&&!I.isNull&&H&&H[B]&&(u=this.getStackIndicator(u,B,this.index),K=H[B],E=K.points[u.key],C=E[0],E=E[1],C===D&&u.key===H[B].base&&(C=r(q,k.min)),k.positiveValuesOnly&&0>=C&&(C=null),I.total=I.stackTotal=K.total,I.percentage=K.total&&I.y/K.total*100,I.stackY=E,K.setOffset(this.pointXOffset||
0,this.barW||0));I.yBottom=n(C)?Math.min(Math.max(-1E5,k.translate(C,0,1,0,1)),1E5):null;l&&(E=this.modifyValue(E,I));I.plotY=C="number"===typeof E&&Infinity!==E?Math.min(Math.max(-1E5,k.translate(E,0,1,0,1)),1E5):void 0;I.isInside=void 0!==C&&0<=C&&C<=k.len&&0<=t&&t<=e.len;I.clientX=z?f(e.translate(B,0,0,0,1,p)):t;I.negative=I.y<(q||0);I.category=d&&void 0!==d[I.x]?d[I.x]:I.x;I.isNull||(void 0!==v&&(M=Math.min(M,Math.abs(t-v))),v=t);I.zone=this.zones.length&&I.getZone()}this.closestPointRangePx=
M;b(this,"afterTranslate")},getValidPoints:function(a,b){var c=this.chart;return m(a||this.points||[],function(a){return b&&!c.isInsidePlot(a.plotX,a.plotY,c.inverted)?!1:!a.isNull})},setClip:function(a){var b=this.chart,c=this.options,e=b.renderer,d=b.inverted,g=this.clipBox,h=g||b.clipBox,k=this.sharedClipKey||["_sharedClip",a&&a.duration,a&&a.easing,h.height,c.xAxis,c.yAxis].join(),f=b[k],m=b[k+"m"];f||(a&&(h.width=0,d&&(h.x=b.plotSizeX),b[k+"m"]=m=e.clipRect(d?b.plotSizeX+99:-99,d?-b.plotLeft:
-b.plotTop,99,d?b.chartWidth:b.chartHeight)),b[k]=f=e.clipRect(h),f.count={length:0});a&&!f.count[this.index]&&(f.count[this.index]=!0,f.count.length+=1);!1!==c.clip&&(this.group.clip(a||g?f:b.clipRect),this.markerGroup.clip(m),this.sharedClipKey=k);a||(f.count[this.index]&&(delete f.count[this.index],--f.count.length),0===f.count.length&&k&&b[k]&&(g||(b[k]=b[k].destroy()),b[k+"m"]&&(b[k+"m"]=b[k+"m"].destroy())))},animate:function(a){var b=this.chart,c=H(this.options.animation),e;a?this.setClip(c):
(e=this.sharedClipKey,(a=b[e])&&a.animate({width:b.plotSizeX,x:0},c),b[e+"m"]&&b[e+"m"].animate({width:b.plotSizeX+99,x:0},c),this.animate=null)},afterAnimate:function(){this.setClip();b(this,"afterAnimate");this.finishedAnimating=!0},drawPoints:function(){var a=this.points,b=this.chart,c,e,d,g,h=this.options.marker,k,f,m,l=this[this.specialGroup]||this.markerGroup,p,n=r(h.enabled,this.xAxis.isRadial?!0:null,this.closestPointRangePx>=h.enabledThreshold*h.radius);if(!1!==h.enabled||this._hasPointMarkers)for(c=
0;c<a.length;c++)e=a[c],g=e.graphic,k=e.marker||{},f=!!e.marker,d=n&&void 0===k.enabled||k.enabled,m=e.isInside,d&&!e.isNull?(d=r(k.symbol,this.symbol),p=this.markerAttribs(e,e.selected&&"select"),g?g[m?"show":"hide"](!0).animate(p):m&&(0<p.width||e.hasImage)&&(e.graphic=g=b.renderer.symbol(d,p.x,p.y,p.width,p.height,f?k:h).add(l)),g&&g.attr(this.pointAttribs(e,e.selected&&"select")),g&&g.addClass(e.getClassName(),!0)):g&&(e.graphic=g.destroy())},markerAttribs:function(a,b){var c=this.options.marker,
e=a.marker||{},d=e.symbol||c.symbol,g=r(e.radius,c.radius);b&&(c=c.states[b],b=e.states&&e.states[b],g=r(b&&b.radius,c&&c.radius,g+(c&&c.radiusPlus||0)));a.hasImage=d&&0===d.indexOf("url");a.hasImage&&(g=0);a={x:Math.floor(a.plotX)-g,y:a.plotY-g};g&&(a.width=a.height=2*g);return a},pointAttribs:function(a,b){var c=this.options.marker,e=a&&a.options,d=e&&e.marker||{},g=this.color,h=e&&e.color,k=a&&a.color,e=r(d.lineWidth,c.lineWidth);a=a&&a.zone&&a.zone.color;g=h||a||k||g;a=d.fillColor||c.fillColor||
g;g=d.lineColor||c.lineColor||g;b&&(c=c.states[b],b=d.states&&d.states[b]||{},e=r(b.lineWidth,c.lineWidth,e+r(b.lineWidthPlus,c.lineWidthPlus,0)),a=b.fillColor||c.fillColor||a,g=b.lineColor||c.lineColor||g);return{stroke:g,"stroke-width":e,fill:a}},destroy:function(){var a=this,c=a.chart,e=/AppleWebKit\/533/.test(C.navigator.userAgent),d,h,g=a.data||[],k,f;b(a,"destroy");I(a);v(a.axisTypes||[],function(b){(f=a[b])&&f.series&&(u(f.series,a),f.isDirty=f.forceRedraw=!0)});a.legendItem&&a.chart.legend.destroyItem(a);
for(h=g.length;h--;)(k=g[h])&&k.destroy&&k.destroy();a.points=null;clearTimeout(a.animationTimeout);p(a,function(a,b){a instanceof M&&!a.survive&&(d=e&&"group"===b?"hide":"destroy",a[d]())});c.hoverSeries===a&&(c.hoverSeries=null);u(c.series,a);c.orderSeries();p(a,function(b,c){delete a[c]})},getGraphPath:function(a,b,c){var e=this,d=e.options,g=d.step,h,k=[],f=[],m;a=a||e.points;(h=a.reversed)&&a.reverse();(g={right:1,center:2}[g]||g&&3)&&h&&(g=4-g);!d.connectNulls||b||c||(a=this.getValidPoints(a));
v(a,function(h,l){var p=h.plotX,x=h.plotY,r=a[l-1];(h.leftCliff||r&&r.rightCliff)&&!c&&(m=!0);h.isNull&&!n(b)&&0<l?m=!d.connectNulls:h.isNull&&!b?m=!0:(0===l||m?l=["M",h.plotX,h.plotY]:e.getPointSpline?l=e.getPointSpline(a,h,l):g?(l=1===g?["L",r.plotX,x]:2===g?["L",(r.plotX+p)/2,r.plotY,"L",(r.plotX+p)/2,x]:["L",p,r.plotY],l.push("L",p,x)):l=["L",p,x],f.push(h.x),g&&f.push(h.x),k.push.apply(k,l),m=!1)});k.xMap=f;return e.graphPath=k},drawGraph:function(){var a=this,b=this.options,c=(this.gappedPath||
this.getGraphPath).call(this),e=[["graph","highcharts-graph",b.lineColor||this.color,b.dashStyle]];v(this.zones,function(c,d){e.push(["zone-graph-"+d,"highcharts-graph highcharts-zone-graph-"+d+" "+(c.className||""),c.color||a.color,c.dashStyle||b.dashStyle])});v(e,function(e,d){var g=e[0],h=a[g];h?(h.endX=a.preventGraphAnimation?null:c.xMap,h.animate({d:c})):c.length&&(a[g]=a.chart.renderer.path(c).addClass(e[1]).attr({zIndex:1}).add(a.group),h={stroke:e[2],"stroke-width":b.lineWidth,fill:a.fillGraph&&
a.color||"none"},e[3]?h.dashstyle=e[3]:"square"!==b.linecap&&(h["stroke-linecap"]=h["stroke-linejoin"]="round"),h=a[g].attr(h).shadow(2>d&&b.shadow));h&&(h.startX=c.xMap,h.isArea=c.isArea)})},applyZones:function(){var a=this,b=this.chart,c=b.renderer,e=this.zones,d,g,h=this.clips||[],k,f=this.graph,m=this.area,l=Math.max(b.chartWidth,b.chartHeight),p=this[(this.zoneAxis||"y")+"Axis"],n,z,q=b.inverted,D,t,C,u,M=!1;e.length&&(f||m)&&p&&void 0!==p.min&&(z=p.reversed,D=p.horiz,f&&f.hide(),m&&m.hide(),
n=p.getExtremes(),v(e,function(e,x){d=z?D?b.plotWidth:0:D?0:p.toPixels(n.min);d=Math.min(Math.max(r(g,d),0),l);g=Math.min(Math.max(Math.round(p.toPixels(r(e.value,n.max),!0)),0),l);M&&(d=g=p.toPixels(n.max));t=Math.abs(d-g);C=Math.min(d,g);u=Math.max(d,g);p.isXAxis?(k={x:q?u:C,y:0,width:t,height:l},D||(k.x=b.plotHeight-k.x)):(k={x:0,y:q?u:C,width:l,height:t},D&&(k.y=b.plotWidth-k.y));q&&c.isVML&&(k=p.isXAxis?{x:0,y:z?C:u,height:k.width,width:b.chartWidth}:{x:k.y-b.plotLeft-b.spacingBox.x,y:0,width:k.height,
height:b.chartHeight});h[x]?h[x].animate(k):(h[x]=c.clipRect(k),f&&a["zone-graph-"+x].clip(h[x]),m&&a["zone-area-"+x].clip(h[x]));M=e.value>n.max}),this.clips=h)},invertGroups:function(a){function b(){v(["group","markerGroup"],function(b){c[b]&&(e.renderer.isVML&&c[b].attr({width:c.yAxis.len,height:c.xAxis.len}),c[b].width=c.yAxis.len,c[b].height=c.xAxis.len,c[b].invert(a))})}var c=this,e=c.chart,d;c.xAxis&&(d=B(e,"resize",b),B(c,"destroy",d),b(a),c.invertGroups=b)},plotGroup:function(a,b,c,e,d){var g=
this[a],h=!g;h&&(this[a]=g=this.chart.renderer.g().attr({zIndex:e||.1}).add(d));g.addClass("highcharts-"+b+" highcharts-series-"+this.index+" highcharts-"+this.type+"-series "+(n(this.colorIndex)?"highcharts-color-"+this.colorIndex+" ":"")+(this.options.className||"")+(g.hasClass("highcharts-tracker")?" highcharts-tracker":""),!0);g.attr({visibility:c})[h?"attr":"animate"](this.getPlotBox());return g},getPlotBox:function(){var a=this.chart,b=this.xAxis,c=this.yAxis;a.inverted&&(b=c,c=this.xAxis);
return{translateX:b?b.left:a.plotLeft,translateY:c?c.top:a.plotTop,scaleX:1,scaleY:1}},render:function(){var a=this,c=a.chart,e,d=a.options,h=!!a.animate&&c.renderer.isSVG&&H(d.animation).duration,g=a.visible?"inherit":"hidden",k=d.zIndex,f=a.hasRendered,m=c.seriesGroup,l=c.inverted;e=a.plotGroup("group","series",g,k,m);a.markerGroup=a.plotGroup("markerGroup","markers",g,k,m);h&&a.animate(!0);e.inverted=a.isCartesian?l:!1;a.drawGraph&&(a.drawGraph(),a.applyZones());a.drawDataLabels&&a.drawDataLabels();
a.visible&&a.drawPoints();a.drawTracker&&!1!==a.options.enableMouseTracking&&a.drawTracker();a.invertGroups(l);!1===d.clip||a.sharedClipKey||f||e.clip(c.clipRect);h&&a.animate();f||(a.animationTimeout=D(function(){a.afterAnimate()},h));a.isDirty=!1;a.hasRendered=!0;b(a,"afterRender")},redraw:function(){var a=this.chart,b=this.isDirty||this.isDirtyData,c=this.group,e=this.xAxis,d=this.yAxis;c&&(a.inverted&&c.attr({width:a.plotWidth,height:a.plotHeight}),c.animate({translateX:r(e&&e.left,a.plotLeft),
translateY:r(d&&d.top,a.plotTop)}));this.translate();this.render();b&&delete this.kdTree},kdAxisArray:["clientX","plotY"],searchPoint:function(a,b){var c=this.xAxis,e=this.yAxis,d=this.chart.inverted;return this.searchKDTree({clientX:d?c.len-a.chartY+c.pos:a.chartX-c.pos,plotY:d?e.len-a.chartX+e.pos:a.chartY-e.pos},b)},buildKDTree:function(){function a(c,e,d){var g,h;if(h=c&&c.length)return g=b.kdAxisArray[e%d],c.sort(function(a,b){return a[g]-b[g]}),h=Math.floor(h/2),{point:c[h],left:a(c.slice(0,
h),e+1,d),right:a(c.slice(h+1),e+1,d)}}this.buildingKdTree=!0;var b=this,c=-1<b.options.findNearestPointBy.indexOf("y")?2:1;delete b.kdTree;D(function(){b.kdTree=a(b.getValidPoints(null,!b.directTouch),c,c);b.buildingKdTree=!1},b.options.kdNow?0:1)},searchKDTree:function(a,b){function c(a,b,k,f){var m=b.point,l=e.kdAxisArray[k%f],p,r,z=m;r=n(a[d])&&n(m[d])?Math.pow(a[d]-m[d],2):null;p=n(a[g])&&n(m[g])?Math.pow(a[g]-m[g],2):null;p=(r||0)+(p||0);m.dist=n(p)?Math.sqrt(p):Number.MAX_VALUE;m.distX=n(r)?
Math.sqrt(r):Number.MAX_VALUE;l=a[l]-m[l];p=0>l?"left":"right";r=0>l?"right":"left";b[p]&&(p=c(a,b[p],k+1,f),z=p[h]<z[h]?p:m);b[r]&&Math.sqrt(l*l)<z[h]&&(a=c(a,b[r],k+1,f),z=a[h]<z[h]?a:z);return z}var e=this,d=this.kdAxisArray[0],g=this.kdAxisArray[1],h=b?"distX":"dist";b=-1<e.options.findNearestPointBy.indexOf("y")?2:1;this.kdTree||this.buildingKdTree||this.buildKDTree();if(this.kdTree)return c(a,this.kdTree,b,b)}})})(K);(function(a){var B=a.Axis,H=a.Chart,E=a.correctFloat,q=a.defined,f=a.destroyObjectProperties,
l=a.each,t=a.format,n=a.objectEach,v=a.pick,u=a.Series;a.StackItem=function(a,b,f,d,h){var c=a.chart.inverted;this.axis=a;this.isNegative=f;this.options=b;this.x=d;this.total=null;this.points={};this.stack=h;this.rightCliff=this.leftCliff=0;this.alignOptions={align:b.align||(c?f?"left":"right":"center"),verticalAlign:b.verticalAlign||(c?"middle":f?"bottom":"top"),y:v(b.y,c?4:f?14:-6),x:v(b.x,c?f?-6:6:0)};this.textAlign=b.textAlign||(c?f?"right":"left":"center")};a.StackItem.prototype={destroy:function(){f(this,
this.axis)},render:function(a){var b=this.axis.chart,c=this.options,d=c.format,d=d?t(d,this,b.time):c.formatter.call(this);this.label?this.label.attr({text:d,visibility:"hidden"}):this.label=b.renderer.text(d,null,null,c.useHTML).css(c.style).attr({align:this.textAlign,rotation:c.rotation,visibility:"hidden"}).add(a)},setOffset:function(a,b){var c=this.axis,d=c.chart,h=c.translate(c.usePercentage?100:this.total,0,0,0,1),c=c.translate(0),c=Math.abs(h-c);a=d.xAxis[0].translate(this.x)+a;h=this.getStackBox(d,
this,a,h,b,c);if(b=this.label)b.align(this.alignOptions,null,h),h=b.alignAttr,b[!1===this.options.crop||d.isInsidePlot(h.x,h.y)?"show":"hide"](!0)},getStackBox:function(a,b,f,d,h,k){var c=b.axis.reversed,m=a.inverted;a=a.plotHeight;b=b.isNegative&&!c||!b.isNegative&&c;return{x:m?b?d:d-k:f,y:m?a-f-h:b?a-d-k:a-d,width:m?k:h,height:m?h:k}}};H.prototype.getStacks=function(){var a=this;l(a.yAxis,function(a){a.stacks&&a.hasVisibleSeries&&(a.oldStacks=a.stacks)});l(a.series,function(b){!b.options.stacking||
!0!==b.visible&&!1!==a.options.chart.ignoreHiddenSeries||(b.stackKey=b.type+v(b.options.stack,""))})};B.prototype.buildStacks=function(){var a=this.series,b=v(this.options.reversedStacks,!0),f=a.length,d;if(!this.isXAxis){this.usePercentage=!1;for(d=f;d--;)a[b?d:f-d-1].setStackedPoints();for(d=0;d<f;d++)a[d].modifyStacks()}};B.prototype.renderStackTotals=function(){var a=this.chart,b=a.renderer,f=this.stacks,d=this.stackTotalGroup;d||(this.stackTotalGroup=d=b.g("stack-labels").attr({visibility:"visible",
zIndex:6}).add());d.translate(a.plotLeft,a.plotTop);n(f,function(a){n(a,function(a){a.render(d)})})};B.prototype.resetStacks=function(){var a=this,b=a.stacks;a.isXAxis||n(b,function(b){n(b,function(c,h){c.touched<a.stacksTouched?(c.destroy(),delete b[h]):(c.total=null,c.cumulative=null)})})};B.prototype.cleanStacks=function(){var a;this.isXAxis||(this.oldStacks&&(a=this.stacks=this.oldStacks),n(a,function(a){n(a,function(a){a.cumulative=a.total})}))};u.prototype.setStackedPoints=function(){if(this.options.stacking&&
(!0===this.visible||!1===this.chart.options.chart.ignoreHiddenSeries)){var c=this.processedXData,b=this.processedYData,f=[],d=b.length,h=this.options,k=h.threshold,e=v(h.startFromThreshold&&k,0),l=h.stack,h=h.stacking,n=this.stackKey,t="-"+n,z=this.negStacks,u=this.yAxis,D=u.stacks,C=u.oldStacks,x,F,A,J,G,g,w;u.stacksTouched+=1;for(G=0;G<d;G++)g=c[G],w=b[G],x=this.getStackIndicator(x,g,this.index),J=x.key,A=(F=z&&w<(e?0:k))?t:n,D[A]||(D[A]={}),D[A][g]||(C[A]&&C[A][g]?(D[A][g]=C[A][g],D[A][g].total=
null):D[A][g]=new a.StackItem(u,u.options.stackLabels,F,g,l)),A=D[A][g],null!==w?(A.points[J]=A.points[this.index]=[v(A.cumulative,e)],q(A.cumulative)||(A.base=J),A.touched=u.stacksTouched,0<x.index&&!1===this.singleStacks&&(A.points[J][0]=A.points[this.index+","+g+",0"][0])):A.points[J]=A.points[this.index]=null,"percent"===h?(F=F?n:t,z&&D[F]&&D[F][g]?(F=D[F][g],A.total=F.total=Math.max(F.total,A.total)+Math.abs(w)||0):A.total=E(A.total+(Math.abs(w)||0))):A.total=E(A.total+(w||0)),A.cumulative=v(A.cumulative,
e)+(w||0),null!==w&&(A.points[J].push(A.cumulative),f[G]=A.cumulative);"percent"===h&&(u.usePercentage=!0);this.stackedYData=f;u.oldStacks={}}};u.prototype.modifyStacks=function(){var a=this,b=a.stackKey,f=a.yAxis.stacks,d=a.processedXData,h,k=a.options.stacking;a[k+"Stacker"]&&l([b,"-"+b],function(b){for(var c=d.length,e,l;c--;)if(e=d[c],h=a.getStackIndicator(h,e,a.index,b),l=(e=f[b]&&f[b][e])&&e.points[h.key])a[k+"Stacker"](l,e,c)})};u.prototype.percentStacker=function(a,b,f){b=b.total?100/b.total:
0;a[0]=E(a[0]*b);a[1]=E(a[1]*b);this.stackedYData[f]=a[1]};u.prototype.getStackIndicator=function(a,b,f,d){!q(a)||a.x!==b||d&&a.key!==d?a={x:b,index:0,key:d}:a.index++;a.key=[f,b,a.index].join();return a}})(K);(function(a){var B=a.addEvent,H=a.animate,E=a.Axis,q=a.createElement,f=a.css,l=a.defined,t=a.each,n=a.erase,v=a.extend,u=a.fireEvent,c=a.inArray,b=a.isNumber,m=a.isObject,d=a.isArray,h=a.merge,k=a.objectEach,e=a.pick,p=a.Point,r=a.Series,I=a.seriesTypes,z=a.setAnimation,M=a.splat;v(a.Chart.prototype,
{addSeries:function(a,b,c){var d,h=this;a&&(b=e(b,!0),u(h,"addSeries",{options:a},function(){d=h.initSeries(a);h.isDirtyLegend=!0;h.linkSeries();b&&h.redraw(c)}));return d},addAxis:function(a,b,c,d){var k=b?"xAxis":"yAxis",f=this.options;a=h(a,{index:this[k].length,isX:b});b=new E(this,a);f[k]=M(f[k]||{});f[k].push(a);e(c,!0)&&this.redraw(d);return b},showLoading:function(a){var b=this,c=b.options,e=b.loadingDiv,d=c.loading,h=function(){e&&f(e,{left:b.plotLeft+"px",top:b.plotTop+"px",width:b.plotWidth+
"px",height:b.plotHeight+"px"})};e||(b.loadingDiv=e=q("div",{className:"highcharts-loading highcharts-loading-hidden"},null,b.container),b.loadingSpan=q("span",{className:"highcharts-loading-inner"},null,e),B(b,"redraw",h));e.className="highcharts-loading";b.loadingSpan.innerHTML=a||c.lang.loading;f(e,v(d.style,{zIndex:10}));f(b.loadingSpan,d.labelStyle);b.loadingShown||(f(e,{opacity:0,display:""}),H(e,{opacity:d.style.opacity||.5},{duration:d.showDuration||0}));b.loadingShown=!0;h()},hideLoading:function(){var a=
this.options,b=this.loadingDiv;b&&(b.className="highcharts-loading highcharts-loading-hidden",H(b,{opacity:0},{duration:a.loading.hideDuration||100,complete:function(){f(b,{display:"none"})}}));this.loadingShown=!1},propsRequireDirtyBox:"backgroundColor borderColor borderWidth margin marginTop marginRight marginBottom marginLeft spacing spacingTop spacingRight spacingBottom spacingLeft borderRadius plotBackgroundColor plotBackgroundImage plotBorderColor plotBorderWidth plotShadow shadow".split(" "),
propsRequireUpdateSeries:"chart.inverted chart.polar chart.ignoreHiddenSeries chart.type colors plotOptions time tooltip".split(" "),update:function(a,d,f){var p=this,m={credits:"addCredits",title:"setTitle",subtitle:"setSubtitle"},n=a.chart,r,g,z=[];if(n){h(!0,p.options.chart,n);"className"in n&&p.setClassName(n.className);if("inverted"in n||"polar"in n)p.propFromSeries(),r=!0;"alignTicks"in n&&(r=!0);k(n,function(a,b){-1!==c("chart."+b,p.propsRequireUpdateSeries)&&(g=!0);-1!==c(b,p.propsRequireDirtyBox)&&
(p.isDirtyBox=!0)});"style"in n&&p.renderer.setStyle(n.style)}a.colors&&(this.options.colors=a.colors);a.plotOptions&&h(!0,this.options.plotOptions,a.plotOptions);k(a,function(a,b){if(p[b]&&"function"===typeof p[b].update)p[b].update(a,!1);else if("function"===typeof p[m[b]])p[m[b]](a);"chart"!==b&&-1!==c(b,p.propsRequireUpdateSeries)&&(g=!0)});t("xAxis yAxis zAxis series colorAxis pane".split(" "),function(b){a[b]&&(t(M(a[b]),function(a,c){(c=l(a.id)&&p.get(a.id)||p[b][c])&&c.coll===b&&(c.update(a,
!1),f&&(c.touched=!0));if(!c&&f)if("series"===b)p.addSeries(a,!1).touched=!0;else if("xAxis"===b||"yAxis"===b)p.addAxis(a,"xAxis"===b,!1).touched=!0}),f&&t(p[b],function(a){a.touched?delete a.touched:z.push(a)}))});t(z,function(a){a.remove(!1)});r&&t(p.axes,function(a){a.update({},!1)});g&&t(p.series,function(a){a.update({},!1)});a.loading&&h(!0,p.options.loading,a.loading);r=n&&n.width;n=n&&n.height;b(r)&&r!==p.chartWidth||b(n)&&n!==p.chartHeight?p.setSize(r,n):e(d,!0)&&p.redraw()},setSubtitle:function(a){this.setTitle(void 0,
a)}});v(p.prototype,{update:function(a,b,c,d){function h(){k.applyOptions(a);null===k.y&&g&&(k.graphic=g.destroy());m(a,!0)&&(g&&g.element&&a&&a.marker&&void 0!==a.marker.symbol&&(k.graphic=g.destroy()),a&&a.dataLabels&&k.dataLabel&&(k.dataLabel=k.dataLabel.destroy()),k.connector&&(k.connector=k.connector.destroy()));p=k.index;f.updateParallelArrays(k,p);n.data[p]=m(n.data[p],!0)||m(a,!0)?k.options:a;f.isDirty=f.isDirtyData=!0;!f.fixedBox&&f.hasCartesianSeries&&(l.isDirtyBox=!0);"point"===n.legendType&&
(l.isDirtyLegend=!0);b&&l.redraw(c)}var k=this,f=k.series,g=k.graphic,p,l=f.chart,n=f.options;b=e(b,!0);!1===d?h():k.firePointEvent("update",{options:a},h)},remove:function(a,b){this.series.removePoint(c(this,this.series.data),a,b)}});v(r.prototype,{addPoint:function(a,b,c,d){var h=this.options,k=this.data,f=this.chart,g=this.xAxis,g=g&&g.hasNames&&g.names,p=h.data,l,m,n=this.xData,r,z;b=e(b,!0);l={series:this};this.pointClass.prototype.applyOptions.apply(l,[a]);z=l.x;r=n.length;if(this.requireSorting&&
z<n[r-1])for(m=!0;r&&n[r-1]>z;)r--;this.updateParallelArrays(l,"splice",r,0,0);this.updateParallelArrays(l,r);g&&l.name&&(g[z]=l.name);p.splice(r,0,a);m&&(this.data.splice(r,0,null),this.processData());"point"===h.legendType&&this.generatePoints();c&&(k[0]&&k[0].remove?k[0].remove(!1):(k.shift(),this.updateParallelArrays(l,"shift"),p.shift()));this.isDirtyData=this.isDirty=!0;b&&f.redraw(d)},removePoint:function(a,b,c){var d=this,h=d.data,k=h[a],f=d.points,g=d.chart,p=function(){f&&f.length===h.length&&
f.splice(a,1);h.splice(a,1);d.options.data.splice(a,1);d.updateParallelArrays(k||{series:d},"splice",a,1);k&&k.destroy();d.isDirty=!0;d.isDirtyData=!0;b&&g.redraw()};z(c,g);b=e(b,!0);k?k.firePointEvent("remove",null,p):p()},remove:function(a,b,c){function d(){h.destroy();k.isDirtyLegend=k.isDirtyBox=!0;k.linkSeries();e(a,!0)&&k.redraw(b)}var h=this,k=h.chart;!1!==c?u(h,"remove",null,d):d()},update:function(a,b){var c=this,d=c.chart,k=c.userOptions,f=c.oldType||c.type,p=a.type||k.type||d.options.chart.type,
g=I[f].prototype,l,m=["group","markerGroup","dataLabelsGroup"],n=["navigatorSeries","baseSeries"],r=c.finishedAnimating&&{animation:!1};if(Object.keys&&"data"===Object.keys(a).toString())return this.setData(a.data,b);n=m.concat(n);t(n,function(a){n[a]=c[a];delete c[a]});a=h(k,r,{index:c.index,pointStart:c.xData[0]},{data:c.options.data},a);c.remove(!1,null,!1);for(l in g)c[l]=void 0;v(c,I[p||f].prototype);t(n,function(a){c[a]=n[a]});c.init(d,a);a.zIndex!==k.zIndex&&t(m,function(b){c[b]&&c[b].attr({zIndex:a.zIndex})});
c.oldType=f;d.linkSeries();e(b,!0)&&d.redraw(!1)}});v(E.prototype,{update:function(a,b){var c=this.chart;a=c.options[this.coll][this.options.index]=h(this.userOptions,a);this.destroy(!0);this.init(c,v(a,{events:void 0}));c.isDirtyBox=!0;e(b,!0)&&c.redraw()},remove:function(a){for(var b=this.chart,c=this.coll,h=this.series,k=h.length;k--;)h[k]&&h[k].remove(!1);n(b.axes,this);n(b[c],this);d(b.options[c])?b.options[c].splice(this.options.index,1):delete b.options[c];t(b[c],function(a,b){a.options.index=
b});this.destroy();b.isDirtyBox=!0;e(a,!0)&&b.redraw()},setTitle:function(a,b){this.update({title:a},b)},setCategories:function(a,b){this.update({categories:a},b)}})})(K);(function(a){var B=a.color,H=a.each,E=a.map,q=a.pick,f=a.Series,l=a.seriesType;l("area","line",{softThreshold:!1,threshold:0},{singleStacks:!1,getStackPoints:function(f){var l=[],t=[],u=this.xAxis,c=this.yAxis,b=c.stacks[this.stackKey],m={},d=this.index,h=c.series,k=h.length,e,p=q(c.options.reversedStacks,!0)?1:-1,r;f=f||this.points;
if(this.options.stacking){for(r=0;r<f.length;r++)f[r].leftNull=f[r].rightNull=null,m[f[r].x]=f[r];a.objectEach(b,function(a,b){null!==a.total&&t.push(b)});t.sort(function(a,b){return a-b});e=E(h,function(){return this.visible});H(t,function(a,h){var f=0,n,z;if(m[a]&&!m[a].isNull)l.push(m[a]),H([-1,1],function(c){var f=1===c?"rightNull":"leftNull",l=0,q=b[t[h+c]];if(q)for(r=d;0<=r&&r<k;)n=q.points[r],n||(r===d?m[a][f]=!0:e[r]&&(z=b[a].points[r])&&(l-=z[1]-z[0])),r+=p;m[a][1===c?"rightCliff":"leftCliff"]=
l});else{for(r=d;0<=r&&r<k;){if(n=b[a].points[r]){f=n[1];break}r+=p}f=c.translate(f,0,1,0,1);l.push({isNull:!0,plotX:u.translate(a,0,0,0,1),x:a,plotY:f,yBottom:f})}})}return l},getGraphPath:function(a){var l=f.prototype.getGraphPath,t=this.options,u=t.stacking,c=this.yAxis,b,m,d=[],h=[],k=this.index,e,p=c.stacks[this.stackKey],r=t.threshold,I=c.getThreshold(t.threshold),z,t=t.connectNulls||"percent"===u,M=function(b,f,l){var m=a[b];b=u&&p[m.x].points[k];var n=m[l+"Null"]||0;l=m[l+"Cliff"]||0;var z,
q,m=!0;l||n?(z=(n?b[0]:b[1])+l,q=b[0]+l,m=!!n):!u&&a[f]&&a[f].isNull&&(z=q=r);void 0!==z&&(h.push({plotX:e,plotY:null===z?I:c.getThreshold(z),isNull:m,isCliff:!0}),d.push({plotX:e,plotY:null===q?I:c.getThreshold(q),doCurve:!1}))};a=a||this.points;u&&(a=this.getStackPoints(a));for(b=0;b<a.length;b++)if(m=a[b].isNull,e=q(a[b].rectPlotX,a[b].plotX),z=q(a[b].yBottom,I),!m||t)t||M(b,b-1,"left"),m&&!u&&t||(h.push(a[b]),d.push({x:b,plotX:e,plotY:z})),t||M(b,b+1,"right");b=l.call(this,h,!0,!0);d.reversed=
!0;m=l.call(this,d,!0,!0);m.length&&(m[0]="L");m=b.concat(m);l=l.call(this,h,!1,t);m.xMap=b.xMap;this.areaPath=m;return l},drawGraph:function(){this.areaPath=[];f.prototype.drawGraph.apply(this);var a=this,l=this.areaPath,v=this.options,u=[["area","highcharts-area",this.color,v.fillColor]];H(this.zones,function(c,b){u.push(["zone-area-"+b,"highcharts-area highcharts-zone-area-"+b+" "+c.className,c.color||a.color,c.fillColor||v.fillColor])});H(u,function(c){var b=c[0],f=a[b];f?(f.endX=a.preventGraphAnimation?
null:l.xMap,f.animate({d:l})):(f=a[b]=a.chart.renderer.path(l).addClass(c[1]).attr({fill:q(c[3],B(c[2]).setOpacity(q(v.fillOpacity,.75)).get()),zIndex:0}).add(a.group),f.isArea=!0);f.startX=l.xMap;f.shiftUnit=v.step?2:1})},drawLegendSymbol:a.LegendSymbolMixin.drawRectangle})})(K);(function(a){var B=a.pick;a=a.seriesType;a("spline","line",{},{getPointSpline:function(a,E,q){var f=E.plotX,l=E.plotY,t=a[q-1];q=a[q+1];var n,v,u,c;if(t&&!t.isNull&&!1!==t.doCurve&&!E.isCliff&&q&&!q.isNull&&!1!==q.doCurve&&
!E.isCliff){a=t.plotY;u=q.plotX;q=q.plotY;var b=0;n=(1.5*f+t.plotX)/2.5;v=(1.5*l+a)/2.5;u=(1.5*f+u)/2.5;c=(1.5*l+q)/2.5;u!==n&&(b=(c-v)*(u-f)/(u-n)+l-c);v+=b;c+=b;v>a&&v>l?(v=Math.max(a,l),c=2*l-v):v<a&&v<l&&(v=Math.min(a,l),c=2*l-v);c>q&&c>l?(c=Math.max(q,l),v=2*l-c):c<q&&c<l&&(c=Math.min(q,l),v=2*l-c);E.rightContX=u;E.rightContY=c}E=["C",B(t.rightContX,t.plotX),B(t.rightContY,t.plotY),B(n,f),B(v,l),f,l];t.rightContX=t.rightContY=null;return E}})})(K);(function(a){var B=a.seriesTypes.area.prototype,
H=a.seriesType;H("areaspline","spline",a.defaultPlotOptions.area,{getStackPoints:B.getStackPoints,getGraphPath:B.getGraphPath,drawGraph:B.drawGraph,drawLegendSymbol:a.LegendSymbolMixin.drawRectangle})})(K);(function(a){var B=a.animObject,H=a.color,E=a.each,q=a.extend,f=a.isNumber,l=a.merge,t=a.pick,n=a.Series,v=a.seriesType,u=a.svg;v("column","line",{borderRadius:0,crisp:!0,groupPadding:.2,marker:null,pointPadding:.1,minPointLength:0,cropThreshold:50,pointRange:null,states:{hover:{halo:!1,brightness:.1},
select:{color:"#cccccc",borderColor:"#000000"}},dataLabels:{align:null,verticalAlign:null,y:null},softThreshold:!1,startFromThreshold:!0,stickyTracking:!1,tooltip:{distance:6},threshold:0,borderColor:"#ffffff"},{cropShoulder:0,directTouch:!0,trackerGroups:["group","dataLabelsGroup"],negStacks:!0,init:function(){n.prototype.init.apply(this,arguments);var a=this,b=a.chart;b.hasRendered&&E(b.series,function(b){b.type===a.type&&(b.isDirty=!0)})},getColumnMetrics:function(){var a=this,b=a.options,f=a.xAxis,
d=a.yAxis,h=f.reversed,k,e={},l=0;!1===b.grouping?l=1:E(a.chart.series,function(b){var c=b.options,h=b.yAxis,f;b.type!==a.type||!b.visible&&a.chart.options.chart.ignoreHiddenSeries||d.len!==h.len||d.pos!==h.pos||(c.stacking?(k=b.stackKey,void 0===e[k]&&(e[k]=l++),f=e[k]):!1!==c.grouping&&(f=l++),b.columnIndex=f)});var n=Math.min(Math.abs(f.transA)*(f.ordinalSlope||b.pointRange||f.closestPointRange||f.tickInterval||1),f.len),q=n*b.groupPadding,z=(n-2*q)/(l||1),b=Math.min(b.maxPointWidth||f.len,t(b.pointWidth,
z*(1-2*b.pointPadding)));a.columnMetrics={width:b,offset:(z-b)/2+(q+((a.columnIndex||0)+(h?1:0))*z-n/2)*(h?-1:1)};return a.columnMetrics},crispCol:function(a,b,f,d){var c=this.chart,k=this.borderWidth,e=-(k%2?.5:0),k=k%2?.5:1;c.inverted&&c.renderer.isVML&&(k+=1);this.options.crisp&&(f=Math.round(a+f)+e,a=Math.round(a)+e,f-=a);d=Math.round(b+d)+k;e=.5>=Math.abs(b)&&.5<d;b=Math.round(b)+k;d-=b;e&&d&&(--b,d+=1);return{x:a,y:b,width:f,height:d}},translate:function(){var a=this,b=a.chart,f=a.options,d=
a.dense=2>a.closestPointRange*a.xAxis.transA,d=a.borderWidth=t(f.borderWidth,d?0:1),h=a.yAxis,k=f.threshold,e=a.translatedThreshold=h.getThreshold(k),l=t(f.minPointLength,5),r=a.getColumnMetrics(),q=r.width,z=a.barW=Math.max(q,1+2*d),u=a.pointXOffset=r.offset;b.inverted&&(e-=.5);f.pointPadding&&(z=Math.ceil(z));n.prototype.translate.apply(a);E(a.points,function(c){var d=t(c.yBottom,e),f=999+Math.abs(d),f=Math.min(Math.max(-f,c.plotY),h.len+f),p=c.plotX+u,m=z,n=Math.min(f,d),r,g=Math.max(f,d)-n;l&&
Math.abs(g)<l&&(g=l,r=!h.reversed&&!c.negative||h.reversed&&c.negative,c.y===k&&a.dataMax<=k&&h.min<k&&(r=!r),n=Math.abs(n-e)>l?d-l:e-(r?l:0));c.barX=p;c.pointWidth=q;c.tooltipPos=b.inverted?[h.len+h.pos-b.plotLeft-f,a.xAxis.len-p-m/2,g]:[p+m/2,f+h.pos-b.plotTop,g];c.shapeType="rect";c.shapeArgs=a.crispCol.apply(a,c.isNull?[p,e,m,0]:[p,n,m,g])})},getSymbol:a.noop,drawLegendSymbol:a.LegendSymbolMixin.drawRectangle,drawGraph:function(){this.group[this.dense?"addClass":"removeClass"]("highcharts-dense-data")},
pointAttribs:function(a,b){var c=this.options,d,h=this.pointAttrToOptions||{};d=h.stroke||"borderColor";var f=h["stroke-width"]||"borderWidth",e=a&&a.color||this.color,p=a&&a[d]||c[d]||this.color||e,n=a&&a[f]||c[f]||this[f]||0,h=c.dashStyle;a&&this.zones.length&&(e=a.getZone(),e=a.options.color||e&&e.color||this.color);b&&(a=l(c.states[b],a.options.states&&a.options.states[b]||{}),b=a.brightness,e=a.color||void 0!==b&&H(e).brighten(a.brightness).get()||e,p=a[d]||p,n=a[f]||n,h=a.dashStyle||h);d={fill:e,
stroke:p,"stroke-width":n};h&&(d.dashstyle=h);return d},drawPoints:function(){var a=this,b=this.chart,m=a.options,d=b.renderer,h=m.animationLimit||250,k;E(a.points,function(c){var e=c.graphic;if(f(c.plotY)&&null!==c.y){k=c.shapeArgs;if(e)e[b.pointCount<h?"animate":"attr"](l(k));else c.graphic=e=d[c.shapeType](k).add(c.group||a.group);m.borderRadius&&e.attr({r:m.borderRadius});e.attr(a.pointAttribs(c,c.selected&&"select")).shadow(m.shadow,null,m.stacking&&!m.borderRadius);e.addClass(c.getClassName(),
!0)}else e&&(c.graphic=e.destroy())})},animate:function(a){var b=this,c=this.yAxis,d=b.options,h=this.chart.inverted,f={},e=h?"translateX":"translateY",l;u&&(a?(f.scaleY=.001,a=Math.min(c.pos+c.len,Math.max(c.pos,c.toPixels(d.threshold))),h?f.translateX=a-c.len:f.translateY=a,b.group.attr(f)):(l=b.group.attr(e),b.group.animate({scaleY:1},q(B(b.options.animation),{step:function(a,d){f[e]=l+d.pos*(c.pos-l);b.group.attr(f)}})),b.animate=null))},remove:function(){var a=this,b=a.chart;b.hasRendered&&E(b.series,
function(b){b.type===a.type&&(b.isDirty=!0)});n.prototype.remove.apply(a,arguments)}})})(K);(function(a){a=a.seriesType;a("bar","column",null,{inverted:!0})})(K);(function(a){var B=a.Series;a=a.seriesType;a("scatter","line",{lineWidth:0,findNearestPointBy:"xy",marker:{enabled:!0},tooltip:{headerFormat:'\x3cspan style\x3d"color:{point.color}"\x3e\u25cf\x3c/span\x3e \x3cspan style\x3d"font-size: 0.85em"\x3e {series.name}\x3c/span\x3e\x3cbr/\x3e',pointFormat:"x: \x3cb\x3e{point.x}\x3c/b\x3e\x3cbr/\x3ey: \x3cb\x3e{point.y}\x3c/b\x3e\x3cbr/\x3e"}},
{sorted:!1,requireSorting:!1,noSharedTooltip:!0,trackerGroups:["group","markerGroup","dataLabelsGroup"],takeOrdinalPosition:!1,drawGraph:function(){this.options.lineWidth&&B.prototype.drawGraph.call(this)}})})(K);(function(a){var B=a.deg2rad,H=a.isNumber,E=a.pick,q=a.relativeLength;a.CenteredSeriesMixin={getCenter:function(){var a=this.options,l=this.chart,t=2*(a.slicedOffset||0),n=l.plotWidth-2*t,l=l.plotHeight-2*t,v=a.center,v=[E(v[0],"50%"),E(v[1],"50%"),a.size||"100%",a.innerSize||0],u=Math.min(n,
l),c,b;for(c=0;4>c;++c)b=v[c],a=2>c||2===c&&/%$/.test(b),v[c]=q(b,[n,l,u,v[2]][c])+(a?t:0);v[3]>v[2]&&(v[3]=v[2]);return v},getStartAndEndRadians:function(a,l){a=H(a)?a:0;l=H(l)&&l>a&&360>l-a?l:a+360;return{start:B*(a+-90),end:B*(l+-90)}}}})(K);(function(a){var B=a.addEvent,H=a.CenteredSeriesMixin,E=a.defined,q=a.each,f=a.extend,l=H.getStartAndEndRadians,t=a.inArray,n=a.noop,v=a.pick,u=a.Point,c=a.Series,b=a.seriesType,m=a.setAnimation;b("pie","line",{center:[null,null],clip:!1,colorByPoint:!0,dataLabels:{distance:30,
enabled:!0,formatter:function(){return this.point.isNull?void 0:this.point.name},x:0},ignoreHiddenPoint:!0,legendType:"point",marker:null,size:null,showInLegend:!1,slicedOffset:10,stickyTracking:!1,tooltip:{followPointer:!0},borderColor:"#ffffff",borderWidth:1,states:{hover:{brightness:.1}}},{isCartesian:!1,requireSorting:!1,directTouch:!0,noSharedTooltip:!0,trackerGroups:["group","dataLabelsGroup"],axisTypes:[],pointAttribs:a.seriesTypes.column.prototype.pointAttribs,animate:function(a){var b=this,
c=b.points,d=b.startAngleRad;a||(q(c,function(a){var c=a.graphic,e=a.shapeArgs;c&&(c.attr({r:a.startR||b.center[3]/2,start:d,end:d}),c.animate({r:e.r,start:e.start,end:e.end},b.options.animation))}),b.animate=null)},updateTotals:function(){var a,b=0,c=this.points,e=c.length,f,l=this.options.ignoreHiddenPoint;for(a=0;a<e;a++)f=c[a],b+=l&&!f.visible?0:f.isNull?0:f.y;this.total=b;for(a=0;a<e;a++)f=c[a],f.percentage=0<b&&(f.visible||!l)?f.y/b*100:0,f.total=b},generatePoints:function(){c.prototype.generatePoints.call(this);
this.updateTotals()},translate:function(a){this.generatePoints();var b=0,c=this.options,e=c.slicedOffset,d=e+(c.borderWidth||0),f,m,n,q=l(c.startAngle,c.endAngle),t=this.startAngleRad=q.start,q=(this.endAngleRad=q.end)-t,u=this.points,x,F=c.dataLabels.distance,c=c.ignoreHiddenPoint,A,B=u.length,G;a||(this.center=a=this.getCenter());this.getX=function(b,c,e){n=Math.asin(Math.min((b-a[1])/(a[2]/2+e.labelDistance),1));return a[0]+(c?-1:1)*Math.cos(n)*(a[2]/2+e.labelDistance)};for(A=0;A<B;A++){G=u[A];
G.labelDistance=v(G.options.dataLabels&&G.options.dataLabels.distance,F);this.maxLabelDistance=Math.max(this.maxLabelDistance||0,G.labelDistance);f=t+b*q;if(!c||G.visible)b+=G.percentage/100;m=t+b*q;G.shapeType="arc";G.shapeArgs={x:a[0],y:a[1],r:a[2]/2,innerR:a[3]/2,start:Math.round(1E3*f)/1E3,end:Math.round(1E3*m)/1E3};n=(m+f)/2;n>1.5*Math.PI?n-=2*Math.PI:n<-Math.PI/2&&(n+=2*Math.PI);G.slicedTranslation={translateX:Math.round(Math.cos(n)*e),translateY:Math.round(Math.sin(n)*e)};m=Math.cos(n)*a[2]/
2;x=Math.sin(n)*a[2]/2;G.tooltipPos=[a[0]+.7*m,a[1]+.7*x];G.half=n<-Math.PI/2||n>Math.PI/2?1:0;G.angle=n;f=Math.min(d,G.labelDistance/5);G.labelPos=[a[0]+m+Math.cos(n)*G.labelDistance,a[1]+x+Math.sin(n)*G.labelDistance,a[0]+m+Math.cos(n)*f,a[1]+x+Math.sin(n)*f,a[0]+m,a[1]+x,0>G.labelDistance?"center":G.half?"right":"left",n]}},drawGraph:null,drawPoints:function(){var a=this,b=a.chart.renderer,c,e,l,m,n=a.options.shadow;n&&!a.shadowGroup&&(a.shadowGroup=b.g("shadow").add(a.group));q(a.points,function(d){e=
d.graphic;if(d.isNull)e&&(d.graphic=e.destroy());else{m=d.shapeArgs;c=d.getTranslate();var h=d.shadowGroup;n&&!h&&(h=d.shadowGroup=b.g("shadow").add(a.shadowGroup));h&&h.attr(c);l=a.pointAttribs(d,d.selected&&"select");e?e.setRadialReference(a.center).attr(l).animate(f(m,c)):(d.graphic=e=b[d.shapeType](m).setRadialReference(a.center).attr(c).add(a.group),d.visible||e.attr({visibility:"hidden"}),e.attr(l).attr({"stroke-linejoin":"round"}).shadow(n,h));e.addClass(d.getClassName())}})},searchPoint:n,
sortByAngle:function(a,b){a.sort(function(a,c){return void 0!==a.angle&&(c.angle-a.angle)*b})},drawLegendSymbol:a.LegendSymbolMixin.drawRectangle,getCenter:H.getCenter,getSymbol:n},{init:function(){u.prototype.init.apply(this,arguments);var a=this,b;a.name=v(a.name,"Slice");b=function(b){a.slice("select"===b.type)};B(a,"select",b);B(a,"unselect",b);return a},isValid:function(){return a.isNumber(this.y,!0)&&0<=this.y},setVisible:function(a,b){var c=this,e=c.series,d=e.chart,h=e.options.ignoreHiddenPoint;
b=v(b,h);a!==c.visible&&(c.visible=c.options.visible=a=void 0===a?!c.visible:a,e.options.data[t(c,e.data)]=c.options,q(["graphic","dataLabel","connector","shadowGroup"],function(b){if(c[b])c[b][a?"show":"hide"](!0)}),c.legendItem&&d.legend.colorizeItem(c,a),a||"hover"!==c.state||c.setState(""),h&&(e.isDirty=!0),b&&d.redraw())},slice:function(a,b,c){var e=this.series;m(c,e.chart);v(b,!0);this.sliced=this.options.sliced=E(a)?a:!this.sliced;e.options.data[t(this,e.data)]=this.options;this.graphic.animate(this.getTranslate());
this.shadowGroup&&this.shadowGroup.animate(this.getTranslate())},getTranslate:function(){return this.sliced?this.slicedTranslation:{translateX:0,translateY:0}},haloPath:function(a){var b=this.shapeArgs;return this.sliced||!this.visible?[]:this.series.chart.renderer.symbols.arc(b.x,b.y,b.r+a,b.r+a,{innerR:this.shapeArgs.r-1,start:b.start,end:b.end})}})})(K);(function(a){var B=a.addEvent,H=a.arrayMax,E=a.defined,q=a.each,f=a.extend,l=a.format,t=a.map,n=a.merge,v=a.noop,u=a.pick,c=a.relativeLength,b=
a.Series,m=a.seriesTypes,d=a.stableSort;a.distribute=function(a,b){function c(a,b){return a.target-b.target}var f,h=!0,k=a,l=[],m;m=0;for(f=a.length;f--;)m+=a[f].size;if(m>b){d(a,function(a,b){return(b.rank||0)-(a.rank||0)});for(m=f=0;m<=b;)m+=a[f].size,f++;l=a.splice(f-1,a.length)}d(a,c);for(a=t(a,function(a){return{size:a.size,targets:[a.target],align:u(a.align,.5)}});h;){for(f=a.length;f--;)h=a[f],m=(Math.min.apply(0,h.targets)+Math.max.apply(0,h.targets))/2,h.pos=Math.min(Math.max(0,m-h.size*
h.align),b-h.size);f=a.length;for(h=!1;f--;)0<f&&a[f-1].pos+a[f-1].size>a[f].pos&&(a[f-1].size+=a[f].size,a[f-1].targets=a[f-1].targets.concat(a[f].targets),a[f-1].align=.5,a[f-1].pos+a[f-1].size>b&&(a[f-1].pos=b-a[f-1].size),a.splice(f,1),h=!0)}f=0;q(a,function(a){var b=0;q(a.targets,function(){k[f].pos=a.pos+b;b+=k[f].size;f++})});k.push.apply(k,l);d(k,c)};b.prototype.drawDataLabels=function(){function b(a,b){var c=b.filter;return c?(b=c.operator,a=a[c.property],c=c.value,"\x3e"===b&&a>c||"\x3c"===
b&&a<c||"\x3e\x3d"===b&&a>=c||"\x3c\x3d"===b&&a<=c||"\x3d\x3d"===b&&a==c||"\x3d\x3d\x3d"===b&&a===c?!0:!1):!0}var c=this,e=c.chart,d=c.options,f=d.dataLabels,m=c.points,z,t,v=c.hasRendered||0,C,x,F=u(f.defer,!!d.animation),A=e.renderer;if(f.enabled||c._hasPointLabels)c.dlProcessOptions&&c.dlProcessOptions(f),x=c.plotGroup("dataLabelsGroup","data-labels",F&&!v?"hidden":"visible",f.zIndex||6),F&&(x.attr({opacity:+v}),v||B(c,"afterAnimate",function(){c.visible&&x.show(!0);x[d.animation?"animate":"attr"]({opacity:1},
{duration:200})})),t=f,q(m,function(h){var k,g=h.dataLabel,m,p,q=h.connector,r=!g,v;z=h.dlOptions||h.options&&h.options.dataLabels;(k=u(z&&z.enabled,t.enabled)&&!h.isNull)&&(k=!0===b(h,z||f));k&&(f=n(t,z),m=h.getLabelConfig(),v=f[h.formatPrefix+"Format"]||f.format,C=E(v)?l(v,m,e.time):(f[h.formatPrefix+"Formatter"]||f.formatter).call(m,f),v=f.style,m=f.rotation,v.color=u(f.color,v.color,c.color,"#000000"),"contrast"===v.color&&(h.contrastColor=A.getContrast(h.color||c.color),v.color=f.inside||0>u(h.labelDistance,
f.distance)||d.stacking?h.contrastColor:"#000000"),d.cursor&&(v.cursor=d.cursor),p={fill:f.backgroundColor,stroke:f.borderColor,"stroke-width":f.borderWidth,r:f.borderRadius||0,rotation:m,padding:f.padding,zIndex:1},a.objectEach(p,function(a,b){void 0===a&&delete p[b]}));!g||k&&E(C)?k&&E(C)&&(g?p.text=C:(g=h.dataLabel=m?A.text(C,0,-9999).addClass("highcharts-data-label"):A.label(C,0,-9999,f.shape,null,null,f.useHTML,null,"data-label"),g.addClass(" highcharts-data-label-color-"+h.colorIndex+" "+(f.className||
"")+(f.useHTML?"highcharts-tracker":""))),g.attr(p),g.css(v).shadow(f.shadow),g.added||g.add(x),c.alignDataLabel(h,g,f,null,r)):(h.dataLabel=g=g.destroy(),q&&(h.connector=q.destroy()))});a.fireEvent(this,"afterDrawDataLabels")};b.prototype.alignDataLabel=function(a,b,c,d,l){var e=this.chart,h=e.inverted,k=u(a.dlBox&&a.dlBox.centerX,a.plotX,-9999),m=u(a.plotY,-9999),n=b.getBBox(),p,q=c.rotation,r=c.align,t=this.visible&&(a.series.forceDL||e.isInsidePlot(k,Math.round(m),h)||d&&e.isInsidePlot(k,h?d.x+
1:d.y+d.height-1,h)),v="justify"===u(c.overflow,"justify");if(t&&(p=c.style.fontSize,p=e.renderer.fontMetrics(p,b).b,d=f({x:h?this.yAxis.len-m:k,y:Math.round(h?this.xAxis.len-k:m),width:0,height:0},d),f(c,{width:n.width,height:n.height}),q?(v=!1,k=e.renderer.rotCorr(p,q),k={x:d.x+c.x+d.width/2+k.x,y:d.y+c.y+{top:0,middle:.5,bottom:1}[c.verticalAlign]*d.height},b[l?"attr":"animate"](k).attr({align:r}),m=(q+720)%360,m=180<m&&360>m,"left"===r?k.y-=m?n.height:0:"center"===r?(k.x-=n.width/2,k.y-=n.height/
2):"right"===r&&(k.x-=n.width,k.y-=m?0:n.height)):(b.align(c,null,d),k=b.alignAttr),v?a.isLabelJustified=this.justifyDataLabel(b,c,k,n,d,l):u(c.crop,!0)&&(t=e.isInsidePlot(k.x,k.y)&&e.isInsidePlot(k.x+n.width,k.y+n.height)),c.shape&&!q))b[l?"attr":"animate"]({anchorX:h?e.plotWidth-a.plotY:a.plotX,anchorY:h?e.plotHeight-a.plotX:a.plotY});t||(b.attr({y:-9999}),b.placed=!1)};b.prototype.justifyDataLabel=function(a,b,c,d,f,l){var e=this.chart,h=b.align,k=b.verticalAlign,m,n,p=a.box?0:a.padding||0;m=c.x+
p;0>m&&("right"===h?b.align="left":b.x=-m,n=!0);m=c.x+d.width-p;m>e.plotWidth&&("left"===h?b.align="right":b.x=e.plotWidth-m,n=!0);m=c.y+p;0>m&&("bottom"===k?b.verticalAlign="top":b.y=-m,n=!0);m=c.y+d.height-p;m>e.plotHeight&&("top"===k?b.verticalAlign="bottom":b.y=e.plotHeight-m,n=!0);n&&(a.placed=!l,a.align(b,null,f));return n};m.pie&&(m.pie.prototype.drawDataLabels=function(){var c=this,d=c.data,e,f=c.chart,l=c.options.dataLabels,m=u(l.connectorPadding,10),n=u(l.connectorWidth,1),t=f.plotWidth,
v=f.plotHeight,C,x=c.center,F=x[2]/2,A=x[1],B,G,g,w,L=[[],[]],P,N,O,K,y=[0,0,0,0];c.visible&&(l.enabled||c._hasPointLabels)&&(q(d,function(a){a.dataLabel&&a.visible&&a.dataLabel.shortened&&(a.dataLabel.attr({width:"auto"}).css({width:"auto",textOverflow:"clip"}),a.dataLabel.shortened=!1)}),b.prototype.drawDataLabels.apply(c),q(d,function(a){a.dataLabel&&a.visible&&(L[a.half].push(a),a.dataLabel._pos=null)}),q(L,function(b,d){var h,k,n=b.length,p=[],r;if(n)for(c.sortByAngle(b,d-.5),0<c.maxLabelDistance&&
(h=Math.max(0,A-F-c.maxLabelDistance),k=Math.min(A+F+c.maxLabelDistance,f.plotHeight),q(b,function(a){0<a.labelDistance&&a.dataLabel&&(a.top=Math.max(0,A-F-a.labelDistance),a.bottom=Math.min(A+F+a.labelDistance,f.plotHeight),r=a.dataLabel.getBBox().height||21,a.positionsIndex=p.push({target:a.labelPos[1]-a.top+r/2,size:r,rank:a.y})-1)}),a.distribute(p,k+r-h)),K=0;K<n;K++)e=b[K],k=e.positionsIndex,g=e.labelPos,B=e.dataLabel,O=!1===e.visible?"hidden":"inherit",N=h=g[1],p&&E(p[k])&&(void 0===p[k].pos?
O="hidden":(w=p[k].size,N=e.top+p[k].pos)),delete e.positionIndex,P=l.justify?x[0]+(d?-1:1)*(F+e.labelDistance):c.getX(N<e.top+2||N>e.bottom-2?h:N,d,e),B._attr={visibility:O,align:g[6]},B._pos={x:P+l.x+({left:m,right:-m}[g[6]]||0),y:N+l.y-10},g.x=P,g.y=N,u(l.crop,!0)&&(G=B.getBBox().width,h=null,P-G<m?(h=Math.round(G-P+m),y[3]=Math.max(h,y[3])):P+G>t-m&&(h=Math.round(P+G-t+m),y[1]=Math.max(h,y[1])),0>N-w/2?y[0]=Math.max(Math.round(-N+w/2),y[0]):N+w/2>v&&(y[2]=Math.max(Math.round(N+w/2-v),y[2])),B.sideOverflow=
h)}),0===H(y)||this.verifyDataLabelOverflow(y))&&(this.placeDataLabels(),n&&q(this.points,function(a){var b;C=a.connector;if((B=a.dataLabel)&&B._pos&&a.visible&&0<a.labelDistance){O=B._attr.visibility;if(b=!C)a.connector=C=f.renderer.path().addClass("highcharts-data-label-connector  highcharts-color-"+a.colorIndex).add(c.dataLabelsGroup),C.attr({"stroke-width":n,stroke:l.connectorColor||a.color||"#666666"});C[b?"attr":"animate"]({d:c.connectorPath(a.labelPos)});C.attr("visibility",O)}else C&&(a.connector=
C.destroy())}))},m.pie.prototype.connectorPath=function(a){var b=a.x,c=a.y;return u(this.options.dataLabels.softConnector,!0)?["M",b+("left"===a[6]?5:-5),c,"C",b,c,2*a[2]-a[4],2*a[3]-a[5],a[2],a[3],"L",a[4],a[5]]:["M",b+("left"===a[6]?5:-5),c,"L",a[2],a[3],"L",a[4],a[5]]},m.pie.prototype.placeDataLabels=function(){q(this.points,function(a){var b=a.dataLabel;b&&a.visible&&((a=b._pos)?(b.sideOverflow&&(b._attr.width=b.getBBox().width-b.sideOverflow,b.css({width:b._attr.width+"px",textOverflow:"ellipsis"}),
b.shortened=!0),b.attr(b._attr),b[b.moved?"animate":"attr"](a),b.moved=!0):b&&b.attr({y:-9999}))},this)},m.pie.prototype.alignDataLabel=v,m.pie.prototype.verifyDataLabelOverflow=function(a){var b=this.center,e=this.options,d=e.center,f=e.minSize||80,h,l=null!==e.size;l||(null!==d[0]?h=Math.max(b[2]-Math.max(a[1],a[3]),f):(h=Math.max(b[2]-a[1]-a[3],f),b[0]+=(a[3]-a[1])/2),null!==d[1]?h=Math.max(Math.min(h,b[2]-Math.max(a[0],a[2])),f):(h=Math.max(Math.min(h,b[2]-a[0]-a[2]),f),b[1]+=(a[0]-a[2])/2),h<
b[2]?(b[2]=h,b[3]=Math.min(c(e.innerSize||0,h),h),this.translate(b),this.drawDataLabels&&this.drawDataLabels()):l=!0);return l});m.column&&(m.column.prototype.alignDataLabel=function(a,c,d,f,l){var e=this.chart.inverted,h=a.series,k=a.dlBox||a.shapeArgs,m=u(a.below,a.plotY>u(this.translatedThreshold,h.yAxis.len)),p=u(d.inside,!!this.options.stacking);k&&(f=n(k),0>f.y&&(f.height+=f.y,f.y=0),k=f.y+f.height-h.yAxis.len,0<k&&(f.height-=k),e&&(f={x:h.yAxis.len-f.y-f.height,y:h.xAxis.len-f.x-f.width,width:f.height,
height:f.width}),p||(e?(f.x+=m?0:f.width,f.width=0):(f.y+=m?f.height:0,f.height=0)));d.align=u(d.align,!e||p?"center":m?"right":"left");d.verticalAlign=u(d.verticalAlign,e||p?"middle":m?"top":"bottom");b.prototype.alignDataLabel.call(this,a,c,d,f,l);a.isLabelJustified&&a.contrastColor&&a.dataLabel.css({color:a.contrastColor})})})(K);(function(a){var B=a.Chart,H=a.each,E=a.objectEach,q=a.pick;a=a.addEvent;a(B.prototype,"render",function(){var a=[];H(this.labelCollectors||[],function(f){a=a.concat(f())});
H(this.yAxis||[],function(f){f.options.stackLabels&&!f.options.stackLabels.allowOverlap&&E(f.stacks,function(f){E(f,function(f){a.push(f.label)})})});H(this.series||[],function(f){var l=f.options.dataLabels,n=f.dataLabelCollections||["dataLabel"];(l.enabled||f._hasPointLabels)&&!l.allowOverlap&&f.visible&&H(n,function(l){H(f.points,function(f){f[l]&&(f[l].labelrank=q(f.labelrank,f.shapeArgs&&f.shapeArgs.height),a.push(f[l]))})})});this.hideOverlappingLabels(a)});B.prototype.hideOverlappingLabels=
function(a){var f=a.length,q,n,v,u,c,b,m,d,h,k=function(a,b,c,d,f,h,k,l){return!(f>a+c||f+k<a||h>b+d||h+l<b)};for(n=0;n<f;n++)if(q=a[n])q.oldOpacity=q.opacity,q.newOpacity=1,q.width||(v=q.getBBox(),q.width=v.width,q.height=v.height);a.sort(function(a,b){return(b.labelrank||0)-(a.labelrank||0)});for(n=0;n<f;n++)for(v=a[n],q=n+1;q<f;++q)if(u=a[q],v&&u&&v!==u&&v.placed&&u.placed&&0!==v.newOpacity&&0!==u.newOpacity&&(c=v.alignAttr,b=u.alignAttr,m=v.parentGroup,d=u.parentGroup,h=2*(v.box?0:v.padding||
0),c=k(c.x+m.translateX,c.y+m.translateY,v.width-h,v.height-h,b.x+d.translateX,b.y+d.translateY,u.width-h,u.height-h)))(v.labelrank<u.labelrank?v:u).newOpacity=0;H(a,function(a){var b,c;a&&(c=a.newOpacity,a.oldOpacity!==c&&a.placed&&(c?a.show(!0):b=function(){a.hide()},a.alignAttr.opacity=c,a[a.isOld?"animate":"attr"](a.alignAttr,null,b)),a.isOld=!0)})}})(K);(function(a){var B=a.addEvent,H=a.Chart,E=a.createElement,q=a.css,f=a.defaultOptions,l=a.defaultPlotOptions,t=a.each,n=a.extend,v=a.fireEvent,
u=a.hasTouch,c=a.inArray,b=a.isObject,m=a.Legend,d=a.merge,h=a.pick,k=a.Point,e=a.Series,p=a.seriesTypes,r=a.svg,I;I=a.TrackerMixin={drawTrackerPoint:function(){var a=this,b=a.chart.pointer,c=function(a){var c=b.getPointFromEvent(a);void 0!==c&&(b.isDirectTouch=!0,c.onMouseOver(a))};t(a.points,function(a){a.graphic&&(a.graphic.element.point=a);a.dataLabel&&(a.dataLabel.div?a.dataLabel.div.point=a:a.dataLabel.element.point=a)});a._hasTracking||(t(a.trackerGroups,function(d){if(a[d]){a[d].addClass("highcharts-tracker").on("mouseover",
c).on("mouseout",function(a){b.onTrackerMouseOut(a)});if(u)a[d].on("touchstart",c);a.options.cursor&&a[d].css(q).css({cursor:a.options.cursor})}}),a._hasTracking=!0);v(this,"afterDrawTracker")},drawTrackerGraph:function(){var a=this,b=a.options,c=b.trackByArea,d=[].concat(c?a.areaPath:a.graphPath),e=d.length,f=a.chart,h=f.pointer,k=f.renderer,l=f.options.tooltip.snap,g=a.tracker,m,n=function(){if(f.hoverSeries!==a)a.onMouseOver()},p="rgba(192,192,192,"+(r?.0001:.002)+")";if(e&&!c)for(m=e+1;m--;)"M"===
d[m]&&d.splice(m+1,0,d[m+1]-l,d[m+2],"L"),(m&&"M"===d[m]||m===e)&&d.splice(m,0,"L",d[m-2]+l,d[m-1]);g?g.attr({d:d}):a.graph&&(a.tracker=k.path(d).attr({"stroke-linejoin":"round",visibility:a.visible?"visible":"hidden",stroke:p,fill:c?p:"none","stroke-width":a.graph.strokeWidth()+(c?0:2*l),zIndex:2}).add(a.group),t([a.tracker,a.markerGroup],function(a){a.addClass("highcharts-tracker").on("mouseover",n).on("mouseout",function(a){h.onTrackerMouseOut(a)});b.cursor&&a.css({cursor:b.cursor});if(u)a.on("touchstart",
n)}));v(this,"afterDrawTracker")}};p.column&&(p.column.prototype.drawTracker=I.drawTrackerPoint);p.pie&&(p.pie.prototype.drawTracker=I.drawTrackerPoint);p.scatter&&(p.scatter.prototype.drawTracker=I.drawTrackerPoint);n(m.prototype,{setItemEvents:function(a,b,c){var e=this,f=e.chart.renderer.boxWrapper,h="highcharts-legend-"+(a instanceof k?"point":"series")+"-active";(c?b:a.legendGroup).on("mouseover",function(){a.setState("hover");f.addClass(h);b.css(e.options.itemHoverStyle)}).on("mouseout",function(){b.css(d(a.visible?
e.itemStyle:e.itemHiddenStyle));f.removeClass(h);a.setState()}).on("click",function(b){var c=function(){a.setVisible&&a.setVisible()};f.removeClass(h);b={browserEvent:b};a.firePointEvent?a.firePointEvent("legendItemClick",b,c):v(a,"legendItemClick",b,c)})},createCheckboxForItem:function(a){a.checkbox=E("input",{type:"checkbox",checked:a.selected,defaultChecked:a.selected},this.options.itemCheckboxStyle,this.chart.container);B(a.checkbox,"click",function(b){v(a.series||a,"checkboxClick",{checked:b.target.checked,
item:a},function(){a.select()})})}});f.legend.itemStyle.cursor="pointer";n(H.prototype,{showResetZoom:function(){function a(){b.zoomOut()}var b=this,c=f.lang,d=b.options.chart.resetZoomButton,e=d.theme,h=e.states,k="chart"===d.relativeTo?null:"plotBox";v(this,"beforeShowResetZoom",null,function(){b.resetZoomButton=b.renderer.button(c.resetZoom,null,null,a,e,h&&h.hover).attr({align:d.position.align,title:c.resetZoomTitle}).addClass("highcharts-reset-zoom").add().align(d.position,!1,k)})},zoomOut:function(){var a=
this;v(a,"selection",{resetSelection:!0},function(){a.zoom()})},zoom:function(a){var c,d=this.pointer,e=!1,f;!a||a.resetSelection?(t(this.axes,function(a){c=a.zoom()}),d.initiated=!1):t(a.xAxis.concat(a.yAxis),function(a){var b=a.axis;d[b.isXAxis?"zoomX":"zoomY"]&&(c=b.zoom(a.min,a.max),b.displayBtn&&(e=!0))});f=this.resetZoomButton;e&&!f?this.showResetZoom():!e&&b(f)&&(this.resetZoomButton=f.destroy());c&&this.redraw(h(this.options.chart.animation,a&&a.animation,100>this.pointCount))},pan:function(a,
b){var c=this,d=c.hoverPoints,e;d&&t(d,function(a){a.setState()});t("xy"===b?[1,0]:[1],function(b){b=c[b?"xAxis":"yAxis"][0];var d=b.horiz,f=a[d?"chartX":"chartY"],d=d?"mouseDownX":"mouseDownY",h=c[d],g=(b.pointRange||0)/2,k=b.getExtremes(),l=b.toValue(h-f,!0)+g,m=b.toValue(h+b.len-f,!0)-g,n=m<l,h=n?m:l,l=n?l:m,m=Math.min(k.dataMin,g?k.min:b.toValue(b.toPixels(k.min)-b.minPixelPadding)),g=Math.max(k.dataMax,g?k.max:b.toValue(b.toPixels(k.max)+b.minPixelPadding)),n=m-h;0<n&&(l+=n,h=m);n=l-g;0<n&&(l=
g,h-=n);b.series.length&&h!==k.min&&l!==k.max&&(b.setExtremes(h,l,!1,!1,{trigger:"pan"}),e=!0);c[d]=f});e&&c.redraw(!1);q(c.container,{cursor:"move"})}});n(k.prototype,{select:function(a,b){var d=this,e=d.series,f=e.chart;a=h(a,!d.selected);d.firePointEvent(a?"select":"unselect",{accumulate:b},function(){d.selected=d.options.selected=a;e.options.data[c(d,e.data)]=d.options;d.setState(a&&"select");b||t(f.getSelectedPoints(),function(a){a.selected&&a!==d&&(a.selected=a.options.selected=!1,e.options.data[c(a,
e.data)]=a.options,a.setState(""),a.firePointEvent("unselect"))})})},onMouseOver:function(a){var b=this.series.chart,c=b.pointer;a=a?c.normalize(a):c.getChartCoordinatesFromPoint(this,b.inverted);c.runPointActions(a,this)},onMouseOut:function(){var a=this.series.chart;this.firePointEvent("mouseOut");t(a.hoverPoints||[],function(a){a.setState()});a.hoverPoints=a.hoverPoint=null},importEvents:function(){if(!this.hasImportedEvents){var b=this,c=d(b.series.options.point,b.options).events;b.events=c;a.objectEach(c,
function(a,c){B(b,c,a)});this.hasImportedEvents=!0}},setState:function(a,b){var c=Math.floor(this.plotX),d=this.plotY,e=this.series,f=e.options.states[a||"normal"]||{},k=l[e.type].marker&&e.options.marker,m=k&&!1===k.enabled,p=k&&k.states&&k.states[a||"normal"]||{},g=!1===p.enabled,q=e.stateMarkerGraphic,r=this.marker||{},t=e.chart,u=e.halo,z,B=k&&e.markerAttribs;a=a||"";if(!(a===this.state&&!b||this.selected&&"select"!==a||!1===f.enabled||a&&(g||m&&!1===p.enabled)||a&&r.states&&r.states[a]&&!1===
r.states[a].enabled)){B&&(z=e.markerAttribs(this,a));if(this.graphic)this.state&&this.graphic.removeClass("highcharts-point-"+this.state),a&&this.graphic.addClass("highcharts-point-"+a),this.graphic.animate(e.pointAttribs(this,a),h(t.options.chart.animation,f.animation)),z&&this.graphic.animate(z,h(t.options.chart.animation,p.animation,k.animation)),q&&q.hide();else{if(a&&p){k=r.symbol||e.symbol;q&&q.currentSymbol!==k&&(q=q.destroy());if(q)q[b?"animate":"attr"]({x:z.x,y:z.y});else k&&(e.stateMarkerGraphic=
q=t.renderer.symbol(k,z.x,z.y,z.width,z.height).add(e.markerGroup),q.currentSymbol=k);q&&q.attr(e.pointAttribs(this,a))}q&&(q[a&&t.isInsidePlot(c,d,t.inverted)?"show":"hide"](),q.element.point=this)}(c=f.halo)&&c.size?(u||(e.halo=u=t.renderer.path().add((this.graphic||q).parentGroup)),u.show()[b?"animate":"attr"]({d:this.haloPath(c.size)}),u.attr({"class":"highcharts-halo highcharts-color-"+h(this.colorIndex,e.colorIndex)}),u.point=this,u.attr(n({fill:this.color||e.color,"fill-opacity":c.opacity,
zIndex:-1},c.attributes))):u&&u.point&&u.point.haloPath&&u.animate({d:u.point.haloPath(0)},null,u.hide);this.state=a;v(this,"afterSetState")}},haloPath:function(a){return this.series.chart.renderer.symbols.circle(Math.floor(this.plotX)-a,this.plotY-a,2*a,2*a)}});n(e.prototype,{onMouseOver:function(){var a=this.chart,b=a.hoverSeries;if(b&&b!==this)b.onMouseOut();this.options.events.mouseOver&&v(this,"mouseOver");this.setState("hover");a.hoverSeries=this},onMouseOut:function(){var a=this.options,b=
this.chart,c=b.tooltip,d=b.hoverPoint;b.hoverSeries=null;if(d)d.onMouseOut();this&&a.events.mouseOut&&v(this,"mouseOut");!c||this.stickyTracking||c.shared&&!this.noSharedTooltip||c.hide();this.setState()},setState:function(a){var b=this,c=b.options,d=b.graph,e=c.states,f=c.lineWidth,c=0;a=a||"";if(b.state!==a&&(t([b.group,b.markerGroup,b.dataLabelsGroup],function(c){c&&(b.state&&c.removeClass("highcharts-series-"+b.state),a&&c.addClass("highcharts-series-"+a))}),b.state=a,!e[a]||!1!==e[a].enabled)&&
(a&&(f=e[a].lineWidth||f+(e[a].lineWidthPlus||0)),d&&!d.dashstyle))for(f={"stroke-width":f},d.animate(f,h(e[a||"normal"]&&e[a||"normal"].animation,b.chart.options.chart.animation));b["zone-graph-"+c];)b["zone-graph-"+c].attr(f),c+=1},setVisible:function(a,b){var c=this,d=c.chart,e=c.legendItem,f,h=d.options.chart.ignoreHiddenSeries,k=c.visible;f=(c.visible=a=c.options.visible=c.userOptions.visible=void 0===a?!k:a)?"show":"hide";t(["group","dataLabelsGroup","markerGroup","tracker","tt"],function(a){if(c[a])c[a][f]()});
if(d.hoverSeries===c||(d.hoverPoint&&d.hoverPoint.series)===c)c.onMouseOut();e&&d.legend.colorizeItem(c,a);c.isDirty=!0;c.options.stacking&&t(d.series,function(a){a.options.stacking&&a.visible&&(a.isDirty=!0)});t(c.linkedSeries,function(b){b.setVisible(a,!1)});h&&(d.isDirtyBox=!0);!1!==b&&d.redraw();v(c,f)},show:function(){this.setVisible(!0)},hide:function(){this.setVisible(!1)},select:function(a){this.selected=a=void 0===a?!this.selected:a;this.checkbox&&(this.checkbox.checked=a);v(this,a?"select":
"unselect")},drawTracker:I.drawTrackerGraph})})(K);(function(a){var B=a.Chart,H=a.each,E=a.inArray,q=a.isArray,f=a.isObject,l=a.pick,t=a.splat;B.prototype.setResponsive=function(f){var l=this.options.responsive,n=[],c=this.currentResponsive;l&&l.rules&&H(l.rules,function(b){void 0===b._id&&(b._id=a.uniqueKey());this.matchResponsiveRule(b,n,f)},this);var b=a.merge.apply(0,a.map(n,function(b){return a.find(l.rules,function(a){return a._id===b}).chartOptions})),n=n.toString()||void 0;n!==(c&&c.ruleIds)&&
(c&&this.update(c.undoOptions,f),n?(this.currentResponsive={ruleIds:n,mergedOptions:b,undoOptions:this.currentOptions(b)},this.update(b,f)):this.currentResponsive=void 0)};B.prototype.matchResponsiveRule=function(a,f){var n=a.condition;(n.callback||function(){return this.chartWidth<=l(n.maxWidth,Number.MAX_VALUE)&&this.chartHeight<=l(n.maxHeight,Number.MAX_VALUE)&&this.chartWidth>=l(n.minWidth,0)&&this.chartHeight>=l(n.minHeight,0)}).call(this)&&f.push(a._id)};B.prototype.currentOptions=function(l){function n(c,
b,l,d){var h;a.objectEach(c,function(a,c){if(!d&&-1<E(c,["series","xAxis","yAxis"]))for(a=t(a),l[c]=[],h=0;h<a.length;h++)b[c][h]&&(l[c][h]={},n(a[h],b[c][h],l[c][h],d+1));else f(a)?(l[c]=q(a)?[]:{},n(a,b[c]||{},l[c],d+1)):l[c]=b[c]||null})}var u={};n(l,this.options,u,0);return u}})(K);return K});
;Type.registerNamespace("IC.Public");
Type.registerNamespace("IC.Controls");
Type.registerNamespace("IC.Public.Mobile");
Type.registerNamespace("IC.Public.Plans");
Type.registerNamespace("IC.Public.Exercise");
Type.registerNamespace("IC.Public.Plans.Common");
Type.registerNamespace("IC.Public.Offers");;//*******************************************************************************************************
//  
//  File:        common.js
//  Author:      Vladimir Nechypurenko
//  Description: Common jquery functions
//               Please, do not change this file unless it will be updated in OC.Project.Template 
//               It can be re-used in different projects
//  Review:      28/09/2010 Vladimir Nechypurenko
//               - add useful functions from Shares project 
//               21/10/2010 Vladimir Nechypurenko
//               - add history registration
//               28/10/2010 Vladimir Nechypurenko
//               - add window hashchange trigger
//               03/11/2010 Vladimir Nechypurenko
//               - add function removeSpecialSymb, which removes the special symbols in the string 
//
//*******************************************************************************************************
/// <reference path="jQuery/jquery-1.4.4.js" />

Type.registerNamespace("OC.MVC");

OC.MVC.JSON = {
    ToUrl: function(obj) {
        var u = [];
        if (typeof(obj) == "object") {
            for (x in obj) {
                if (obj[x] instanceof Array)
                    u.push(x + "=" + encodeURI(obj[x].join(",")));
                else if (obj[x] instanceof Object)
                    u.push(OC.MVC.JSON.ToUrl(obj[x])); //u.push(JSON.ToURL(obj[x]));
                else
                    u.push(x + "=" + encodeURI(obj[x]));
            }
        }
        return u.join("&");
    }
};

$(document).ajaxSend(function (event, xhr, settings) {
    if (settings.type != 'GET') {
        var token = $('input[name=__RequestVerificationToken]').val();
        if (!(settings.contentType == "application/json; charset=utf-8")) {
            if (!(settings.data != undefined && settings.data.constructor != undefined && settings.data.constructor.name == "FormData")) {
                if (!(settings.data == "[object FormData]")) {
                    settings.data = settings.data == undefined ? settings.data = "" : settings.data;
                    settings.data = token != undefined ? settings.data.indexOf('__RequestVerificationToken') > -1 ? settings.data : settings.data += "&__RequestVerificationToken=" + token : settings.data;
                    settings.contentType = 'application/x-www-form-urlencoded; charset=utf-8';
                }
            }
        }
        xhr.setRequestHeader("__RequestVerificationToken", token);
    }
});

$(document).ajaxComplete(function (event, xhr, settings) {
    if ((xhr.responseJSON != null && xhr.responseJSON.message != null && xhr.responseJSON.message == "Concurrent_Login") || (xhr.responseText != null && xhr.responseText.toString().indexOf("Concurrent_Login") > 0))
        window.location.href = "/Login/Login";    
});
OC.MVC.constants = {
    virtualFolder: "",
    mainContainer: "mainContent",
    mainBody: "mainBody",
    iis6: false,
    loginUrl: "/Login/Login",
    genericView: "/General/RenderView",
    errorContainer: "mainErrorDiv",
    gaTrackingCode: typeof(gaTrackingCode) == "string" ? gaTrackingCode : "UA-XXXXXXX-YY"
};

OC.MVC.ga = {
    level: { visitor: 1, session: 2, page: 3 },
    trackPageview: function(url) {
        if (typeof(_gat) == 'object') {
            var pageTracker = _gat._getTracker(OC.MVC.constants.gaTrackingCode);
            if (pageTracker) {
                if (url) {
                    pageTracker._trackPageview(url);
                } else {
                    pageTracker._trackPageview();
                }
            }
        }
    },
    setCustomVariable: function(index, name, value, level) {
        if (typeof(_gat) == 'object') {
            var pageTracker = _gat._getTracker(OC.MVC.constants.gaTrackingCode);
            if (pageTracker && typeof(_gaq) == 'object') {
                _gaq.push(['_setCustomVar', index, name, value, level]);
            }
        }
    }
};

OC.MVC.events = {
    beforeLoadHtml: null
};

OC.MVC.events.beforeLoadHtml = function (html) {
    var div = $('<div>');
    div.html(html);
    if (div.find("#loginContent").length > 0) {
        setTimeout(function () {
            window.location = "/";
        }, 486);
        return false;
    }
    return true;
};

OC.MVC.util = {
    ///
    /// function loads the specified view into OC.MVC.constants.mainContainer, using a history repository
    ///   Parameters:
    ///   - viewName - view URL, which should be loaded
    ///   - data - data object, which can be sent to view URL controller as input parameter
    ///
    loadMainContainerView: function(viewName, data) {
        if (!this.isLoading) {
            OC.MVC.history.register(OC.MVC.constants.mainContainer, viewName, data);
            this.runView(viewName, data);
        }
    },
    ///
    /// function loads the specified view into container, using a history repository
    ///   Parameters:
    ///   - containerName - id of DIV, which HTML will be updated with view HTML
    ///   - viewName - view URL, which should be loaded
    ///   - data - data object, which can be sent to view URL controller as input parameter
    ///
    loadView: function(containerName, viewName, data) {
        if (!this.isLoading) {
            OC.MVC.history.register(containerName, viewName, data);
            this.runView(viewName, data);
        }
    },
    runView: function(viewName, data) {
        try {
            pageTracker._trackPageview(viewName);
        } catch(err) {
        }
        var history = OC.MVC.history;
        //var urlData = OC.MVC.JSON.ToUrl(data);
        var hash = viewName;
        history.go(hash, data);
    },
    loadHtml: function(containerName, html) {
        var container = $('#' + containerName);
        if ($.browser.msie && $.browser.version == '6.0') {
            // IE 6 is just too crap - grrrr!
            container.html(html);
            IC.Public.hideLoading();
        } else if ($.browser.msie) { // Fix fade effect in IE + load the HTML into Container
            var _mcf = $("#" + containerName + "Fader");
            if (_mcf.length == 0) {
                _mcf = $('#mainContentFader');
            }
            if (_mcf.length == 0) {
                container.html(html);
                IC.Public.hideLoading();
            } else {
                var _position = container.offset();
                if (_position) {
                    _mcf.css("top", _position.top);
                    _mcf.css("left", _position.left);
                    _mcf.css("width", container.width());
                    _mcf.css("height", container.height());
                    _mcf.fadeIn(200, function() {
                        container.css('visibility', 'hidden');
                        container.html(html);
                        _mcf.css("width", container.width());
                        if ($.browser.version == '6.0') {
                            _mcf.css("height", container.height());
                        } else {
                            _mcf.css("height", $(document).height() - 200);
                        }
                        container.css('visibility', 'visible');
                        _mcf.fadeOut(400);
                        IC.Public.hideLoading();
                    });
                }
                ;
            }
        } else { //Fade effect in all other browsers + load the HTML into Container
            container.fadeOut(200, function() {
                container.html(html);
                container.fadeIn(400);
                IC.Public.hideLoading();
            });
        }
        OC.MVC.history.isLoading = false;
    },
    ///
    /// function loads the specified view into container, without using a history repository
    ///   Parameters:
    ///   - containerName - id of DIV, which HTML will be updated with view HTML
    ///   - viewName - view URL, which should be loaded
    ///   - data - data object, which can be sent to view URL controller as input parameter
    ///
    _loadView: function (containerName, viewName, data) {
        if (viewName) {
            if (viewName == '') {
                $('#' + containerName).html('<span>No view page defined for specified action</span>');
            } else {
                
                //Note: Scripts load multiple times when using load method i.e. $('#' + containerName).load(viewName, data);
                var container = $("#" + containerName);
                if (container.length > 0) { // If container is found, load URL
                    IC.Public.showLoading("");
                    $.ajax({
                        url: viewName,
                        data: data,
                        success: function(result) {
                            OC.MVC.util.loadHtml(containerName, result);
                            IC.Public.registerGA(viewName, { viewKey: $(result).find("[name=ViewKey]").val() });
                        },
                        error: function(err) {
                            OC.MVC.util.errorMessage(err.responseText);
                            IC.Public.hideLoading();
                        }
                    });
                } else {
                    // If container is not found, load DefaultTemplate container and then load the URL
                    // It happens, when the page was loaded without OC.MVC.constants.mainContainer (like Public pages)
                    var url = OC.MVC.util.getLink("OpenAccess", "TemplatePage");
                    IC.Public.showLoading("");
                    $.ajax({
                        url: url,
                        success: function(result) {
                            $("#" + OC.MVC.constants.mainBody).html(result);
                            //OC.MVC.history.isLoading = false;
                            OC.MVC.util._loadView(containerName, viewName, data);
                            //IC.Public.hideLoading();
                        },
                        error: function(err) {
                            OC.MVC.util.errorMessage(err.responseText);
                            IC.Public.hideLoading();
                        }
                    });
                }

            }
        }
    },
    ///
    /// function return the valid URL for specified action, depending on host IIS version
    /// and location of Web site:
    ///   OC.MVC.constants.virtualFolder - must be specified, if Web-site is the virtual folder of other site
    ///   OC.MVC.iis6 - (true/false). Default true, adds aspx in the controller name to make it valid for IIS6 
    ///
    getLink: function(controller, action, data, virtualFolder) {
        if (controller == '' && action == '') return '';
        var _ext = OC.MVC.constants.iis6 ? '/' : '/';
        var _virtualFolder = virtualFolder || OC.MVC.constants.virtualFolder;
        if (_virtualFolder) {
            return '/' + _virtualFolder + '/' + controller + _ext + action + (data ? '?' + data : '');
        } else {
            return '/' + controller + _ext + action + (data ? '?' + data : '');
        }
    },
    getGenericLink: function(controller, action, data) {
        return OC.MVC.constants.genericView + "?actionUrl=" + OC.MVC.util.getLink(controller, action, data);
    },
    ///
    /// utility function, which can useful to get cell in jqGrid by rowObject and colName
    ///
    getCell: function(rowObject, colName) {
        var value = '';
        for (var i = 0; i < rowObject.childNodes.length; ++i) {
            if (rowObject.childNodes[i].nodeName == colName) {
                value = rowObject.childNodes[i].text || rowObject.childNodes[i].textContent;
                break;
            }
        }
        return value;
    },
    ///
    /// show/hide elements by css-class name
    ///
    showHideControls: function(className, value) {
        if (className) {
            if (className[0] != '.') className = '.' + className;
            $(className).each(function(index, item) {
                var _item = $(item);
                if (value) $(item)._show();
                else {
                    $(item)._hide();
                }
            });
        }
    },
    ///
    /// This function returns a comma delimited string of all checked checkboxes in a checkbox list
    ///
    /// checkBoxListName is the name of the checkbox list
    ///
    getAllCheckBoxCheckedValuesCommaDelimited: function(checkBoxListName) {
        var selectedItems = $('INPUT[type=checkbox][name=' + checkBoxListName + ']:checked').map(function() {
            this.value;
        }).get();
        return selectedItems;
    },
    ///
    /// This function checks the validity of an email address and returns a boolean.
    ///
    /// emailAddress is the email address string to be checked
    ///
    isEmailValid: function(emailAddress) {
        return (emailAddress.search(/^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z]+$/) != -1);
    },
    ///
    /// This function loads HTML returned from a Controller/Action call into a modal popup
    ///
    /// clickableElementSelector is the selection reference used by jQuery, eg. "A.clickMe"
    /// controller is the name of the controller
    /// action is the name of the action method in the controller
    ///
    loadContentToModal: function(clickableElementSelector, controller, action) {
        $(clickableElementSelector).click(function(e) {
            e.preventDefault();
            OC.MVC.util.showModal(controller, action);
        });
    },
    showModal: function (controller, action, data, options, callBack) {
        var navigateUrl = OC.MVC.util.getLink(controller, action);
        OC.MVC.util.showModalByUrl(navigateUrl, data, options, callBack);
    },

    showModalByUrl: function(url, data, options, callBack) {
        $.ajax({
            url: url,
            type: 'GET',
            data: data,
            success: function (result) {
                if (!OC.MVC.events.beforeLoadHtml(result))
                    return;
                
                if (result) {
                    //Replace htmlData usage with $.parseHTML after the upgrade to jquery 1.8+
                    var htmlData = $(result);
                    var modalContent = $('<div>');
                    modalContent.addClass('modalContent');
                    modalContent.html(result);
                    var config = {};
                    $.extend(config, options);

                    config.onShow = function () {
                        htmlData.filter('script').each(function () {
                            var scriptText = this.text || this.textContent || this.innerHTML || '';
                            $.globalEval(scriptText);
                        });
                        if (typeof (options.onShow) == "function")
                            options.onShow();
                        if (typeof (callBack) == 'function') {
                            callBack();
                        }
                   };

                    modalContent.modal(config);
                    if (options.hideCloseButton == true) {
                        $("a[class='modalCloseImg simplemodal-close']").hide();
                    }
                }
            },
            error: function(xmlHttpRequest, textStatus) {
                alert('Error loading content: ' + textStatus);
            }
        });
    },
    ///
    /// This function converts an RGB colour code to Hex format
    ///
    rgbToHex: function(color) {
        if (color.substr(0, 1) === '#') {
            return color;
        }
        var digits = /(.*?)rgb\((\d+), (\d+), (\d+)\)/.exec(color);

        var red = (parseInt(digits[2]) << 16);
        var green = (parseInt(digits[3]) << 8);
        var blue = parseInt(digits[4]);

        var rgb = (red | green | blue).toString(16);
        if (rgb.length < 6) {
            var noOfLeadingZeroNeeded = 6 - (rgb.length);
            for (var index = 0; index < noOfLeadingZeroNeeded; index++) {
                rgb = "0" + rgb;
            }
        }

        return digits[1] + '#' + rgb;
    },
    ///
    /// Removes special symbols from the string
    ///
    removeSpecialSymb: function(value) {
        if (typeof(value) == "string") {
            return value.replace(/[^a-zA-Z 0-9]+/g, '_').replace(/\s+/g, "_");
        } else {
            return value;
        }
    },
    getCloneOfObject: function(oldObject) {
        var tempClone = {};

        if (typeof(oldObject) == "object")
            for (prop in oldObject)
                // for array use private method getCloneOfArray
                if ((typeof(oldObject[prop]) == "object") &&
                    (oldObject[prop]).__isArray)
                    tempClone[prop] = this.getCloneOfArray(oldObject[prop]);
                    // for object make recursive call to getCloneOfObject
                else if (typeof(oldObject[prop]) == "object")
                    tempClone[prop] = this.getCloneOfObject(oldObject[prop]);
                    // normal (non-object type) members
                else
                    tempClone[prop] = oldObject[prop];

        return tempClone;
    },
    getCloneOfArray: function(oldArray) {
        var tempClone = [];

        for (var arrIndex = 0; arrIndex < oldArray.length; arrIndex++) {
            if (typeof(oldArray[arrIndex]) == "object")
                tempClone.push(this.getCloneOfObject(oldArray[arrIndex]));
            else
                tempClone.push(oldArray[arrIndex]);
        }

        return tempClone;
    },
    url_decode: function(urltext) {
        var string = "";
        var i = 0;
        var c = c1 = c2 = 0;

        while (i < urltext.length) {

            c = urltext.charCodeAt(i);

            if (c < 128) {
                string += String.fromCharCode(c);
                i++;
            } else if ((c > 191) && (c < 224)) {
                c2 = urltext.charCodeAt(i + 1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            } else {
                c2 = urltext.charCodeAt(i + 1);
                c3 = urltext.charCodeAt(i + 2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }

        }
        // Create a regular expression to search all +s in the string
        var lsRegExp = /\+/g;
        // Return the decoded string
        return unescape(string.replace(lsRegExp, " "));
    },
    errorMessage: function(errorPageHtml) {
        var div = $('<div>');
        div.html(errorPageHtml);
        var errmessage = div.find("#errorContainer");
        this.loadHtml(OC.MVC.constants.mainContainer, errmessage.html());
        OC.MVC.history.isLoading = false;
    },
    adjustModalSizeAndPosition: function () {
        $('#simplemodal-container').css('height', 'auto');
        $.modal.setPosition();
    }
};

OC.MVC.history = {
    _historyItems: null,
    _previousHash: null,
    _currentData: null,
    _history: null,
    init: function() {
        var _obj = OC.MVC.history;
        if (!_obj.isInitialized) {
            this._history = $.History;
            this._history.bind(function(state) {
                if (state == '') { //On back click the state can be empty, but we can take window.location.search
                    if (window.location.pathname == OC.MVC.constants.genericView) {
                        state = OC.MVC.util.url_decode($.History.extractHash(window.location.search.replace("?actionUrl=", "")));
                    }
                }
                OC.MVC.history.load(state, _obj._currentData);
            });
            _obj.isInitialized = true;
        }
    },
    getHistory: function() {
        return this._history;
    },
    go: function(hash, data) {
        if (LockBox && hash) {
            LockBox.set("lastUrl", hash);
            LockBox.set("lastUrlData", OC.MVC.JSON.ToUrl(data));
        }
        this._currentData = data;

        if (window.location.pathname == OC.MVC.constants.genericView) {
            IC.Public.hideLoading();
            setTimeout(function() {
                var locationUrl = window.location.pathname + "?actionURL=" + hash;
                if (data) {
                    locationUrl += "&" + OC.MVC.JSON.ToUrl(data);
                }
                window.location = locationUrl;

            }, 0);
            return;
        }
        else {
            this.getHistory().go(hash);
        }
    },
    load: function(hash, data) {
        var _obj = OC.MVC.history;
        if (!this.isLoading) {
            if (hash && hash != "undefined") {
                if (hash == "") hash = LockBox.get("lastUrl");
                if (hash != "") {
                    this.isLoading = true;
                    if (hash != "" && _obj.getHistoryItems().hasItem(hash)) {
                        var item = _obj.getHistoryItems().getItem(hash);
                        OC.MVC.util._loadView(item.container, item.url, data || item.data);
                    }
                    else {
                        OC.MVC.util._loadView(OC.MVC.constants.mainContainer, hash, data);
                    }
                }
            }
            else {
                window.location = window.location.pathname;
            }
        }
    },
    getHistoryItems: function() {
        var _obj = OC.MVC.history;
        if (!_obj._historyItems) {
            _obj._historyItems = new Hash();
        }
        return _obj._historyItems;
    },
    getHistoryItem: function(hash) {
        var _obj = OC.MVC.history;
        if (!_obj.getHistoryItems().hasItem(hash)) {
            return _obj.getHistoryItems().getItem(hash);
        }
        else {
            return null;
        }
    },
    setHistoryItem: function(hash, containerName, viewName, data) {
        var _obj = OC.MVC.history;
        var historyItem = { container: containerName, url: viewName, data: data };
        _obj.getHistoryItems().setItem(hash, historyItem);
    },
    replaceHistoryItem: function(hash, containerName, viewName, data) {
        var _obj = OC.MVC.history;
        var urlData = OC.MVC.JSON.ToUrl(data);
        var newhash = viewName + (urlData === "" ? "" : "?" + urlData);
        _obj.getHistoryItems().removeItem(hash);

        var historyItem = { container: containerName, url: viewName, data: data };
        _obj.getHistoryItems().setItem(newhash, historyItem);
    },

    register: function(containerName, viewName, data) {
        var _obj = OC.MVC.history;
        _obj.init();
        //var urlData = OC.MVC.JSON.ToUrl(data);
        var hash = viewName;
        _obj.setHistoryItem(hash, containerName, viewName, data);
    }
}

jQuery.fn.outerHTML = function () {
    return $('<div>').append(this.eq(0).clone()).html();
};

function validateTIN(textValue,taxResidence, taxIdType) {
    var numbers = /^[0-9]+$/;

    if (taxIdType == "TIN") {
        if (taxResidence == "AUS") {
            if (textValue.value.length > 9 || textValue.value.length < 8 || (textValue.value.match(numbers) == null)) {
                showPopupForInvalidTin();
            }
            if (taxResidence == "NZL") {
                showPopupForInvalidTin();
            }
        }
    }
}
function showPopupForInvalidTin() {
    $("#validateTIN").modal('show');
}

;///
/// Extending the String class
///

String.prototype.htmlEncode = function() {
    var value = this.valueOf();
    return $('<div/>').text(value).html();
};

String.prototype.htmlDecode = function() {
    var value = this.valueOf();
    return $('<div/>').html(value).text();
};

String.prototype.startsWith = function (str) {
    return (this.match("^" + str) == str);
};

String.prototype.endsWith = function (str) {
    return (this.match(str + "$") == str);
};

String.prototype.replaceAll = function (strTarget, strReplacement) {
    var strText = this;
    var intIndexOfMatch = strText.indexOf(strTarget);
    while (intIndexOfMatch != -1) {
        strText = strText.replace(strTarget, strReplacement);
        intIndexOfMatch = strText.indexOf(strTarget);
    }
    return (strText);
};

String.prototype.trim = function() {
    return this.replace( /^\s+|\s+$/g , '');
};
;//*******************************************************************************************************
//  
//  File:        hash.js
//  Author:      Vladimir Nechypurenko
//  Description: Class to provide hashed collections in javascript
//  Review:      
//
//*******************************************************************************************************

function Hash()
{
	this.length = 0;
	this.items = new Array();
	this.keys = new Array();
	for (var i = 0; i < arguments.length; i += 2) {
		if (typeof(arguments[i + 1]) != 'undefined') {
			this.items[arguments[i]] = arguments[i + 1];
			this.length++;
		}
	}

	this.removeItem = function(in_key) {
		var tmp_previous;
		if (typeof (this.items[in_key]) != 'undefined') {
			this.length--;
			var tmp_previous = this.items[in_key];
			delete this.items[in_key];
		}

		for (var i = 0; i < this.keys.length; ++i) {
			if (this.keys[i] == in_key) {
			   this.keys.splice(i, 1);
			}
		}

		return tmp_previous;
	}

	this.getItem = function(in_key) {
		return this.items[in_key];
	}

	this.setItem = function(in_key, in_value)
	{
		var tmp_previous;
		if (typeof(in_value) != 'undefined') {
			if (typeof(this.items[in_key]) == 'undefined') {
				this.length++;
			}
			else {
				tmp_previous = this.items[in_key];
			}

			this.items[in_key] = in_value;
			if (!this.hasKey(in_key))
			   this.keys.push(in_key);
		}
	   
		return tmp_previous;
	}

	this.hasKey = function(in_key) {
		for (var i = 0; i < this.keys.length; ++i) {
			if (this.keys[i] == in_key) {
			   return true;
			}
		}
		return false;
	}

	this.hasItem = function(in_key)
	{
		return typeof(this.items[in_key]) != 'undefined';
	}

	this.clear = function()
	{
		for (var i in this.items) {
			delete this.items[i];
		}
        this.keys = [];
		this.length = 0;
	}
};;//*******************************************************************************************************
//  
//  File:        exportData.js
//  Author:      Vladimir Nechypurenko
//  Description: Export jqGrid data into CSV
//  Review:      
//
//*******************************************************************************************************

function onDownloadClick(suffix) {
    var gridObj = $('.gridContainer').find('div.ui-jqgrid').last();
    if (gridObj.length > 0) {
        var gridId = gridObj.get(0).id;
        if (gridId) {
            gridId = gridId.replace("gbox_", "");
            var grid = $("#" + gridId);
            if (grid.length > 0) {
                var url = grid.getUrl();
                var data = OC.MVC.util.getCloneOfObject(grid.getPostData());
                data.ExportToCSV = true;
                if (suffix && suffix.length > 0) {
                    url = url + suffix;
                }
                document.location = url + '?' + OC.MVC.JSON.ToUrl(data);
            }
        }
    }
}
;var JSON;JSON||(JSON={}),(function(){"use strict";function i(n){return n<10?"0"+n:n}function f(n){return o.lastIndex=0,o.test(n)?'"'+n.replace(o,function(n){var t=s[n];return typeof t=="string"?t:"\\u"+("0000"+n.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+n+'"'}function r(i,e){var h,l,c,a,v=n,s,o=e[i];o&&typeof o=="object"&&typeof o.toJSON=="function"&&(o=o.toJSON(i)),typeof t=="function"&&(o=t.call(e,i,o));switch(typeof o){case"string":return f(o);case"number":return isFinite(o)?String(o):"null";case"boolean":case"null":return String(o);case"object":if(!o)return"null";n+=u,s=[];if(Object.prototype.toString.apply(o)==="[object Array]"){for(a=o.length,h=0;h<a;h+=1)s[h]=r(h,o)||"null";return c=s.length===0?"[]":n?"[\n"+n+s.join(",\n"+n)+"\n"+v+"]":"["+s.join(",")+"]",n=v,c}if(t&&typeof t=="object")for(a=t.length,h=0;h<a;h+=1)typeof t[h]=="string"&&(l=t[h],c=r(l,o),c&&s.push(f(l)+(n?": ":":")+c));else for(l in o)Object.prototype.hasOwnProperty.call(o,l)&&(c=r(l,o),c&&s.push(f(l)+(n?": ":":")+c));return c=s.length===0?"{}":n?"{\n"+n+s.join(",\n"+n)+"\n"+v+"}":"{"+s.join(",")+"}",n=v,c}}typeof Date.prototype.toJSON!="function"&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+i(this.getUTCMonth()+1)+"-"+i(this.getUTCDate())+"T"+i(this.getUTCHours())+":"+i(this.getUTCMinutes())+":"+i(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()});var e=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,o=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,n,u,s={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},t;typeof JSON.stringify!="function"&&(JSON.stringify=function(i,f,e){var o;n="",u="";if(typeof e=="number")for(o=0;o<e;o+=1)u+=" ";else typeof e=="string"&&(u=e);t=f;if(f&&typeof f!="function"&&(typeof f!="object"||typeof f.length!="number"))throw new Error("JSON.stringify");return r("",{"":i})}),typeof JSON.parse!="function"&&(JSON.parse=function(n,t){function r(n,i){var f,e,u=n[i];if(u&&typeof u=="object")for(f in u)Object.prototype.hasOwnProperty.call(u,f)&&(e=r(u,f),e!==undefined?u[f]=e:delete u[f]);return t.call(n,i,u)}var i;n=String(n),e.lastIndex=0,e.test(n)&&(n=n.replace(e,function(n){return"\\u"+("0000"+n.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(n.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return i=eval("("+n+")"),typeof t=="function"?r({"":i},""):i;throw new SyntaxError("JSON.parse");})})();//*******************************************************************************************************
//  
//  File:        oc-lockbox.js
//  Author:      Vladimir Nechypurenko
//  Description: Statis JS object to keep the values in the localStorage of the Browser
//  Review: 
//
//*******************************************************************************************************

var LockBox = {
    ensureStorage: function() {
        if (LockBox.storage != null)
            return;

        var storage;

        if (typeof (localStorage) == "object") { //For all other browsers
            storage = localStorage;
        }
        else if (document && document.all) { //For IE
            var userData = $("<span>");
            userData.attr("id", "userData");
            userData.css("behavior", "url(#default#userData)");

            storage = userData.get(0);
            //storage = document.createElement("span");
            //storage.style.behavior = "url(#default#userData)";

            if (document.body) {
                userData.appendTo(document.body);
                //document.body.appendChild(storage);                
            }
            else {
                return;
            }

            if (storage) {
                storage.load("lockbox");
            }
        }

        LockBox.storage = storage;
    },

    set: function(name, value) {
        LockBox.ensureStorage();

        if (LockBox.storage) {
            if (typeof (localStorage) == "object") { //For all other browsers
                LockBox.storage[name] = value;
            }
            else if (document.all) {
                var userData = $("span#userData");
                userData.attr(name, value);
                //LockBox.storage.setAttribute(name, value);
                LockBox.storage.save("lockbox");
            }
        }
    },

    get: function(name, defaultValue) {
        LockBox.ensureStorage();

        var result = null;
        if (LockBox.storage) {
            if (typeof (localStorage) == "object") {
                result = LockBox.storage[name];
            }
            else {
                var userData = $("span#userData");
                result = userData.attr(name);
                if (this.isInitialLoad) {
                    result = undefined;
                    this.isInitialLoad = false;
                }
            }
        }

        return result || defaultValue;
    },

    remove: function(name) {
        LockBox.ensureStorage();
        if (LockBox.storage) {
            if (typeof (localStorage) == "object") {
                LockBox.storage.removeItem(name);
            }
            else if (document.all) {
                var userData = $("span#userData");
                userData.removeAttr(name);
                //userData.get(0).save("lockbox");
                LockBox.storage.save("lockbox");
            }
        }
    }
}
;OC.MVC.tabs = {
  tabsContent: 'tabsMainContent', 
  init: function(tabContainerName) {
     $("#" + tabContainerName).find("ul li").click(function() {
        $("#" + tabContainerName).find("ul li.active").removeClass("active");
        $(this).addClass("active");
        try
        {
          var navigateUrl = $(this).find('a').attr('navigateUrl').split('/');
          if (navigateUrl.length >= 2) {
            var tabAction = OC.MVC.util.getLink(navigateUrl[0], navigateUrl[1]); 
            OC.MVC.tabs.loadTabContent(tabAction);
          }
        }
        catch(err) 
        {}
     });
     
     $("#" + tabContainerName).find("ul li.active").click();
  },
  loadTabContent: function(navigateUrl) {
     if (navigateUrl && navigateUrl != '') {
       OC.MVC.util.loadView(OC.MVC.tabs.tabsContent, navigateUrl);
     }
  }
};//*******************************************************************************************************
//  
//  File:        oc-grid.js
//  Author:      Andy Tan, Vladimir Nechypurenko
//  Description: JS configuration for jqGrid, which is populated by GridHtmlHelper 
//  Review:      02/12/2010 Vladimir Nechypurenko
//               - Review the code and add function
//               08/12/2010 Vladimir Nechypurenko
//               - Add getUrl function 
//
//*******************************************************************************************************

OC.MVC.grid = function () {
};
OC.MVC.grid.events = {
    gridComplete: 'grid_gridComplete',
    loadComplete: 'grid_loadComplete',
    beforeRequest: 'grid_beforeRequest',
    onsort: 'onsort'
};

OC.MVC.grid.prototype = {
    ///
    /// Init function for jqGrid
    ///  - gridName - Id of the Grid, which is specified in GridModel constructor
    ///  - pager - id of the div, which will be used as Pager container
    ///  - config - JSON object generated by Grid Html Helper
    ///  - isHidePagingIfNoPages - boolean value to Hide or Show Paging bar if total pages == 1
    ///
    init: function (gridName, pager, config, isHidePagingIfNoPages) {
        if ($('#' + pager).length > 0 && ($('#gbox_' + gridName).length == 0)) {

            // globalise
            this.mainContainerId = '#gbox_' + gridName;
            this.jContainer = $('#' + gridName);
            this.gridName = gridName;
            this.config = config;

            this.config.gridview = true;
            this.config.height = 'auto';

            //Without the following block Postback doesn't work -- Thanks to Vald :-)
            if (this.config.postData) {
                this.config.postData = jQuery.parseJSON(this.config.postData);
            } else {
                this.config.postData = {};
            }

            this.isHidePagingIfNoPages = isHidePagingIfNoPages;

            // set config
            this.config.pager = $('#' + pager);

            // Eval the formatter link
            var colModel = this.config.colModel;
            for (var i in colModel) {
                colModelItem = colModel[i];
                if (colModelItem.formatter != null) {
                    colModelItem.formatter = eval(colModelItem.formatter);
                }
            }

            if (this.isHidePagingIfNoPages) {
                this.config.pager.hide();
            }

            // register events
            this.registerEvents();
            // show grid
            if (!this.config.postData) this.config.postData = [];

            this.myGrid = this.jContainer.jqGrid(this.config);
            if (config.datatype == 'local' && config.localData) {
                for (var i = 0; i < config.localData.length; i++) {
                    var rowLocalData = config.localData[i];
                    var rowData = {};
                    $.each(rowLocalData.cell, function (index, value) {
                        rowData[colModel[index].name] = value;
                    });

                    this.myGrid.addRowData(i, rowData);
                }
                // Right border changes
                $(this.mainContainerId).find('TH.ui-state-default:last-child').css('border-right', '0');
                this.myGrid.find('TR.jqgrow TD[role$=gridcell]:last-child').css('border-right', '0');
            }
        }
    },
    
    registerEvents: function () {

        var gridObj = this;

        this.config.gridComplete = function () {
            gridObj.gridComplete(gridObj);
        };

        // Grid complete functions
        this.config.onPaging = function () {
            gridObj.onPaging(gridObj);
        };

        // When grid data is loaded/reloaded
        this.config.loadComplete = function (data) {
            var pager = $('#pg_' + gridObj.gridName + '_pager');
            if (gridObj.isHidePagingIfNoPages && data && data.total == 1) {
                pager.parent().hide();
            }
            else
                pager.parent().show();
            gridObj.loadComplete(gridObj, data);
        };

        // Before grid is requested
        if (this.config.beforeRequest == null) {
            this.config.beforeRequest = function () {
                gridObj.beforeRequest(gridObj);
            };
        }
        else {
            this.config.beforeRequest = eval(this.config.beforeRequest);
        }

        // Upon column sort
        this.config.onSortCol = function () {
            gridObj.onSortCol(gridObj);
        };

        this.config.loadError = function (xhr, status, error) {
            gridObj.onLoadError(gridObj, xhr, status, error);
        };
    },
    onPaging: function (gridObj) {
        var gridContext = gridObj.jContainer;
        var totalRecords = gridContext.getGridParam("records");
        var page = gridContext.getGridParam("page");
        var rowNum = gridContext.getGridParam("rowNum");
        if (totalRecords > 0 && page > 0 && (page - 1) * rowNum > totalRecords) {
            gridContext.setGridParam({ page: parseInt((totalRecords - 1) / rowNum) + 1 });
        }
    },
    gridComplete: function (gridObj) {

        var gridContext = gridObj.jContainer;
        var config = gridObj.config;
        var isHidePagingIfNoPages = gridObj.isHidePagingIfNoPages;

        // Config setting if enabled: Don't display pager if there's no pages...
        var totalRecords = gridContext.getGridParam("records");
        var page = gridContext.getGridParam("page");
        var rowNum = gridContext.getGridParam("rowNum");

        // Header tooltips
        var gridColModel = gridContext.getGridParam("colModel");
        if (!gridObj.headerToolTipsLoaded) {
            for (var i = 0; i < gridColModel.length; i++) {
                var columnSettings = gridColModel[i];
                if (columnSettings.isShowHeaderToolTipLink === true && columnSettings.headerToolTipLinkLabel
                        //[TODO] This is a temporary fix, we should be able to dynamically enable/disable the tooltip for each column.
                        && IC.Public.showHeaderTooltip()) {
                    var gridMainContainer = $(gridObj.mainContainerId);
                    var headerObj = gridMainContainer.find('TH.ui-th-column:eq(' + i + ')');
                    headerObj.attr('title', columnSettings.headerToolTipLinkText);
                    if (IC.Public.showHeaderTooltipLabel()) {
                        headerObj.find('DIV>SPAN:eq(0)').after('<a href="#" class="headerToolTip"><span>' + columnSettings.headerToolTipLinkLabel + '</span></a>');
                    }
                }
            }
            gridObj.headerToolTipsLoaded = true;
        }

        if (totalRecords <= 0) {
            config.pager.hide();
        }
        else {
            config.pager.show();
            var pagingControls = $(gridObj.mainContainerId).find('[id$=_pager_center]');
            if (totalRecords <= rowNum || isHidePagingIfNoPages) {
                pagingControls.css('visibility', 'hidden');
            }
            else {
                pagingControls.css('visibility', 'visible');
            }
        }

        // If there are no records...
        $(gridObj.mainContainerId).find('table.noRecords').remove();
        if (totalRecords == 0) {
            gridContext.after('<table class="noRecords"><tr><td>No records found</td></tr></table>');
        }

        var pageInfo = $(gridObj.mainContainerId).find(".ui-paging-info");
        if (totalRecords > 0) {

            $("#gridPagingTop").html(pageInfo.html());
            $("#gridPagingTop").show();
        }
        else {
            $("#gridPagingTop").hide();
        }

        // Fire event
        gridContext.trigger(OC.MVC.grid.events.gridComplete, [totalRecords]);

    },
    loadComplete: function (gridObj, data) {

        var gridContext = gridObj.jContainer;

        if (data == null) {
            gridContext.clearGridData();
            this.errorHandler();
            return;
        }
        // Empty row cells... assign &nbsp; to them if IE6 & 7
        var ie6 = ($.browser.msie && $.browser.version.substring(0, 1) === '6');
        var ie7 = ($.browser.msie && $.browser.version.substring(0, 1) === '7');
        if (ie6 || ie7) {
            gridContext.find('TD[role$=gridcell]').each(function (i) {
                var txt = $(this).html();
                if (txt == '') {
                    $(this).html('&nbsp;');
                }
            });
        }

        // Right border changes
        $(gridObj.mainContainerId).find('TH.ui-state-default:last-child').css('border-right', '0');
        gridContext.find('TR.jqgrow TD[role$=gridcell]:last-child').css('border-right', '0');

        var lineHeight = 0;
        gridContext.find('TD[role=gridcell]').each(function (i) {

            // Find the position of each TD.columnGroupStart and give the associated TH the same class name
            if ($(this).hasClass('columnGroupEnd')) {
                gridContext.find('TH.ui-th-column:eq(' + i + ')').addClass('columnGroupEnd');
            }

            // Ellipsis!
            if ($(this).hasClass('ellipsis')) {
                var txt = $(this).text();
                var html = $(this).html();
                var testHtml = '';
                var txtOld = '';
                var count = 0;
                var isTested = false;

                while ($(this).height() > lineHeight) { // this row is longer than expected, shrink the text
                    count++;
                    txtOld = txt;

                    // test to see if a single character line is higher than 'lineHeight'. If so, replace lineHeight with new one
                    if (!isTested) {
                        testHtml = html.replaceAll(txtOld.htmlEncode(), '.');
                        $(this).html(testHtml);
                        if ($(this).height() > lineHeight) {
                            lineHeight = $(this).height(); // reset row height
                            $(this).html(html); // restore text
                            break;
                        }
                        isTested = true;
                    }

                    // reduce the characters by 3
                    txt = txt.substr(0, txt.length - 3);
                    html = html.replaceAll(txtOld.htmlEncode(), txt.htmlEncode());
                    $(this).html(html);

                    if ($(this).height() <= lineHeight) { // ah ha! Remove a further 3 characters for the ...
                        txtOld = txt;
                        txt = txt.substr(0, txt.length - 3);
                        html = html.replaceAll(txtOld.htmlEncode(), txt.htmlEncode() + '...');
                        $(this).html(html);
                        break;
                    }
                    else if (count > 100) // prevent an infinite loop if possible
                        break;
                }
            }

        });

        // Fire event
        gridContext.trigger(OC.MVC.grid.events.loadComplete, [gridObj, data]);
    },
    beforeRequest: function (gridObj) {
        gridObj.jContainer.trigger(OC.MVC.grid.events.beforeRequest);

    },
    onSortCol: function (gridObj) {
        gridObj.jContainer.trigger(OC.MVC.grid.events.onsort);

    },
    onLoadError: function (gridObj, xhr, status, error) {
        gridObj.jContainer.clearGridData();
        this.errorHandler();
    },
    errorHandler: function () {
        var gridContext = this.jContainer;
        $(this.mainContainerId).find('table.noRecords').remove();
        gridContext.after('<table class="noRecords"><tr><td>Sorry - there has been a temporary problem trying to load your data. <br/><br/>Please <a href="javascript:void(0);">try again</a> </td></tr></table>');
        $("table.noRecords").find("a").bind("click", { obj: this }, function (events) {
            var _obj = events.data.obj;
            _obj.myGrid.trigger("reloadGrid");
        });
    }
};
///
///Function to get the Column index by Name
/// It's useful in the jqGrid formaters.
///
$.jgrid.extend({
    getColIndex: function (col) {
        var ret = -1;
        this.each(function () {
            var $t = this, pos = -1;
            $($t.p.colModel).each(function (i) {
                if (this.name === col || this.index === col) {
                    pos = i; return false;
                }
            });
            ret = pos;
        });
        return ret;
    },
    getUrl: function () {
        var ret = "";
        this.each(function () {
            var $t = this;
            ret = $t.p.url;
        });
        return ret;
    }
});

jQuery.fn.getColumnValue = function (column) {
    var cell = this.getColumnCell(column);
    if (cell) return cell.text();
    return null;
};

jQuery.fn.getColumnCell = function (column) {
    var gridView = this.parents('div.ui-jqgrid-view');
    if (gridView && gridView.length > 0) {
        var gridId = gridView.attr('id').replace("gview_", "");
        if (gridId) {
            var selector = gridId + "_" + column;
            return this.children('[aria-describedby="' + selector + '"]');
        }
    }
    return null;
};
;
/// <reference path="jquery-1.4.1.js" />

OC.MVC.searchPanel = {
    mainContentPlaceHolderId: 'searchPanelMainContent',
    searchResultsPlaceHolderId: 'searchPanelResults',
    init: function(mainContentViewFormId, searchInputId, mainContentViewControllerName, mainContentViewActionName, searchControllerName, searchActionName) {
        $("#linkMoreSearchOptions").click(function() {
            $("div.searchPanel .moreOptions").css("display", "block");

            $(this).css("display", "none");
            $("#linkHideSearchOptions").css("display", "inline");
        });

        $("#linkHideSearchOptions").click(function() {
            $("div.searchPanel .moreOptions").css("display", "none");

            $(this).css("display", "none");
            $("#linkMoreSearchOptions").css("display", "inline");
        });

        $("#btnSearchMoreOptions").click(function() {
            OC.MVC.searchPanel.performSearch();
        });

        $("#btnSearch").click(function() {
            OC.MVC.searchPanel.performSearch();
        });

        $("#btnClear").click(function() {
            OC.MVC.searchPanel.resetSearchCriterias();
        });

        this.mainContentViewFormId = mainContentViewFormId;
        this.mainContentViewControllerName = mainContentViewControllerName;
        this.mainContentViewActionName = mainContentViewActionName;
        this.searchControllerName = searchControllerName;
        this.searchActionName = searchActionName;
        this.searchInputId = searchInputId;

        OC.MVC.searchPanel.loadMainContent();
    },

    loadMainContent: function() {
        var url = OC.MVC.util.getLink(this.mainContentViewControllerName, this.mainContentViewActionName);
        if (url && url != '') {
            OC.MVC.util.loadView(OC.MVC.searchPanel.mainContentPlaceHolderId, url);
        }
    },

    performSearch: function() {
        var serialisedData = OC.MVC.searchPanel.buildSearchCriterias();
        var url = this.searchControllerName + "/" + this.searchActionName;
        var searchResultsPlaceHolderElement = $('#' + this.searchResultsPlaceHolderId);
        $.post(
                url,
                serialisedData,
                function(data) {
                    // Populate Search Results
                    searchResultsPlaceHolderElement.html(data);
                    searchResultsPlaceHolderElement.css("display", "block");
                },
                'html');

        //OC.MVC.util.loadView(this.searchResultsPlaceHolderId, url, serialisedData);
    },

    resetSearchCriterias: function() {
        $('#' + this.mainContentViewFormId)[0].reset();
        $('#' + this.searchInputId).attr("value", "");
    },

    buildSearchCriterias: function() {
        // Build up search criterias in serialised format i.e. name="Jess"&age="13"
        var searchCriteriasResult = '';
        var serialisedFormData = $('#' + this.mainContentViewFormId).serialize();
        var searchInputData = $('#' + this.searchInputId).val();
        if (serialisedFormData && serialisedFormData != '') {
            if (searchInputData && searchInputData != '') {
                searchCriteriasResult = serialisedFormData + "&" + this.searchInputId + "=" + searchInputData;
            }
            else {
                searchCriteriasResult = serialisedFormData;
            }
        }

        return searchCriteriasResult;
    }
}
    ;//*******************************************************************************************************
//  
//  File:        oc-validate.js
//  Author:      Vladimir Nechypurenko
//  Description: OC MVC validation functions
//
//*******************************************************************************************************

OC.MVC.validate = {
    // Used for Required attribute 
    requiredField: function(fieldName) {
        var ok = '#' + fieldName + '_ok';
        var err = '#' + fieldName + '_err';

        var el = $('#' + fieldName);

        $('#' + fieldName + "_errorDiv").show();

        if (el.val() == '') {
            $(ok).hide();
            var errMsg = $('#' + fieldName).attr("title");
            $(err).attr("alt", errMsg);
            $(err).show();
            return false;
        }
        else {
            $(err).hide();
            $(ok).show();
            return true;
        }
    },
    // Moves all the Server validation messages into Error container, if it exists
    validationMsgProcess: function() {
        $.each($(".field-validation-error, .field-validation-warning"), function(index, value) {
            var form = $(this).closest('form');
            //var errorContainer = form.find('.errorContainer');

            var errorElement = form.find('#' + $(this).attr('forid'));
            var errorMessage = $(this).html();
            var errorClass = "error";
            if ($(value).hasClass("field-validation-warning")) {
                errorClass = "warning";
            }
            IC.Public.displayErrorFor(errorElement, form, errorMessage, errorClass);

            $(this).remove();
        });
    }
};//*******************************************************************************************************
//  
//  File:        oc-menu.js
//  Author:      Vladimir Nechypurenko
//  Description: OC MVC menu plug-in for OC.MVC Menu Html Helper
//
//*******************************************************************************************************

OC.MVC.menu = function (options) {
    if (options) {
        this.menuId = options.menuId;
        this.useGenericLinks = options.useGenericLinks;
    }
};

var menuDone = false;
var focusedMenu = null;
var highlightedLink = null;
var blurMenuTimeout = null;
var highlightMenu = function(topMenuName) {
    var topMenuLink = null
    if (topMenuName != null) {
        topMenuLink = $('li#' + topMenuName + 'MenuItem');
        topMenuLink.removeClass('focus');
    }
    if (highlightedLink != topMenuLink) {
        if (highlightedLink != null) { highlightedLink.removeClass('highlight'); }
        highlightedLink = topMenuLink;
        if (highlightedLink != null) { highlightedLink.addClass('highlight'); }
    }
};

OC.MVC.menu.prototype = {
    menuId: 'mainMenu',
    useGenericLinks: false,
    //
    // bind onclick event, using actionUrl attribute of <a> tag from OC.MVC menu
    //
    //good
    hideHoldingDropdown: function() {
        var focusedSelect = $(".holdingFilter select");
        if (focusedSelect) {
            focusedSelect.blur();
        }
    },
    focusMenu: function(topMenuLink) {

        clearTimeout(blurMenuTimeout);
        var topMenuFocus;
        if (topMenuLink != null) {
            topMenuFocus = topMenuLink.parent();
            this.hideHoldingDropdown();
        }

        if (focusedMenu != null && topMenuFocus != null && focusedMenu.find('a').html() == topMenuFocus.find('a').html()) { return false; }
        if (focusedMenu != null) { focusedMenu.removeClass('focus'); focusedMenu.removeClass('onhover'); }
        focusedMenu = topMenuFocus;
        if (focusedMenu != null) { focusedMenu.addClass('focus'); }
    },
    blurMenu: function() {
        var _obj = this;
        blurMenuTimeout = setTimeout(function() {
            _obj.focusMenu(null);
        }, 500);
    },
    onMenuItemClick: function(events) {
        var _obj = events.data.obj;
        // Work arround for the GreenID session issues
        IC.Public.showLoading("");
        // Hide the pre-opened cluetips
        $("#cluetip").hide();

        if (events.data.actionUrl == "/Securities/OnlineSaleSellSecurities" || //Portfolio OSF
            events.data.actionUrl =="/Securities/SellOrTransferSecurities" // Employee OSF
            ) {
            window.location.href = events.data.actionUrl;
            return false;
        }

        ///*fix for issuer level style applications - JIRA - WEB - 19943*/
        //if (events.data.actionUrl.indexOf('Securities/OnlineSaleSellSecurities') > -1 || //Portfolio OSF
        //    events.data.actionUrl.indexOf('Securities/SellOrTransferSecurities') > -1 // Employee OSF
        //    ) {
        //    window.location.href = events.data.actionUrl;
        //    //window.location.reload(true);
        //    //window.location.assign(events.data.actionUrl);
        //    return false;
        //}

        var container = $("#" + OC.MVC.constants.mainContainer);
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, events.data.actionUrl);
        jQuery.Event(events).stopPropagation();
        _obj.focusMenu(null);
        this.blur();
    },
    onMenuGenericItemClick: function(events) {
        var _obj = events.data.obj;
        window.location = events.data.actionUrl;
    },
    init: function() {
        if (menuDone) { return; }
        menuDone = true;
        var _obj = this;

        $('#mainMenu ul').bgiframe();

        if (this.useGenericLinks) {
            $('a.topMenu').each(function(index, topMenu) {
                var actionUrl = $(topMenu).attr("actionUrl");
                if (!actionUrl) { actionUrl = $(topMenu).parent().find('li a').first().attr("actionUrl"); }
                if (typeof actionUrl != 'undefined') {
                    $(topMenu).parent().bind("click", { obj: _obj, actionUrl: actionUrl }, _obj.onMenuGenericItemClick);
                }
                $(topMenu).bind("focus", function() { _obj.focusMenu($(topMenu)); });
            });
        }
        else {
            $('a.topMenu').each(function(index, topMenu) {
                $(topMenu).attr("href", "javascript:void(0);");
                var actionUrl = $(topMenu).attr("actionUrl");
                if (!actionUrl) { actionUrl = $(topMenu).parent().find('li a').first().attr("actionUrl"); }
                if (typeof actionUrl != 'undefined') {
                    $(topMenu).parent().bind("click", { obj: _obj, actionUrl: actionUrl }, _obj.onMenuItemClick);
                }
                $(topMenu).bind("focus", function() { _obj.focusMenu($(topMenu)); });
                $(topMenu).parent().bind("mouseover", function() { _obj.focusMenu($(topMenu)); });
                $(topMenu).bind("blur", function() { _obj.blurMenu(); });
                $(topMenu).parent().bind("mouseout", function() { _obj.blurMenu(); });

                $(topMenu).parent().find('li a').each(function(subIndex, subMenu) {
                    var subActionUrl = $(subMenu).attr("actionUrl");
                    $(subMenu).attr("href", "javascript:void(0);");
                    if (typeof subActionUrl != 'undefined') {
                        $(subMenu).bind("click", { obj: _obj, actionUrl: subActionUrl }, _obj.onMenuItemClick);
                        $(subMenu).bind("focus", function() { $(this).parent().attr('class', 'focus'); _obj.focusMenu($(topMenu)); });
                        $(subMenu).bind("blur", function() { $(this).parent().attr('class', ''); });
                    }
                });
            });
        }
    }
};


;//*******************************************************************************************************
//  
//  File:        oc-checklist.js
//  Author:      Vladimir Nechypurenko
//  Description: JS part for Html.CheckList Helper
//               Binds the 'click' event to the List title to provide Collapsable functionality
//  Review:      
//
//*******************************************************************************************************

$.fn.checkList = function(options) {
    this.each(function() {
        var _element = $(this);
        var _div = _element.find("div.collapsible");
        _div.bind('click', function() {
            hideShowList();
        });
        _element.find('a.checkListTitle').bind('click', function() {
            hideShowList();
        });

        function hideShowList() {
            var ul = _element.find("ul");
            if (ul.hasClass("invisible")) {
                ul.removeClass("invisible");
                _div.removeClass("plus");
                _div.addClass("minus");
            }
            else {
                ul.addClass("invisible");
                _div.removeClass("minus");
                _div.addClass("plus");
            }
        }
    });
};//*******************************************************************************************************
//  
//  File:        oc-groupgrid.js
//  Author:      Vladimir Nechypurenko
//  Description: OC Group Grid: Allows rowspaning (grouping), usign jqGrid styles
//  Review:      
//
//*******************************************************************************************************

$.fn.ocGroupGrid = function(options) {
    if (typeof pin == 'string') {
        var fn = $.fn.ocGroupGrid[pin];
        if (!fn) {
            throw ("ocGroupGrid - No such method: " + options);
        }
        var args = $.makeArray(arguments).slice(1);
        return fn.apply(this, args);
    }
    return this.each(function() {
        var p = options;
        var ts = $(this);
        ts.css('overflow', 'hidden');
        var gridId = ts.attr('gridId');

        function IsLastColumn(index) {
            var result = true;
            if (index + 1 < p.colModel.length) {
                for (var i = index + 1; i < p.colModel.length; ++i) {
                    if (!p.colModel[i].hidden) {
                        result = false;
                    }
                }
            }
            return result;
        }

        $.each(p.colModel, function(index, value) {
            if (value.formatter != '') {
                value.formatter = eval(value.formatter);
            }
            value.canBeGrouped = true;
            if (p.groupConfig.ignoreGroupingFields) {
                if ($.inArray(value.index, p.groupConfig.ignoreGroupingFields) >= 0 || $.inArray(value.name, p.groupConfig.ignoreGroupingFields) >= 0) {
                    value.canBeGrouped = false;
                }
            }
            value.isLast = IsLastColumn(index);
        });

        var events = {
            objRef: ts,
            tableWidth: options.width,
            groupBy: p.groupConfig ? p.groupConfig.groupFieldName : undefined,
            getGroupValue: function(row) {
                var result = '';
                for (var i = 0; i < p.colModel.length; ++i) {
                    if (this.groupBy && (p.colModel[i].name == this.groupBy || p.colModel[i].index == this.groupBy)) {
                        if (row.cell) {
                            return row.cell[i];
                        }
                        else {
                            return row[i];
                        }
                    }
                }
                return result;
            },
            buildGrid: function() {
                this.div = $('<div>');
                this.div.css('position', 'relative');
                this.div.appendTo(this.objRef);
                this.div.append(this.getDataGrid());
            },
            addDataRow: function(row, table) {
                var tr = $('<tr>');
                tr.addClass('ui-widget-content');
                tr.addClass('jqgrow');
                tr.addClass('ui-row-ltr');
                tr.attr('role', 'row');

                var _obj = this;
                $.each(row.cell, function(index, value) {
                    if (p.colModel[index]) {
                        var tag = _obj.addDataCell(index, p.colModel[index], row.id, value, row.cell);
                        if (tag) {
                            tr.append(tag);
                            tr.attr('role', 'group-row');
                        }
                    }
                });

                p.value = this.getGroupValue(row);
                p.prevRow = row;

                return tr;
            },
            cellVal: function(val) {
                return val === undefined || val === null || val === "" || val === "null" ? "&#160;" : val + "";
            },
            formatter: function(colm, rowId, cellval, rwdat) {
                if (cellval === "null") {
                    v = this.cellVal(cellval);
                }
                else if (typeof colm.formatter !== 'undefined') {
                    var opts = { rowId: rowId, colModel: colm, gid: this.objRef.id };
                    if ($.isFunction(colm.formatter)) {
                        v = colm.formatter.call(this.objRef, cellval, opts, rwdat, 'add');
                    } else if ($.fmatter) {
                        v = $.fn.fmatter(colm.formatter, cellval, opts, rwdat, 'add');
                    } else {
                        v = this.cellVal(cellval);
                    }
                }
                else {
                    v = this.cellVal(cellval);
                }
                if (typeof (v) == "string") {
                    return v.split('\r\n').join('<br/>');
                }
                else {
                    return v;
                }
            },
            addDataCell: function(colid, colm, rowId, value, row) {
                var td = $('<td>');

                td.css('text-align', colm.align);
                if (colm.width) {
                    td.css('width', colm.width + 'px');
                }
                if (colm.hidden) {
                    td.css('display', 'none');
                }
                if (colid == row.length - 1 || colm.isLast) {
                    td.css('border-right', '0pt none');
                }

                var group = this.getGroupValue(row);
                var groupValue = $.jgrid.htmlEncode(group + "_" + OC.MVC.util.removeSpecialSymb(value));

                if (p.groupRow && colm.canBeGrouped && !colm.hidden && p.value == group) {
                    if (p.prevRow && p.prevRow.cell[colid] == value) {
                        var cell = null;
                        if (p.groupHash.hasKey(groupValue)) {
                            cell = p.groupHash.getItem(groupValue);
                        }
                        else {
                            // Very slow on large sets of data. Replaced with for loops. Could do with row indexing as well.
                            // cell = p.groupRow.find("td#col_" + colid + "[groupValue='" + groupValue + "']");

                            for (var j = 0; j < p.groupRow.length; j++) {
                                var _cell = p.groupRow[j].getElementsByTagName("td")["col_" + colid];

                                if (_cell && _cell.getAttribute('groupValue') === groupValue) {
                                    cell = $(_cell);
                                    break;
                                }
                            }

                            p.groupHash.setItem(groupValue, cell);
                        }

                        if (colid === p.UpdateColumn) {
                            var checkBox = cell.find("input.updatecheckbox");
                            var prevValue = checkBox.attr('value');
                            prevValue += ',' + row[p.HolderKeyColumn];
                            checkBox.attr('value', prevValue);
                        }
                        var rowspan = cell.attr('rowspan');
                        rowspan = rowspan ? parseInt(rowspan) : 1;
                        cell.attr('rowspan', ++rowspan);
                        return undefined;
                    }
                }

                td.attr('id', 'col_' + colid);
                td.attr('groupValue', groupValue);

                td.html(this.formatter(colm, rowId, value, row));
                return td;
            },
            refreshData: function(table) {
                var _obj = this;
                ts.trigger("beforeRefresh");

                p.prevRow = undefined;
                p.value = undefined;

                table.find('tr').remove();
                if (p.postData && p.postData.length > 0 && (typeof (p.postData) == "string" || typeof (p.postData[0]) == "string")) {
                    p.postData = $.parseJSON(p.postData);
                }
                $.ajax({
                    url: p.url,
                    type: p.mtype,
                    dataType: p.datatype,
                    data: p.postData || [],
                    success: function(result) {
                        if (result && result.records > 0) {
                            _obj.processData(table, result);
                        }
                        else {
                            _obj.noRecordTable();
                        }
                    }
                });
            },
            processData: function(table, data) {
                p.groupHash = new Hash();
                for (var i = 0; i < p.colModel.length; ++i) {
                    if (p.colModel[i].name == 'Update') {
                        p.UpdateColumn = i;
                    } else if (p.colModel[i].name == 'HolderKey') {
                        p.HolderKeyColumn = i;
                    }
                }
                var _obj = this;
                if (data.rows && data.rows.length > 0) {
                    $.each(data.rows, function(index, value) {
                        var tbody = table.append(_obj.addDataRow(value, table));
                        var tr = tbody.find('tr');

                        if (tr.attr("role") === "group-row") {
                            p.groupRow = tr;
                        }

                        if (index == data.rows.length - 1) {
                            ts.trigger("grid_gridComplete", [table, data.rows]);
                        }
                    });
                }
                else {
                    ts.trigger("grid_gridComplete", [table, data.rows]);
                }
            },
            getDataGrid: function() {
                ts.groupTable = $('<table>');

                ts.groupTable.addClass("resizeGrid");
                ts.groupTable.addClass("ui-jqgrid-btable");
                ts.groupTable.attr("cellspacing", "0");
                ts.groupTable.attr("cellpadding", "0");
                ts.groupTable.attr("border", "0");
                ts.groupTable.css("width", this.tableWidth + "px");

                this.refreshData(ts.groupTable);

                return ts.groupTable;
            },
            noRecordTable: function() {
                var table = $('<table>');
                table.attr('class', 'noRecords');
                table.html('<tr><td>No records found</td></tr>');
                table.appendTo(this.div);
                ts.trigger("grid_noRows", [table, null]);
            }
        };

        this.p = p;
        this.p.events = events;
        this.p.events.buildGrid();
    })
}

$.fn.ocGroupGridRefresh = function(postData) {
    return this.each(function() {
        if ($(this).get(0).p && $(this).get(0).p.events) {
            $(this).get(0).p.postData = $.extend($(this).get(0).p.postData, postData);
            var table = $(this).find("table.ui-jqgrid-btable");
            $(this).get(0).p.events.refreshData(table);
        }
    });
}

$.getGroupGrid = function(name) { 
   return $("#bdiv_" + name);
}

$.fn.getGroupGridCheckedValues = function() { 
   var checkedValues = $(this).find('input.updatecheckbox').map(function() { if (this.checked == true) { return $(this).val() }; }).get();
   return checkedValues.join(',');
};//*******************************************************************************************************
//  
//  File:        oc-date.js
//  Author:      Vladimir Nechypurenko
//  Description: JS wraper for jQuery UI DatePicker 
//  Review:      
//
//*******************************************************************************************************

OC.MVC.dateTime = {
    add: function(id, config) {
        $("#" + id).datepicker(config);
        if (config.mask) {
            $("#" + id).unmask().mask(config.mask);
        }
    }
}

$.fn.dateValidate = function (options) {
    var invalidErrorString = '';
    var futureErrorString = 'Future';
    var pastErrorString = 'Past';
    if (options.name == 'From' || options.name == 'To') { invalidErrorString += ' "' + options.name + '"'; futureErrorString += ' "' + options.name + '"'; pastErrorString += ' "' + options.name + '"'; }
    else { invalidErrorString += ' ' + options.name; futureErrorString += ' ' + options.name; pastErrorString += ' ' + options.name; }

    invalidErrorString += ' Date format is incorrect: Please use the format dd/mm/yyyy.';
    futureErrorString += ' date selected: Today\'s date or earlier is only available.';
    pastErrorString += ' date selected: Today\'s date or later is only available.';
    IC.Public.removeErrorFor($(this), $(this).closest("form"), invalidErrorString);
    IC.Public.removeErrorFor($(this), $(this).closest("form"), futureErrorString);
    IC.Public.removeErrorFor($(this), $(this).closest("form"), pastErrorString);
    if ($(this).val() == '') {
        var today = new Date();
        var _subYears = 0;
        if (options.name == 'From') { _subYears = 2; }
        var year = today.getYear();
        if (year < 1900) { year = year + 1900; }
        var showDateString = today.getDate() + '/' + (today.getMonth() + 1) + '/' + (year - _subYears);
        $(this).val(showDateString);
    }
    else {
        try {
            var dateVal = $.datepicker.parseDate(options.mask, $(this).val());
            var dateString = $.datepicker.formatDate(options.mask, dateVal);
            if ($(this).val().length != 10) { $(this).val(dateString); }
            var now = new Date();
            if (!options.allowFuture) {
                if (dateVal > now) {
                    IC.Public.displayErrorFor($(this), $(this).closest("form"), futureErrorString);
                }
            } else if (!options.allowPast) {
                now.setHours(0, 0, 0, 0);
                if (dateVal < now) {
                    
                    IC.Public.displayErrorFor($(this), $(this).closest("form"), pastErrorString);
                }
            }
        }
        catch (e) {
            IC.Public.displayErrorFor($(this), $(this).closest("form"), invalidErrorString);
        }
    }
};OC.MVC.autocompleteRegistry = new Hash();

OC.MVC.autocomplete = function (options) {
    if (options) {
        this.name = options.name;
        this.url = options.url;
        this.minLength = options.minLength || 1;
    }
    this.init();
};

OC.MVC.autocomplete.prototype = {
    init: function () {
        var element = $('#' + this.name + '_text');
        var valueElement = $('#' + this.name);
        var obj = this;
        OC.MVC.autocompleteRegistry.setItem(this.name, this);
        element.autocomplete({
            source: function(request, response) {
                $.ajax({
                    url: obj.url,
                    data: { searchText: element.val() },
                    dataType: 'json',
                    type: 'POST',
                    success: function(data) {
                        response(data);
                    }
                });
            },
            autoFocus: true,
            select: function(event, ui) {
                valueElement.val(ui.item.value);
                element.val(ui.item.label);
                if (ui.item.attributes) {
                    obj.selectedAttributes = {};
                    $.extend(obj.selectedAttributes, ui.item.attributes);
                }

                element.trigger("afterselect", [ui]);
                return false;
            },
            search: function(event, ui) {
                valueElement.val('');
                return true;
            },
            focus: function() {
                // prevent value inserted on focus
                return false;
            },
            close: function () {
                if ($(this).data() && $(this).data().autocomplete)
                    $(this).data().autocomplete.term = null;
            },
            minLength: obj.minLength
        });
    }
};/// <reference path="../json2.js" />
//$.uploadControl = $.uploadControl || {};

var uploaderSettings = {
    uploader: '/Scripts/jQuery-plugins/swfupload.swf',
    buttonImg: '/images/buttons/browsebackground.PNG',
    cancelImg: 'cancel.png',
    buttonCSS: 'oc-upload-button',
    uploadControls: new Hash()
};

OC.MVC.uploaderAPI = {
    getUploaderObj: function(name) {
        if (uploaderSettings.uploadControls.hasKey(name)) {
            return uploaderSettings.uploadControls.getItem(name);
        }
        return null;
    },
    getUploader: function(name) {
        return $("#" + name + "_container");
    },
    getDownloadTag: function(name) {
        return $("#" + name + "_download");
    },
    getDownloadLink: function(name) {
        return OC.MVC.uploaderAPI.getDownloadTag(name).attr("downloadLink");
    },
    getUploadLink: function(name) {
        return OC.MVC.uploaderAPI.getDownloadTag(name).attr("uploadLink");
    }
};

OC.MVC.uploaderEvents = {
    onFileSelect: 'onFileSelect',
    onBeforeFileUpload: 'onBeforeFileUpload',
    onUploadSuccess: 'onUploadSuccess',
    onUploadComplete: 'onUploadComplete',
    onUploadProgress: 'onUploadProgress'
};

OC.MVC.uploader = function(name, config) {
    this.init(name, config);
};

OC.MVC.uploader.prototype = {
    _uploadParameters: {},
    init: function (name, config) {
        var self = this;
        this.config = config;
        this.name = name;
        this.container = $("#" + self.name + "_container");
        this.uploadedFileName = this.container.find("#uploadedFileName");
        this.cancelBtn = this.container.find("#cancelBtn");
        this.cancelBtn.bind("click", { obj: this }, this.onCancelClick);

        if (config) {
            config = self._uploadConfig(config);
            config.button_placeholder_id = name;

            this.uploader = new SWFUpload(config);
            uploaderSettings.uploadControls.setItem(name, this);
        }
    },

    uploadProgress: function () {
    },

    onCancelClick: function (events) {
        var _obj = events.data.obj;
        _obj.container.removeClass("uploadError");
        var errorDiv = _obj.container.find("#" + _obj.name + "_error");
        errorDiv.hide();
        $("input#" + _obj.name).val("");
        _obj.show();
    },

    show: function () {
        $("#" + this.name + "_queued").hide();
        this.uploadedFileName.html("");
        this.container.find("object").removeClass('swfinvisible');
    },

    debug: function (message) {
        try {
            if (window.console && typeof (window.console.error) === "function" && typeof (window.console.log) === "function") {
                if (typeof (message) === "object" && typeof (message.name) === "string" && typeof (message.message) === "string") {
                    window.console.error(message);
                } else {
                    window.console.log(message);
                }
            }
        } catch (ex) {
        }
        try {
            if (this.settings.debug) {
                this.debugMessage(message);
            }
        } catch (ex1) {
        }
    },

    _uploadConfig: function (config) {
        var self = this;

        config.flash_url = config.flash_url || OC.MVC.util.getGenericLink(uploaderSettings.uploader);
        config.button_image_url = config.button_image_url || OC.MVC.util.getGenericLink(uploaderSettings.buttonImg);
        config.file_upload_limit = config.file_upload_limit || 0;
        config.file_queue_limit = config.file_queue_limit || 1;
        config.button_text = '<span class="uploadButton">' + (config.button_text || 'Browse') + '</span>';
        config.button_text_style = ".uploadButton { color: #ffffff; font-family: Arial; font-size: 12; font-weight: bold; }";
        config.button_width = config.button_width && parseInt(config.button_width) > 0 ? config.button_width : "67";
        config.button_height = config.button_height && parseInt(config.button_height) > 0 ? config.button_height : "22";
        config.button_text_left_padding = "12";
        config.button_text_top_padding = "1";
        config.button_cursor = SWFUpload.CURSOR.HAND;
        config.prevent_swf_caching = false;
        config.debug = false;
        config.debug_handler = this.debug;
        config.file_queued_handler = function (file) {
            self.container.find("object").addClass('swfinvisible');
            if (self.config.show_cancel_button) {
                self.uploadedFileName.html(file.name + ' (' + file.size + ' bytes)');
                self.uploadedFileName.addClass("nodownload");
                var queued = $("#" + self.name + "_queued");
                queued.show();
                queued.addClass("newfile");
            }
            var input = $("input#" + self.name);
            input.val(file.name);
            input.attr("filesize", file.size);

            if (self.config.auto_start == "true") {
                self.container.trigger(OC.MVC.uploaderEvents.onBeforeFileUpload, [self, file]);
                setTimeout(function () {
                    queued.removeClass("newfile");
                    self.uploader.startUpload();
                }, 100);
            }
            self.container.trigger(OC.MVC.uploaderEvents.onFileSelect, [self, file]);
        };
        config.upload_success_handler = function (file, serverData, responseReceived) {
            self.uploadedFileName.html(file.name + ' - Uploaded');
            self.container.trigger(OC.MVC.uploaderEvents.onUploadSuccess, [self, file, serverData, responseReceived]);
        };

        config.upload_complete_handler = function (file) {
            self.container.trigger(OC.MVC.uploaderEvents.onUploadComplete, [self, file]);
        };

        config.upload_progress_handler = function (file, done, total) {
            self.uploadedFileName.html(file.name + ' (' + Math.ceil((done / total) * 100) + ' %)');
            self.container.trigger(OC.MVC.uploaderEvents.onUploadProgress, [self, file, done, total]);
        };

        config.file_queue_error_handler = function(file, errorCode, message) {
            if (message.length <= 5) {
                message = "Sorry, an error occurred while uploading a file: " + message;
            }
            alert(message);
        };

        config.upload_error_handler = function (file, errorCode, message) {
            self.container.addClass("uploadError");
            var errorDiv = self.container.find("#" + self.name + "_error");
            if (message.length <= 5) {
                message = "Sorry, an error occurred while uploading a file: " + message;
            }
            errorDiv.find("span").html(message);
            errorDiv.show();
        };

        return config;
    }
};

;//*******************************************************************************************************
//  
//  File:        default.js
//  Author:      Vladimir Nechypurenko
//  Description: JavaScript for Default.aspx page.
//               Loads the default main menu item or the last URL from History
//  Review:      
//
//*******************************************************************************************************

OC.MVC.defaultPage = {
    init: function() {
        OC.MVC.history.isLoading = true;
        if (!this.isInitialized) {
            var container = $("#" + OC.MVC.constants.mainContainer);

            //Get the default action url from the Menu
            var defaultItem = $("#mainMenu a[defaultItem=true]").first();
            var defaultUrl = defaultItem.attr("actionUrl");
            //var defaultUrl = $("#DefaultUrl").val();

            var defaultData = null;
            var mainBody = $("#" + OC.MVC.constants.mainBody);
            //Pull the URL, saved in the history, if user does not click Home page and back to Home page in the history
            if (typeof (LockBox) == "object" && (window.location && window.location.hash != "" && window.location.hash != "#")) {
                var _url = LockBox.get("lastUrl");
                if (_url && _url != '') {
                    defaultUrl = _url;
                }
                defaultData = LockBox.get("lastUrlData");
            }

            if (mainBody.length > 0) {
                var _obj = this;
                if (container.length == 0) {

                    var url = IC.Public.urls.General.Index;

                    $.ajax({
                        url: url,
                        data: [],
                        success: function (result) {
                            mainBody.html(result);
                            _obj.loadView(defaultUrl, defaultData);
                            IC.Public.registerGA(defaultUrl, { viewKey: $(result).find("[name=ViewKey]").val() });
                        },
                        error: function(err) {
                            OC.MVC.util.errorMessage(err.responseText);
                        }
                    });
                } else {
                    if (!defaultUrl) {
                        defaultUrl = IC.Public.urls.Login.Login;
                    }
                    OC.MVC.history.isLoading = false;
                    window.location = defaultUrl;
                }
                this.isInitialized = true;
            }
        }

        OC.MVC.ga.trackPageview();
    },
    loadView: function(url, data) {
        if (url) {
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                data: data,
                dataType: 'html',
                success: function (result) {
                    var div = $('<div>');
                    div.html(result);
                    if ($(div).find("#master_layout").length > 0) {
                        window.location = "/";
                        return;
                    }
                    $("#" + OC.MVC.constants.mainContainer).html(result);
                    OC.MVC.history.isLoading = false;
                    IC.Public.hideLoading();
                },
                error: function(err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    }
};/// <reference path="jQuery/jquery-1.4.4.js" />
/// <reference path="MicrosoftMvc/MicrosoftAjax.js" />
/// <reference path="common.js" />

IC.Public.consts = {
    actionsDetails: [{ title: "Tax Details", url: "" },
                      { title: "Address", url: "" }],
    actionsHoldings: [{ title: "View Details", url: OC.MVC.util.getLink("HoldingDetails", "Index") },
                      { title: "Balance History", url: OC.MVC.util.getLink("Holdings", "Balance") }],
    minorExemption: "333333333",
    gaIssuerCode_CustomVariableIndex: 1,
    genericError: "An error occurred while processing your request. Please try again after some time.",
    impersonateUserNoAccessMessage:"Not able to continue. Impersonated user cannot process this request."
};

IC.Public.urls = {
    Login: {
        Login: OC.MVC.util.getLink("Login", "Login")
    },
    General: {
        Index: OC.MVC.util.getLink("General", "Index")
    },
    Chatbot: {
        BotToken: OC.MVC.util.getLink("Bot", "BotToken"),
        BotReconnectToken: OC.MVC.util.getLink("Bot", "BotReconnectToken")
    },
    Settings: {
        changeEmail: OC.MVC.util.getLink("Settings", "ChangeEmail"),
        changePassword: OC.MVC.util.getLink("Settings", "ChangePassword"),
        changeSecurityQuestion: OC.MVC.util.getLink("Settings", "ChangeSecurityQuestion"),
        changeTransactionPassword: OC.MVC.util.getLink("Settings", "ChangeTransactionPassword"),
        confirmPassword: OC.MVC.util.getLink("Settings", "ConfirmPassword"),
        changeEmailComplete: OC.MVC.util.getLink("Settings", "ChangeEmailComplete"),
        settings: OC.MVC.util.getLink("Settings", "Settings"),
        changePasswordSubmit: OC.MVC.util.getLink("Settings", "ChangePasswordSubmit"),
        changeTransactionPasswordSubmit: OC.MVC.util.getLink("Settings", "ChangeTransactionPasswordSubmit"),
        validateTransactionPassword: OC.MVC.util.getLink("Settings", "ValidateTransactionPassword"),
        validateTransactionPin: OC.MVC.util.getLink("Settings", "ValidateTransactionPin"),
        createTransactionPasswordSubmit: OC.MVC.util.getLink("Settings", "CreateTransactionPassword"),
        thirdPartyAuthorisation: OC.MVC.util.getLink("Settings", "ManageThirdPartyAccess"),
        smsPinRegistrationIndex: OC.MVC.util.getLink("SmsPin", "Index"),
        smsPinRegistration: OC.MVC.util.getLink("SmsPin", "Register")
    },
    PlanDetails: {
        vestingGrid: OC.MVC.util.getLink("Detail", "PlanDetailVestingGrid")
    },
    Exercise: {
        exerciseTransactionList: OC.MVC.util.getLink("Exercise", "ExerciseTransactionList"),
        awardsList: OC.MVC.util.getLink("Exercise", "AwardsList"),
        transactionsList: OC.MVC.util.getLink("Exercise", "TransactionsList"),
        message: OC.MVC.util.getLink("Exercise", "Message"),
        details: OC.MVC.util.getLink("Exercise", "Details"),
        detailsWithValues: OC.MVC.util.getLink("Exercise", "DetailsWithValues"),
        validateExercise: OC.MVC.util.getLink("Exercise", "ValidateExercise"),
        confirmExercise: OC.MVC.util.getLink("Exercise", "ConfirmExercise")
    },
    LoanRepayment: {
        loanRepaymentList: OC.MVC.util.getLink("LoanRepayment", "LoanRepaymentDetailsList"),
        historicalLoanTransactions: OC.MVC.util.getLink("LoanRepayment", "ViewHistoricalLoanTransactions"),
        historicalLoanTransactionsListItems: OC.MVC.util.getLink("LoanRepayment", "HistoricalLoanTransactionsListItems"),
        historicalLoanTransactionsList: OC.MVC.util.getLink("LoanRepayment", "HistoricalLoanTransactionsList"),
        loanRepaymentCash: OC.MVC.util.getLink("LoanRepayment", "LoanRepaymentCash"),
        loanRepaymentShares: OC.MVC.util.getLink("LoanRepayment", "LoanRepaymentSalesDetails"),
        loanRepaymentPartialShares: OC.MVC.util.getLink("LoanRepayment", "LoanRepaymentPartialSellShares"),
        RepaymentDetailsWithValues: OC.MVC.util.getLink("LoanRepayment", "RepaymentDetailsWithValues"),
        CashRepaymentDetailsWithValues: OC.MVC.util.getLink("LoanRepayment", "CashRepaymentDetailsWithValues"),
        SaleRepaymentDetailsWithValues: OC.MVC.util.getLink("LoanRepayment", "SaleRepaymentDetailsWithValues")
    },
    Securities: {
        sellOrTransferList: OC.MVC.util.getLink("Securities", "SellOrTransferSecurities"),
        transactionsList: OC.MVC.util.getLink("Securities", "SecuritiesTransactions")
    },
    PaymentMethod: {
        internationalBankWire: OC.MVC.util.getLink("PaymentMethod", "InternationalBankWire"),
        bankAccountDetails: OC.MVC.util.getLink("PaymentMethod", "BankAccountDetails"),
        onlineSalebankAccountDetails: OC.MVC.util.getLink("PaymentMethod", "OnlineSaleBankAccountDetails")
    },
    Offers: {
        getOfferDocument: OC.MVC.util.getLink("Offers", "GetOfferDocument")
    },
    Summary: {
        downloadPlanStatement: OC.MVC.util.getLink("Summary", "DownloadPlanStatement"),
        downloadEquityPlanStatement: OC.MVC.util.getLink("Summary", "DownloadEquityPlanStatement")
    },
    Message: {
        blackoutMessage: OC.MVC.util.getLink("Message", "BlackoutMessage"),
        preClearanceMessage: OC.MVC.util.getLink("Message", "PreClearanceMessage")
    },
    OnlineSale: {
        OnlineSaleSellSecurities: OC.MVC.util.getLink("Securities", "OnlineSaleSellSecurities"),
        OnlineSaleSellSecuritiesList: OC.MVC.util.getLink("Securities", "OnlineSaleSellSecuritiesList"),
        OnlineSaleTransactionsList: OC.MVC.util.getLink("Securities", "SaleTransactionsList"),
        OnlineSaleTransactionMethod: OC.MVC.util.getLink("Securities", "OnlineSaleTransactionMethod"),
        OnlineSaleTransactionMethodDetails: OC.MVC.util.getLink("Securities", "OnlineSaleTransactionMethodDetails"),
        OnlineSaleConfirmSecuritySaleDetails: OC.MVC.util.getLink("Securities", "OnlineSaleConfirmSecuritySaleDetails"),
    },
};

IC.Public.getValidatorCfg = function () {
    //var errorContainers = $('[id=mainErrorDiv]');
    //if (errorContainers.length == 0) return null;

    //var _errorContainer = errorContainers[errorContainers.length - 1];
    var _errorContainer = $("#mainErrorDiv");
    return {
        errorContainer: _errorContainer,
        errorLabelContainer: $("ol", _errorContainer),
        wrapper: 'li',
        meta: "validate"
    };
};

IC.Public.getFormValidatorCfg = function (form) {
    if (form == undefined)
        return IC.Public.getValidatorCfg();

    var _errorContainer = $(form).find("#mainErrorDiv");
    return {
        errorContainer: _errorContainer,
        errorLabelContainer: $("ol", _errorContainer),
        wrapper: 'li',
        meta: "validate"
    };
};

IC.Public.removeErrorFor = function (element, form, errorMessage) {
    var cfg = IC.Public.getFormValidatorCfg(form);
    var errorEncoded = $.jgrid.htmlEncode(OC.MVC.util.removeSpecialSymb(errorMessage));
    // go thtough all li elements and delete if it's this error container
    $.each(cfg.errorLabelContainer.children(), function (index, errorListItem) {
        var listItemEncoded = $.jgrid.htmlEncode(OC.MVC.util.removeSpecialSymb($(errorListItem).find("label").html()));
        if (listItemEncoded == errorEncoded) {
            $(errorListItem).remove();
        }
    });
    // hide error area if there are no more errors
    if (cfg.errorLabelContainer.children.length == 0) {
        cfg.errorContainer.hide();
    }
    // clear error from element
    if (element != null) {
        element.removeClass('error');
    }
};

IC.Public.displayErrorFor = function (element, form, errorMessage, className) {
    if (!className) {
        className = 'error';
    }
    var errorEncoded = $.jgrid.htmlEncode(OC.MVC.util.removeSpecialSymb(errorMessage));
    // adds the error msg to the container if not added yet
    var cfg = IC.Public.getFormValidatorCfg(form);
    var isErrorExist = false;
    $.each(cfg.errorLabelContainer.children(), function (index, errorListItem) {
        var listItemEncoded = $.jgrid.htmlEncode(OC.MVC.util.removeSpecialSymb($(errorListItem).find("label").html()));
        if (listItemEncoded == errorEncoded) {
            isErrorExist = true;
        }
    });
    if (!isErrorExist) {
        var errorItem = $('<' + cfg.wrapper + '>');
        var error = $('<label>');
        error.addClass(className);
        error.html(errorMessage);
        if (element != null) {
            element.addClass('error');
            error.attr("for", element.attr('id'));
        }
        error.appendTo(errorItem);
        errorItem.appendTo(cfg.errorLabelContainer);

        cfg.errorContainer.show();
        cfg.errorLabelContainer.show();
    }
};

IC.Public.showLoading = function (msg) {
    var loadingSpinner = $("#loadingSpinner");
    loadingSpinner.find("span").html(msg || "");
    if (loadingSpinner.css("display") == "none") {
        loadingSpinner.show();
    }
};
IC.Public.hideLoading = function () {
    $("#loadingSpinner").hide();
};
IC.Public.onChangeMonthYear = function (year, month, inst) {
    var date = $(this).val();
    if (date && date != '') {
        var dateParts = date.split('/');
        if (dateParts.length == 3) {
            dateParts[1] = month;
            dateParts[2] = year;
            $(this).val(dateParts.join('/'));
        }
    }
};
IC.Public.getIssuerCode = function (viewKey) {
    if (viewKey && viewKey != "-1" && viewKey.substring(0, 2) != "CV") {
        var _viewKeys = viewKey.split(',');
        if (_viewKeys.length > 1) {
            return _viewKeys[1];
        }
    }
    return "";
};
IC.Public.getCompanyCode = function (viewKey) {
    if (viewKey && viewKey != "-1") {
        var _viewKeys = viewKey.split(',');
        if (_viewKeys.length > 0) {
            return _viewKeys[0];
        }
    }
    return "";
};


IC.Public.registerGA = function (url, data) {
    var _url = new String(url);
    if (_url.startsWith("#")) _url = _url.substring(1);
    var gaIssuerCode = $("#gaIssuerCode");
    var issuerCode = "";

    if (typeof (_gat) == 'object') {
        var pageTracker = _gat._getTracker(OC.MVC.constants.gaTrackingCode);

        if (pageTracker) {
            if ((gaIssuerCode.length > 0 && $.trim(gaIssuerCode.val()) != '' && _url != "/Holdings/Index")) {
                issuerCode = gaIssuerCode.val();
            } else if (typeof (data) == 'object') {
                issuerCode = IC.Public.getIssuerCode(data.viewKey || data.key || data.ViewKey);
            }
            if ($.trim(issuerCode) != "") {
                pageTracker._setCustomVar(IC.Public.consts.gaIssuerCode_CustomVariableIndex, "IssuerCode", issuerCode, OC.MVC.ga.level.page);
                try {
                    pageTracker._setVar(issuerCode);
                } catch (err) {
                }
            }

            pageTracker._trackPageview(_url);
        }
    }
};

IC.Public.getDateFromToday = function (years) {
    var today = new Date();
    var mthPrefix = '';
    var dayPrefix = '';
    if (today.getMonth() < 9)
        mthPrefix = '0';
    if (today.getDate() < 10)
        dayPrefix = '0';

    return dayPrefix + today.getDate() + '/' + mthPrefix + (today.getMonth() + 1) + '/' + (today.getFullYear() - years);
};

IC.Public.getDate = function (dateStr) {
    var mthPrefix = '';
    var dayPrefix = '';
    var date = new Date(dateStr);
    if (date.getMonth() < 9)
        mthPrefix = '0';
    if (date.getDate() < 10)
        dayPrefix = '0';
    return dayPrefix + date.getDate() + '/' + mthPrefix + (date.getMonth() + 1) + '/' + date.getFullYear();
};
IC.Public.formatDate = function (date) {
    var datearray = date.split("-");
    return datearray[2] + '/' + datearray[1] + '/' + datearray[0];
};

//amount	                    The value to be formatted. Usually amount is a numeric value but a String can be supplied, since parseFloat(…) is used to check the incoming value.
//currency_symbol_before	    Currency symbol/text string to be placed before the amount (and after any − sign)
//currency_symbol_after	        Currency symbol/text string appended to the amount.
//thousands_separator	        Thousands separator string to apply for amounts ≥ 1000.
//decimal_point	                Decimal point (period) string to apply if required.
//significant_after_decimal_pt	Number of significant digits after the decimal point.
//display_after_decimal_pt	    Number of digits to display after the decimal point. (Must be ≥ 0)

IC.Public.FormatMoney = function (amount, currency_symbol_before,
    currency_symbol_after, thousands_separator, decimal_point,
    significant_after_decimal_pt, display_after_decimal_pt) {
    // 30JUL2008 MSPW  Fixed minus display by moving this line to the top
    // We need to know how the significant digits will alter our displayed number
    var significant_multiplier = Math.pow(10, significant_after_decimal_pt);

    // Only display a minus if the final displayed value is going to be <= -0.01 (or equivalent)
    var str_minus = (amount * significant_multiplier <= -0.5 ? "-" : "");

    // Sanity check on the incoming amount value
    amount = parseFloat(amount);
    if (isNaN(amount) || Math.LOG10E * Math.log(Math.abs(amount)) +
        Math.max(display_after_decimal_pt, significant_after_decimal_pt) >= 21) {
        return str_minus + currency_symbol_before +
            (isNaN(amount) ? "#" : "####################".substring(0, Math.LOG10E * Math.log(Math.abs(amount)))) +
                (display_after_decimal_pt >= 1 ?
                    (decimal_point + "##################".substring(0, display_after_decimal_pt)) : "") +
                        currency_symbol_after;
    }

    // Make +ve and ensure we round up/down properly later by adding half a penny now.
    amount = Math.abs(amount) + (0.5 / significant_multiplier);

    amount *= significant_multiplier;

    var str_display = parseInt(
        parseInt(amount) * Math.pow(10, display_after_decimal_pt - significant_after_decimal_pt)).toString();

    // Prefix as many zeroes as is necessary and strip the leading 1
    if (str_display.length <= display_after_decimal_pt)
        str_display = (Math.pow(10, display_after_decimal_pt - str_display.length + 1).toString() +
            str_display).substring(1);

    var comma_sep_pounds = str_display.substring(0, str_display.length - display_after_decimal_pt);
    var str_pence = str_display.substring(str_display.length - display_after_decimal_pt);

    if (thousands_separator.length > 0 && comma_sep_pounds.length > 3) {
        comma_sep_pounds += ",";

        // We need to do this twice because the first time only inserts half the commas.  The reason is
        // the part of the lookahead ([0-9]{3})+ also consumes characters; embedding one lookahead (?=...)
        // within another doesn't seem to work, so (?=[0-9](?=[0-9]{3})+,)(.)(...) fails to match anything.
        if (comma_sep_pounds.length > 7)
            comma_sep_pounds = comma_sep_pounds.replace(/(?=[0-9]([0-9]{3})+,)(.)(...)/g, "$2,$3");

        comma_sep_pounds = comma_sep_pounds.replace(/(?=[0-9]([0-9]{3})+,)(.)(...)/g, "$2,$3");

        // Remove the fake separator at the end, then replace all commas with the actual separator
        comma_sep_pounds = comma_sep_pounds.substring(0, comma_sep_pounds.length - 1).replace(/,/g, thousands_separator);
    }

    return str_minus + currency_symbol_before +
        comma_sep_pounds + (display_after_decimal_pt >= 1 ? (decimal_point + str_pence) : "") +
            currency_symbol_after;
};

jQuery.fn.showButton = function () {
    return this.parent().show();
};

jQuery.fn.hideButton = function () {
    return this.parent().hide();
};

IC.Public.getUrl = function (url, data) {
    var pathname = $(location).attr('pathname');
    var result;
    if (pathname && pathname.startsWith("/Employee")) {
        result = pathname + url;
    } else
        result = url;
    if (data) result += "?" + $.param(data);
    return result;
};

IC.Public.getViewKey = function () {
    $(this).appendPostData({ viewKey: $("#ViewKey").val() });
};

IC.Public.getViewKeyAndIsViewConfirmation = function () {
    $(this).appendPostData({ viewKey: $("#ViewKey").val(), isViewConfirmation: $("#IsViewConfirmation").val() });
};

//[TODO] This is a temporary fix, we should be able to dynamically enable/disable the tooltip for each column.
IC.Public.showHeaderTooltip = function () {
    var showHeaderTooltip = $('#showHeaderTooltip').val();
    if (showHeaderTooltip == undefined) {
        return true;
    }
    return (showHeaderTooltip.toLowerCase() === "true");
};

IC.Public.showHeaderTooltipLabel = function () {
    var showHeaderTooltipLabel = $('#showHeaderTooltipLabel').val();
    if (showHeaderTooltipLabel == undefined) {
        return true;
    }
    return (showHeaderTooltipLabel.toLowerCase() === "true");
};

IC.Public.hideHeaderTitleForMemberClients = function () {
    $.ajax({
        url: OC.MVC.util.getLink("VotingType", "HideHeaderTitle"),
        type: 'GET',
        success: function (result) {
            if (result) {
                var hideHeaderTitle = result.hideHeaderTitle;
                if (hideHeaderTitle) {
                    $('span.headerTitle').hide();
                }
            }
        }
    });
};

IC.Public.ReinvestmentPlansRestrictionTypes = {
    None: "0",
    HoldingValueGreaterThanLowerLimit: "1",
    HoldingValueGreaterThanUpperLimit: "2",
    CompanyOrJointInvestor: "3",
    ThirdPartyImpersonationMode: "4"
};

IC.Public.getRestrictedTypeMessage = function (restrictionType) {
    var disabledMessage = $('#UpdateMaxHolderValueExceededTitle').val();

    switch (restrictionType) {
        case IC.Public.ReinvestmentPlansRestrictionTypes.HoldingValueGreaterThanLowerLimit:
            disabledMessage = $('#HoldingValueGreaterThanLowerLimitMessage').val();
            break;
        case IC.Public.ReinvestmentPlansRestrictionTypes.HoldingValueGreaterThanUpperLimit:
            disabledMessage = $('#HoldingValueGreaterThanUpperLimitMessage').val();
            break;
        case IC.Public.ReinvestmentPlansRestrictionTypes.CompanyOrJointInvestor:
            disabledMessage = $('#CompanyOrJointInvestorMessage').val();
            break;
        case IC.Public.ReinvestmentPlansRestrictionTypes.ThirdPartyImpersonationMode:
            disabledMessage = $('#ThirdPartyImpersonationModeMessage').val();
            break;
    }

    return disabledMessage;
};

IC.Public.disableControls = function (id) {
    if (id != null && id != undefined)
        $('#' + id + ' input, select, button').attr("disabled", true);
};

IC.Public.IsThirdPartyImpersonateUser = function () {
    var isThirdPartyImpersonateUser = $("#IsThirdPartyImpersonateUser").val();
    if (isThirdPartyImpersonateUser != undefined) {
        if (isThirdPartyImpersonateUser.toLowerCase() == "true")
            return true;
        else
            return false;
    }
    return false;
};

IC.Public.HideMenu = function () {
    this.mainMenu = $("#mainMenu");

    if (this.mainMenu.length > 0) {
        this.mainMenu.find('ul').remove();
        this.mainMenu.removeClass('mainmenu');
        this.mainMenu.addClass('noMenu');
    }
    if (this.actionLinks != undefined) {
        var links = this.actionLinks.find('li');
        $.each(links, function (index, value) {
            var a = $(value).find('a');
            if (a.attr('id') == 'contactUs') {
                $(value).addClass('noLeftBar');
            } else {
                $(value).remove();
            }
        });
    }
};

IC.Public.DoNothingForChatbotEnterKeyPress = function (e) {
    if (e.target.parentElement != null && e.target.parentElement != undefined) {
        if (e.target.parentElement.className.toLowerCase() == 'wc-textbox') {
            //e.preventDefault();
            return true;
        }
    }
    return false;
};;Type.registerNamespace("IC.Public");

IC.Public.updateCampaignsPanel = function(page) {

    var url = OC.MVC.util.getLink("CampaignsPanel", "Index");
    
    
    var campaignsPanel = $('#sidePanel');

    if (campaignsPanel != undefined) {
        campaignsPanel.show();

        $.ajax({
            url: url,
            type: 'POST',
            data: { page: page },
            async: false,
            success: function (result) {
                if ($(campaignsPanel).html() != result) {
                    $(campaignsPanel).html(result);
                }
            },
            error: function(error) {
                if ($(campaignsPanel).html() != error) {
                    $(campaignsPanel).html(error);
                }
            }
        });
    }
}

IC.Public.hideCampaignsPanel = function () {
    var campaignsPanel = $('#sidePanel');
    campaignsPanel.hide();
}
;IC.Public.backtoportfolio = function (options) {
};

IC.Public.backtoportfolio.prototype = {
    onBackToPortfolioClick: function (events) {
        var url = OC.MVC.util.getLink("Holdings", "Index");
        OC.MVC.util.loadMainContainerView(url);
    },
    onBackToHoldingDetailsClick: function (events) {
        var url = OC.MVC.util.getLink("HoldingDetails", "Index");
        OC.MVC.util.loadMainContainerView(url);
    },

    onBackToPlanSummaryClick: function (events) {

        window.location.href = "/Employee/" + $('#IssuerCode').val();
    },

    onBackToMissingPortfolioClick: function (events) {
        window.location.href = "/";
    },

    onMissingNextClick: function (events) {
        var missingTax = null;
        var missingEcomm = null;
        if ($('#IsMissingTaxDetailsEnabled').val() != undefined && $('#IsMissingTaxDetailsEnabled').val().toLowerCase() == "true")
            missingTax = true;
        if ($('#IsMissingCommunicationDetailsEnabled').val() != undefined && $('#IsMissingCommunicationDetailsEnabled').val().toLowerCase() == "true")
            missingEcomm = true;
        if ($('#IsMissingFATCACRSEnabled').val() != undefined && $('#IsMissingFATCACRSEnabled').val().toLowerCase() == "true")
            missingFATCA = true;
        if (missingTax || missingEcomm || missingFATCA) {
            IC.Public.showLoading("");
            $.ajax({
                url: OC.MVC.util.getLink("UpdateTaxDetails", "CheckForMissingTaxDetailsAgain"),
                type: 'POST',
                success: function (result) {
                    if (missingTax) {
                        var urlTax = "/UpdateTaxDetails/GetTaxDetails?isRedirectedFromLoginPage=" + true;
                        window.location.href = urlTax;
                    } else
                        if (missingEcomm) {
                            var urlComm = "/CommunicationsPreferences/Preferences?isRedirectedFromLoginPage=" + true;
                            window.location.href = urlComm;

                        }
                        else if (missingFATCA) {
                            var urlFATCA = "/ForeignTaxResidency/ForeignTaxResidencyCertificationIndex?isRedirectedFromLoginPage=" + true;
                            window.location.href = urlFATCA;
                        }
                        else {
                            if ($('#IsEmployeeLogin').val().toLowerCase() == "true") {
                                window.location.href = "/Summary/Summary?isRedirectedFromLoginPage=" + true;
                            } else {
                                var urlHoldings = "/";
                                window.location.href = urlHoldings;
                            }
                        }
                    IC.Public.hideLoading();
                },
                error: function (err) {

                    $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                    IC.Public.hideLoading("");
                }
            });
        }
    },

    onBackToAuthorisedPortfolioClick: function (events) {
        var url;
        $.ajax({
            url: OC.MVC.util.getLink("Authorisation/Authorisation", "PortfolioUserRedirect"),
            type: 'POST',
            async: false,
            success: function (result) {
                url = result;
            },
            error: function (err) {
                url = '';
            }
        });
        if (url != '') {
            window.location = url + '&redirectToAuthorisedPortfolio=true';
        }
    },
    init: function () {
        $("#btnBackToPortfolio").bind("click", { obj: this }, this.onBackToPortfolioClick);
        $("#btnBackToMissingPortfolio").bind("click", { obj: this }, this.onBackToMissingPortfolioClick);
        $("#btnBackToHoldingDetails").bind("click", { obj: this }, this.onBackToHoldingDetailsClick);
        $("#btnBackToPlanSummary").bind("click", { obj: this }, this.onBackToPlanSummaryClick);
        $("#btnBackToAuthorisedPortfolio").bind("click", { obj: this }, this.onBackToAuthorisedPortfolioClick);
        var btnMissingNext = $("#btnMissingNext");
        btnMissingNext.bind("click", { obj: this }, this.onMissingNextClick);
    }
};
;IC.Public.SmsPinbacktoportfolio = function (options) {
};

IC.Public.SmsPinbacktoportfolio.prototype = {
    onBackToPortfolioClick: function (events) {
        var urlHoldings = "/";
        window.location.href = urlHoldings;
        var url = OC.MVC.util.getLink("Holdings", "Index");
        OC.MVC.util.loadMainContainerView(url);
    },
    
    init: function () {
        $("#btnBackToPortfolio").bind("click", { obj: this }, this.onBackToPortfolioClick);
       
    }
};
;IC.Public.BuyBack = function (options) {
    if (options) {
        $.extend(this, options);
    }
};

IC.Public.BuyBack.prototype = {

    init: function () {
        var btnCancel = $("#btnCancel");
        btnCancel.click({ obj: this }, this.onCancelClick);

        var btnContinue = $("#btnConfirm");
        btnContinue.click({ obj: this }, this.onContinueClick);

        var btnWithdraw = $("#btnWithdraw");
        btnWithdraw.click({
            obj: this
        }, this.onWithdrawClick);

        var acceptTenderInstructionButton = $("#AcceptTenderInstruction");
        var acceptEmailUpdateButton = $("#AcceptEmailUpdate");

        if (acceptTenderInstructionButton.length == 0 || acceptTenderInstructionButton.is(":checked")) {
            btnContinue.parent().removeClass("disabled");
            btnContinue.removeAttr("disabled");
        } else {
            btnContinue.parent().addClass("disabled");
            btnContinue.attr("disabled", "disabled");
        }

        acceptEmailUpdateButton.click(function () {
            if ($(this).is(":checked") && acceptTenderInstructionButton.is(":checked")) {
                btnContinue.parent().removeClass("disabled");
                btnContinue.removeAttr("disabled");
            } else {
                btnContinue.parent().addClass("disabled");
                btnContinue.attr("disabled", "disabled");
            }
        });

        acceptTenderInstructionButton.click(function () {
            if ($(this).is(":checked") && (acceptEmailUpdateButton.length == 0 || acceptEmailUpdateButton.is(":checked"))) {
                btnContinue.parent().removeClass("disabled");
                btnContinue.removeAttr("disabled");
            } else {
                btnContinue.parent().addClass("disabled");
                btnContinue.attr("disabled", "disabled");
            }
        });

        if (acceptTenderInstructionButton.is(":checked") == false ||
            (acceptEmailUpdateButton.length == 0 || acceptEmailUpdateButton.is(":checked")) == false) {
            btnContinue.parent().addClass("disabled");
            btnContinue.attr("disabled", "disabled");
        }

        /* for less than min shares */
        var tenderAllFinalControl = $("#TenderAllSharesAsFinalPrice");
        tenderAllFinalControl.click(function () {
            var value = this.value;
            if (value) {
                $("input[name=TenderShareWithDiscount]").prop("checked", false);
            }
        });

        $("input[name=TenderShareWithDiscount]").change(function () {
            $("#TenderAllSharesAsFinalPrice").prop("checked", false);
        });

        /* for over min shares */
        $("#ShareUnitToTenderAsFinalPrice").blur(this.calculateTotalTender);
        $(".tenderShareUnitWithDiscount input[type=text]").blur(this.calculateTotalTender);

        if ($("#ShareUnitToTenderAsFinalPrice")) {
            this.calculateTotalTender();
        };

        $(".minimumPrice").change(function (src) {
            $(".minimumPrice").each(function () {
                if (this != src.target)
                    $(this).attr("checked", false);
            });
        });

        $("input[name='PaymentInstruction.PaymentType']:radio").change(function () {
            IC.Public.BuyBack.prototype.showHideNzSpecific(this);
        });

        this.showHideNzSpecific($("input[name='PaymentInstruction.PaymentType']:checked")[0]);
    },

    showHideNzSpecific:function(src) {
        var selectedPaymentType = src.value;
        if (selectedPaymentType == "NZL") {
            $("#nzSpecific").show();
        }
        else {
            $("#nzSpecific").hide();
        }
    },
    calculateTotalTender: function (src) {
        var totalSharesTenderedControl = $("#TotalSharesTendered");
        var shareUnitToTenderAsFinalPriceControl = $("#ShareUnitToTenderAsFinalPrice");
        var total = 0;
        if (!isNaN(shareUnitToTenderAsFinalPriceControl.val()))
            total += Number(shareUnitToTenderAsFinalPriceControl.val());
        $(".tenderShareUnitWithDiscount input[type=text]").each(function () {
            if (!isNaN($(this).val()))
                total += Number($(this).val());
        });
        totalSharesTenderedControl.val(total);
    },

    onCancelClick: function (events) {

        var self = events.data.obj;
        if (self.urlReferrer) {
            window.location.href = self.urlReferrer;
            return;
        }

        var url = self.attr("urlreferrer");
        if (url && url.indexOf(window.location.hostname) == -1) {
            window.location.href = url;
            return;
        }

        window.location.href = "/";
    },

    onContinueClick: function () {
        var url = OC.MVC.util.getLink("BuyBack", "Confirm");
        IC.Public.showLoading("");
        $("#btnConfirm").attr("disabled", "disabled");
        $("#btnConfirm").parent().addClass("disabled");
        $.ajax({
            url: url,
            type: 'POST',
            data: $("#frmBuyBack").serialize(),
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
                $("#btnConfirm").removeAttr("disabled");
                $("#btnConfirm").parent().removeClass("disabled");
            },
            error: function (err) {
                ("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
                $("#btnConfirm").removeAttr("disabled");
                $("#btnConfirm").parent().removeClass("disabled");
            }
        });
    },

    onWithdrawClick: function () {
        var url = OC.MVC.util.getLink("BuyBack", "ConfirmWithdrawal");
        IC.Public.showLoading("");

        $.ajax({
            url: url,
            type: 'POST',
            data: $("#frmBuyBack").serialize(),
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                ("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
    }
};IC.Public.BuyBackAmendment = function (options) {
    if (options) {
        $.extend(this, options);
    }
};

IC.Public.BuyBackAmendment.prototype = {

    init: function () {
        var btnCancel = $("#btnCancel");
        btnCancel.click({ obj: this }, this.onCancelClick);

        var btnContinue = $("#btnConfirm");
        btnContinue.click({ obj: this }, this.onContinueClick);

        var btnWithdraw = $("#btnWithdraw");
        btnWithdraw.click({
        obj: this }, this.onWithdrawClick);

    },

    
    onCancelClick: function (events) {

        var self = events.data.obj;
        if (self.urlReferrer) {
            window.location.href = self.urlReferrer;
            return;
        }

        var url = self.attr("urlreferrer");
        if (url && url.indexOf(window.location.hostname) == -1) {
            window.location.href = url;
            return;
        }

        window.location.href = "/";            
    },

    onContinueClick: function() {
        var url = OC.MVC.util.getLink("BuyBack", "ConfirmAmendment");
        IC.Public.showLoading("");

        $.ajax({
            url: url,
            type: 'POST',
            data: $("#frmBuyBack").serialize(),
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                ("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },

    onWithdrawClick: function () {
        var url = OC.MVC.util.getLink("BuyBack", "ConfirmWithdrawal");
        IC.Public.showLoading("");

        $.ajax({
            url: url,
            type: 'POST',
            data: $("#frmBuyBack").serialize(),
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                ("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
    }
};IC.Public.BuyBackConfirmation = function (options) {
    if (options) {
        $.extend(this, options);
    }
};

IC.Public.BuyBackConfirmation.prototype = {

    init: function () {
        var btnCancel = $("#btnCancel");
        btnCancel.bind("click", { obj: this }, this.onCancelClick);

        var btnContinue = $("#btnConfirm");
        btnContinue.bind("click", { obj: this }, this.onContinueClick);
    },
    
    onCancelClick: function () {

        var url = OC.MVC.util.getLink("BuyBack", "Index");
        IC.Public.showLoading("");
        $.post(url, $("#frmBuyBack").serialize(), function (data) {
            $("#" + OC.MVC.constants.mainContainer).html(data);
            IC.Public.hideLoading();
        });
    },
    onContinueClick: function () {
        $("#btnConfirm").attr("disabled", "disabled");
        $("#btnConfirm").parent().addClass("disabled");
        var url = OC.MVC.util.getLink("BuyBack", "Submit");
        IC.Public.showLoading("");
        $.post(url, $("#frmBuyBack").serialize(), function (data) {
            $("#" + OC.MVC.constants.mainContainer).html(data);
            IC.Public.hideLoading();
            $("#btnConfirm").removeAttr("disabled");
            $("#btnConfirm").parent().removeClass("disabled");
        });
    }
};IC.Public.BuyBackReceipt = function (options) {
    if (options) {
        $.extend(this, options);
    }
};

IC.Public.BuyBackReceipt.prototype = {

    init: function () {
        var btnDone = $("#btnDone");
        btnDone.bind("click", { obj: this }, this.onDoneClick);

        var lnkDownloadReceipt = $("#lnkDownloadReceipt");
        lnkDownloadReceipt.bind("click", { obj: this }, this.onDownloadClick);
    },

    onDoneClick: function (events) {
        $("#btnDone").attr("disabled", "disabled");
        $("#btnDone").parent().addClass("disabled");
        var self = events.data.obj;
        if (self.urlReferrer) {
                window.location.href = self.urlReferrer;
            return;
        }

        var url = OC.MVC.util.getLink("BuyBack", "Index");
        window.location.href = url;
    },

    onDownloadClick: function () {
        var url = OC.MVC.util.getLink("BuyBack", "DownloadBuyBackTransactionReceipt");
        IC.Public.showLoading("");
        $.post(url, $("#frmBuyBack").serialize(), function (data) {
            $("#" + OC.MVC.constants.mainContainer).html(data);
            IC.Public.hideLoading();
        });
    }
};//*******************************************************************************************************
//  
//  File:        ic.public.holdingFilter.js
//  Author:      Vladimir Nechypurenko
//  Description: HoldingFilter control JS
//  Review:      26/10/2010 Vladimir Nechypurenko
//               - Filter re-design
//               03/03/2011 Vladimir Nechypurenko
//               - Hide the Securities list for all cases. It is not used anymore.
// 
//*******************************************************************************************************

IC.Controls.holdingFilter = function(options) {
    if (options) {
        $.extend(this, options);
        this.isUpdateSecurityCodes = options.isUpdateSecurityCodes || false;
    }
};

//
// Class has several events, which can be used in other pages:
//  - applyHoldingFilter - event to be used, when required to refresh the data on the page
//  - cancelHoldingFilter - event to be used, when filter data is reset
//  - holdingSelect - event to be used, when Holding record is selected
//
IC.Controls.holdingFilter.prototype = {
    holdingListName: 'ViewKey',
    isUpdateSecurityCodes: false,
    onSaveFilter: function() {
        if (!this.isLoading) {
            var url = OC.MVC.util.getLink("HoldingFilter", "Save");
            var data = this.form.serializeObject();
            var _obj = this;
            $.ajax({
                url: url,
                dataType: 'json',
                type: 'POST',
                data: data,
                success: function(result) {
                    if (result && result.Status == "SUCCESS") {
                        _obj.form.trigger('applyHoldingFilter', data.ViewKey);
                    } else {
                        if(result && (result.ErrorMessage != undefined || result.ErrorMessage != ""))
                        alert(result.ErrorMessage);
                    }
                },
                error: function(err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },
    loadCustomView: function(viewKey) {
        var _obj = this;
        var cvkeys = viewKey ? viewKey.split(',') : '';
        if (cvkeys.length >= 2) {
            if (cvkeys[1] == "-1") {
                var url = OC.MVC.util.getLink("HoldingFilter", "RenderCustomFilter");
                OC.MVC.util.loadMainContainerView(url);
            } else {
                _obj.onSaveFilter();
                //_obj.securityList.hide();
                _obj.editBtn.show();
                //_obj.form.trigger('applyHoldingFilter', viewKey);
            }
        }
    },
    onHoldingListSelect: function(events) {
        var _obj = events.data.obj;
        var value = $(this).val();
        _obj.gaIssuerCode.val(IC.Public.getIssuerCode(value));
        if (value == "-1") { //All Holdings are selected
            _obj.btnBackHoldingDetails.hide();
            //_obj.securityList.hide();
            _obj.editBtn.hide();
            _obj.moreRight.hide();
            _obj.form.trigger('cancelHoldingFilter');
            if (!_obj.isLoading) {
                _obj.onSaveFilter();
            }
        } else if (value.substring(0, 3) == "CV0") { //Custom selection
            _obj.btnBackHoldingDetails.hide();
            if (!_obj.isLoading) {
                _obj.loadCustomView(value);
            }
        } else if (value.substring(0, 2) == "CV") { //Saved custom selection
            _obj.btnBackHoldingDetails.hide();
            //_obj.securityList.hide();
            _obj.editBtn.show();
            if (!_obj.isLoading) {
                _obj.onSaveFilter();
            }
        } else {
            _obj.btnBackHoldingDetails.show();
            _obj.editBtn.hide();
            if (!_obj.isLoading) {
                _obj.onSaveFilter();
                setTimeout(function() {
                    _obj.form.trigger('holdingSelect', value);
                }, 1);

            }
        }
    },
    onEditClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("HoldingFilter", "RenderCustomFilter");
        var data = { viewKey: _obj.holdingList.val() };
        OC.MVC.util.loadMainContainerView(url, data);
    },

    onBackHoldingDetailsClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("HoldingDetails", "Index");
        var data = { key: _obj.holdingList.val() };
        OC.MVC.util.loadMainContainerView(url, data);
    },

    btnBackPlanSummaryClick: function (events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("Summary", "Summary");
        var data = { key: _obj.holdingList.val() };
        OC.MVC.util.loadMainContainerView(url, data);
    },

    btnBackPortfolioClick: function (events) {
            var _obj = events.data.obj;
            var url = OC.MVC.util.getLink("Holdings", "Index");
            var data = { key: _obj.holdingList.val() };
            OC.MVC.util.loadMainContainerView(url, data);
            },

    init: function() {
        this.isLoading = true;
        this.editBtn = $("#btnEditCustomFilter");
        this.form = $("form.holdingFilterForm");
        this.gaIssuerCode = $("#gaIssuerCode");

        this.btnBackHoldingDetails = $("#btnBackHoldingDetails");
        if (this.btnBackHoldingDetails.length > 0) {
            this.btnBackHoldingDetails.unbind("click");
            this.btnBackHoldingDetails.bind("click", { obj: this }, this.onBackHoldingDetailsClick);
        }

        this.btnBackPlanSummary = $("#btnBackPlanSummary");
        if (this.btnBackPlanSummary.length > 0) {
            this.btnBackPlanSummary.unbind("click");
            this.btnBackPlanSummary.bind("click", { obj: this }, this.btnBackPlanSummaryClick);
        }
        this.btnBackPortfolio = $("#btnBackPortfolio");
        if (this.btnBackPortfolio.length > 0) {
            this.btnBackPortfolio.unbind("click");
            this.btnBackPortfolio.bind("click", { obj: this }, this.btnBackPortfolioClick);
        }
        this.holdingList = $("select[name='" + this.holdingListName + "']");
        this.gaIssuerCode.val(IC.Public.getIssuerCode(this.holdingList.val()));

        this.moreRight = $("span.moreRight");

        this.holdingList.unbind("change");
        this.holdingList.bind("change", { obj: this }, this.onHoldingListSelect);

        this.holdingList.change();

        if (this.editBtn.length > 0) {
            this.editBtn.bind("click", { obj: this }, this.onEditClick);
        }
        this.isLoading = false;
    }
};

$.fn.getHoldingFilterData = function(options) {
    var filterData = {};
    this.each(function() {
        $.extend(filterData, $(this).serializeObject());
    });
    return filterData;
};

;//*******************************************************************************************************
//
//  File:        ic.public.planBackToLinks.js
//  Author:      Philip Lee
//  Description: planBackToLinks control JS
//  Review:      
//               
// 
//*******************************************************************************************************

IC.Controls.planBackToLinks = function (options) {
    $.extend(this, options);
    this.init();
};

IC.Controls.planBackToLinks.prototype = {
    init: function() {
        this.btnBackToAllPlans = $("#btnBackToAllPlans");
        if (this.btnBackToAllPlans.length > 0) {
            this.btnBackToAllPlans.unbind("click");
            this.btnBackToAllPlans.bind("click", null, this.onbtnBackToAllPlansClick);
        }

        this.btnBackToPlan = $("#btnBackToPlan");
        if (this.btnBackToPlan.length > 0) {
            this.btnBackToPlan.unbind("click");
            this.btnBackToPlan.bind("click", { viewKey: this.btnBackToPlan.attr('viewKey') }, this.onbtnBackToPlanClick);
        }
    },
    onbtnBackToAllPlansClick: function (events) {
        var url = OC.MVC.util.getLink("Summary", "Summary");
        OC.MVC.util.loadMainContainerView(url, null);
    },
    onbtnBackToPlanClick: function (events) {
        var viewKey = events.data.viewKey;
        var url = OC.MVC.util.getLink("Summary", "PlanDetail");
        var data = { viewKey: viewKey };
        OC.MVC.util.loadMainContainerView(url, data);
    }
};
;//*******************************************************************************************************
//  
//  File:        ic.controls.customView.js
//  Author:      Vladimir Nechypurenko
//  Description: Custom view JS for Custom view page
//  Review:      
//
//*******************************************************************************************************

IC.Controls.customView = function(options) {
    if (options) {
    }
};

IC.Controls.customView.prototype = {
    onDoneClick: function(events) {
        var _obj = events.data.obj;
        var data = _obj.customFilterForm.serializeObject();
        if (data) {
            data.FilterValues = _obj.customerFilterContent.find("input[type=checkbox]:checked").map(function() {
                return $(this).val();
            }).get().join(',');
            var url = OC.MVC.util.getLink("HoldingFilter", "SaveCustomView");
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function(result) {
                    if (result && result.Status == "SUCCESS") {
                        history.back();
                    }
                    else if (result) {
                        alert("Error on adding of a new Custom View: " + result.ErrorMessage);
                    }
                    IC.Public.hideLoading();
                },
                error: function(err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },
    onFilterTypeSelect: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("HoldingFilter", "GetFilterTypesList");

        var data = {
            filterType: $(this).val(),
            viewKey: _obj.viewKey.val(),
            filterName: _obj.filterName.val()
        };

        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function(result) {
                _obj.customerFilterContent.html(result);
            }
        });
    },
    onDeleteClick: function(events) {
        var _obj = events.data.obj;
        var confirm_result = confirm("Are you sure to delete the selected custom filter?");
        if (confirm_result) {
            var data = {
                viewKey: _obj.viewKey.val()
            };
            var url = OC.MVC.util.getLink("HoldingFilter", "Delete");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function(result) {
                    if (result && result.Status == "SUCCESS") {
                        history.back();
                    }
                    else if (result) {
                        alert("Error on Custom View delete: " + result.ErrorMessage);
                    }
                }
            });
        }
    },
    init: function() {
        this.customFilterForm = $("form#customFilterForm");
        this.customerFilterContent = $("#customerFilterContent");
        this.btnBack = $('#btnBack');
        this.btnBack.bind("click", function() { history.back(); });
        this.filterName = $("input[name=FilterName]");
        this.viewKey = $("input[name=ViewKey]");

        this.btnDeleteCustomView = $("#btnDeleteCustomView");
        this.btnDeleteCustomView.bind("click", { obj: this }, this.onDeleteClick);

        this.btnDone = $('#btnDone');
        this.btnDone.bind("click", { obj: this }, this.onDoneClick);

        this.filterType = $("select[name=FilterType]");
        this.filterType.bind("change", { obj: this }, this.onFilterTypeSelect);
        this.filterType.trigger("change");
    }
};//*******************************************************************************************************
//  
//  File:        ic.controls.publicLinks.js
//  Author:      Vladimir Nechypurenko
//  Description: Functions for Public links: Contact Us, Settings, Sitemap
//
//*******************************************************************************************************

IC.Controls.publicLinks = function(options) {
    if (options) {
        this.menuId = options.menuId;
    }
    this.init();
};

IC.Controls.publicLinks.prototype = {
    onLinkClick: function() {
        var container = $(this).attr("container");
        var url = $(this).attr("actionUrl");

        if (container && url) {
            IC.Public.showLoading("");

            OC.MVC.util.loadView(container, url);
        }
    },

    onRegistryMenuLinkClick: function(events) {
        var _obj = events.data.obj;
        clearTimeout(_obj.hoverEvent);
        var _div = $(".registryMenuDiv");
        var _position = $(this).offset();
        _div.css("left", (_position.left + $(this).width() - _div.width() + 12) + "px");
        if ((_position.top + $(this).height() + 24 + _div.height() + 72 > $(document).height())
            || (_position.top + $(this).height() + 24 + _div.height() - $(document).scrollTop() > $(window).height())) {
            _div.css("top", (_position.top - _div.height() - 24) + "px");
        } else {
            _div.css("top", (_position.top + $(this).height() + 2) + "px");
        }
        _div.css("visibility", "visible");
        _div.find("a").unbind("click");
        _div.find("a").bind("click", { obj: _obj }, _obj.onRegistryLinkClick);
        $("body").bind("mouseup", { obj: this }, _obj.onRegistryMenuLinkBlur);
    },

    onRegistryMenuLinkBlur: function(events) {
        var _obj = events.data.obj;
        $("body").unbind("mouseup", _obj.onShortcutBlur);
        _obj.hoverEvent = setTimeout(function() {
            var _div = $(".registryMenuDiv");
            _div.css("visibility", "hidden");
        }, 150);
    },

    onRegistryLinkClick: function() {
        if (!OC.MVC.history.isLoading) {
            var container = $(this).attr("container");

            var url = "";
            if (container) {
               url = $(this).attr("actionUrl");
            }

            var data = { registryCode: $(this).attr("registryCode") };

            if (url) {
                IC.Public.showLoading("");
                $.ajax({
                    url: url,
                    type: 'POST',
                    data: data,
                    success: function(result) {
                        IC.Public.hideLoading();
                        LockBox.set("lastUrl", OC.MVC.util.getLink("Holdings", "Index"));
                        if (result.Success && result.Message === "ThirdParty") {
                            window.location.href = result.ActionUrl;
                        }
                        else if (result.Success && result.Message === "NonThirdParty") {
                            window.location.reload();
                        }
                        else if (!result.Success) {
                            IC.Public.hideLoading();
                        }
                    },
                    error: function(err) {
                        IC.Public.hideLoading();
                    }
                });
            }
       }
    },

    onRegistryVoteMenuLinkClick: function(events) {
        var _obj = events.data.obj;
        clearTimeout(_obj.hoverEvent);
        var _div = $(".registryMenuDiv");
        var _position = $(this).offset();
        _div.css("left", (_position.left + $(this).width() - _div.width() + 12) + "px");
        if ((_position.top + $(this).height() + 24 + _div.height() + 72 > $(document).height())
            || (_position.top + $(this).height() + 24 + _div.height() - $(document).scrollTop() > $(window).height())) {
            _div.css("top", (_position.top - _div.height() - 24) + "px");
        } else {
            _div.css("top", (_position.top + $(this).height() + 2) + "px");
        }
        _div.css("visibility", "visible");
        _div.find("a").unbind("click");
        _div.find("a").bind("click", { obj: _obj }, _obj.onRegistryVoteLinkClick);
        $("body").bind("mouseup", { obj: this }, _obj.onRegistryMenuLinkBlur);
    },

    onRegistryVoteLinkClick: function(events) {
        if (!OC.MVC.history.isLoading) {
            var container = $(this).attr("container");
            if (container) {
                var url = $(this).attr("actionUrl");

                if (url) {
                    IC.Public.showLoading("");
                    setTimeout(function() {
                        if (url[0] != "/")
                            url = window.location.protocol + "//" + url;
                        location.href = url;
                    }, 100);
                }
            }
        }
    },

    onPortfolioMenuLinkClick: function (events) {
        var _obj = events.data.obj;
        clearTimeout(_obj.hoverEvent);
        var _div = $(".portfolioMenuDiv");
        var _position = $(this).offset();
        _div.css("left", (_position.left + $(this).width() - _div.width() + 12) + "px");
        if ((_position.top + $(this).height() + 24 + _div.height() + 72 > $(document).height())
            || (_position.top + $(this).height() + 24 + _div.height() - $(document).scrollTop() > $(window).height())) {
            _div.css("top", (_position.top - _div.height() - 24) + "px");
        } else {
            _div.css("top", (_position.top + $(this).height() + 2) + "px");
        }
        _div.css("visibility", "visible");
        _div.find("a").unbind("click");
        _div.find("a").bind("click", { obj: _obj }, _obj.onPortfolioLinkClick);
        $("body").bind("mouseup", { obj: this }, _obj.onPortfolioMenuLinkBlur);
    },

    onPortfolioMenuLinkBlur: function (events) {
        var _obj = events.data.obj;
        $("body").unbind("mouseup", _obj.onShortcutBlur);
        _obj.hoverEvent = setTimeout(function () {
            var _div = $(".portfolioMenuDiv");
            _div.css("visibility", "hidden");
        }, 150);
    },

    onPortfolioLinkClick: function () {
        if (!OC.MVC.history.isLoading) {
            var container = $(this).attr("container");

            var url = "";
            if (container) {
                url = $(this).attr("actionUrl");
            }
            var parameters = {};
            if ($(this).attr("type") != undefined && $(this).attr("type").toLowerCase() == "authorisedportfolio") {
                $("#mainMenu").css("visibility", "hidden");
                $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
            } else {
                $("#mainMenu").css("visibility", "visible");
                $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
            }

            if (!url) {
                url = OC.MVC.util.getLink("Authorisation/Authorisation", "PortfolioUserRedirect");
                parameters = { redirectToAuthorisedPortfolio: false };
                if ($(this).attr("type").toLowerCase() == "authorisedportfolio") {
                    parameters = { redirectToAuthorisedPortfolio: true };
                }
            }
            var result;
            $.ajax({
                type: "POST",
                url: url,
                data: parameters,
                async: false,
                success: function(data) { result = data; },
                error: function() { result = ''; }
            });

            if (result != '') {
                window.location = result;
            }
        }
    },
    init: function() {
        $("#" + this.menuId + " a").bind("click", this.onLinkClick);
        OC.MVC.history.isLoading = false;

        var registryMenuLink = $("#registryMenuLink");
        registryMenuLink.unbind("click");
        registryMenuLink.bind("click", { obj: this }, this.onRegistryMenuLinkClick);
        registryMenuLink.bind("blur", { obj: this }, this.onRegistryMenuLinkBlur);

        var portfolioMenuLink = $("#portfolioMenuLink");
        portfolioMenuLink.unbind("click");
        portfolioMenuLink.bind("click", { obj: this }, this.onPortfolioMenuLinkClick);
        portfolioMenuLink.bind("blur", { obj: this }, this.onPortfolioMenuLinkBlur);

        var registryVoteMenuLink = $("#registryVoteMenuLink");
        registryVoteMenuLink.unbind("click");
        registryVoteMenuLink.bind("click", { obj: this }, this.onRegistryVoteMenuLinkClick);
        registryVoteMenuLink.bind("blur", { obj: this }, this.onRegistryMenuLinkBlur);

    }
};


;IC.Controls.currency = function (options) {
    if (options) {
        $.extend(this, options);
    }
};

IC.Controls.currency.prototype = {
    init: function() {
        var self = this;
        self.currencyDropdown = $('#conversionCurrency');
        self.currencyDropdown.unbind('change');
        self.currencyDropdown.bind('change', { obj: this }, self.onCurrencyChange);

//        self.disclaimerDiv = $('#disclaimerDiv');
//        self.currencyLabel = $('#currencyLabel');
//        self.currencyLabel.unbind('mouseenter').bind('mouseenter', function () {
//            self.disclaimerDiv.show();
//        });
//        self.currencyLabel.unbind('mouseleave').bind('mouseleave', function () {
//            self.disclaimerDiv.hide();
//        });

        //        self.disclaimerDiv.hide();

        //$('#currencyLabel').cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 250 });
    },
    
    // triggers applyCurrencyChange event
    onCurrencyChange: function (eventArgs) {
        var self = eventArgs.data.obj;
        var currencyCode = $(this).val();
        if (currencyCode != "-1") {
            self.saveSettings(currencyCode);
            // Hide the pre-opened cluetips
            $("#cluetip").hide();
        }
    },

    saveSettings: function (currencyCode) {
        var self = this;
        IC.Public.showLoading("");
        $.ajax({       
            url: OC.MVC.util.getLink("Currency", "SaveSettings"),
            type: 'POST',
            data: { currencyCode: currencyCode },
            success: function (result) {
                IC.Public.hideLoading();
                if (result && result.IsSuccess) {
                    //self.currencyDropdown.trigger('applyCurrencyChange', currencyCode);
                    // Fix Antiforgery Token issues
                    $.ajax({
                        url: OC.MVC.util.getLink("General", "RefreshToken"),
                        async: false,
                        type: "GET",
                        success: function (result) {
                            $("input[name='__RequestVerificationToken']").remove();
                            $('body').append(result);
                            IC.Public.hideLoading();
                            self.currencyDropdown.trigger('applyCurrencyChange', currencyCode);
                            // Hide the pre-opened cluetips
                            $("#cluetip").hide();
                        }
                    });
                } else if(result.ErrorMessage) {
                    alert(result.ErrorMessage);
                }else {
                    //leave it, it has the redirect.
                }
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    }
};;//*******************************************************************************************************
//  
//  File:        ic.public.holdingDetails.js
//  Author:      Vladimir Nechypurenko
//  Description: JS for Holding details page
//  Review:      
//
//*******************************************************************************************************

IC.Public.affirmHoldingDetails = function(options) {
    if (options) {
    }
}

IC.Public.affirmHoldingDetails.prototype = {
    onTransactionHistoryClick: function() {
        var url = OC.MVC.util.getLink("Holdings", "TransactionsHistory");
        OC.MVC.util.loadMainContainerView(url);
    },
    onPaymentHistoryClick: function() {
        var url = OC.MVC.util.getLink("PaymentHistory", "Index");
        OC.MVC.util.loadMainContainerView(url);
    },
    init: function() {
        this.isLoading = true;
        highlightMenu('home');

        $('button[id^="btnTransactionHistory"]').bind("click", { obj: this }, this.onTransactionHistoryClick);
        $("#btnPaymentHistory").bind("click", { obj: this }, this.onPaymentHistoryClick);

        this.isLoading = false;
    }
}
;//*******************************************************************************************************
//  
//  File:        ic.public.holdingsList.js
//  Author:      Vladimir Nechypurenko
//  Description: JS to attach format or events for the Holdings grid
//  Review:      
//
//*******************************************************************************************************

IC.Public.holdingsList = {
    holdingActionColumn: function (cellValue, options, rowObject) {
        var _this = IC.Public.holdingsList;
        var div = $('<div>');
        var holderKey = '';
        var isVotingAvailable = false;
        var isAgmQuestionsAvailable = false;
        var isEmployeePlanRow = false;

        var index = $(this).getColIndex("HolderKey");
        if (index >= 0) { holderKey = rowObject[index]; }


        index = $(this).getColIndex("IsVotingAvailable");
        if (index >= 0) { isVotingAvailable = rowObject[index].toLowerCase() == "true"; }
        index = $(this).getColIndex("IsAgmQuestionsAvailable");
        if (index >= 0) { isAgmQuestionsAvailable = rowObject[index].toLowerCase() == "true"; }
        index = $(this).getColIndex("IsEmployeePlan");
        if (index >= 0) { isEmployeePlanRow = rowObject[index].toLowerCase() == "true"; }

        var a = $('<a>');
        a.attr("href", "javascript:void(0);");
        a.addClass("holdingDetailLink");
        a.attr("key", holderKey);
        a.html("View Details");
        a.attr("isEmployeePlanRow", isEmployeePlanRow)
        a.appendTo(div);

        var shortcuts = $('<a>');
        shortcuts.attr("class", "shortcuts");
        shortcuts.attr("style", "padding-left: 5px;");
        shortcuts.attr("href", "javascript:void(0);");
        shortcuts.html("Shortcuts<small>&#9660;</small>");
        shortcuts.attr("key", holderKey);
        shortcuts.attr("isVotingAvailable", isVotingAvailable);
        shortcuts.attr("isAgmQuestionsAvailable", isAgmQuestionsAvailable);
        shortcuts.attr("isEmployeePlanRow", isEmployeePlanRow);
        shortcuts.appendTo(div);

        return div.html();
    },
    selfTooltip: function (cellValue, options, rowObject) {
        var div = $('<div>');
        var span = $('<span>');
        span.attr("title", cellValue);
        span.addClass("tooltip");
        span.html(cellValue.replace(/\r\n/g, '<br/>'));
        span.appendTo(div);
        return div.html();
    },
    paymentStatusTooltip: function (cellValue, options, rowObject) {
        var div = $('<div>');
        var span = $('<span>');
        span.addClass("tooltip");

        // Get Replacement Status and Replacement Date
        var replacementStatus;
        var replacementStatusIndex = $(this).getColIndex("ReplacementStatus");
        if (replacementStatusIndex >= 0) {
            replacementStatus = rowObject[replacementStatusIndex];
        }
        var replacementDate;
        var replacementDateIndex = $(this).getColIndex("ReplacementDate");
        if (replacementDateIndex >= 0) {
            replacementDate = rowObject[replacementDateIndex];
        }
        
        if (replacementStatus != undefined && replacementStatus.trim() != '') {
            var toolTip = replacementStatus;
            // If Replacement Date exists -> Display Replacement Status and Replacement Date
            if (replacementDate != undefined && replacementDate.trim() != '') {
                toolTip += "\r\n" + "Replaced on " + replacementDate;
            }
            span.attr("title", toolTip);
        } else {
            span.attr("title", cellValue);
        }

        span.html(cellValue.replace(/\r\n/g, '<br/>'));
        span.appendTo(div);
        return div.html();
    },
    meetingNameTooltip: function (cellValue, options, rowObject) {
        var div = $('<div>');
        var span = $('<span>');
        span.attr("title", cellValue);
        span.addClass("tooltip");
        if (cellValue.length > 28) {
            cellValue = cellValue.substring(0, 25) + "...";
        }
        span.html(cellValue);
        span.appendTo(div);
        return div.html();
    },
    securityCodeTooltip: function (cellValue, options, rowObject) {
        var _this = IC.Public.holdingsList;
        var div = $('<div>');
        var span = $('<span>');
        var index = $(this).getColIndex("IssuerName");
        var index2 = $(this).getColIndex("SecurityDescription");
        var toolTip = '';

        if (index >= 0) {
            toolTip = toolTip + rowObject[index].trim();
        }
        if (index2 >= 0 && rowObject[index2].trim() != '') {
            toolTip = toolTip + ', ' + rowObject[index2].trim();
        }
        span.attr("title", toolTip.replace('&amp;', '&').replace('&apos;', '\'').replace('&gt;', '>').replace('&lt;', '<'));

        span.addClass("tooltip");
        span.html(cellValue);
        span.appendTo(div);
        return div.html();
    },
    regHolderTooltip: function (cellValue, options, rowObject) {
        var _this = IC.Public.holdingsList;
        var div = $('<div>');
        var span = $('<span>');
        var index = $(this).getColIndex("RegisteredHolderName");
        if (index < 0) {
            index = $(this).getColIndex("Registered&nbsp;Details");
        }

        var element = $('#hidRegHolderNameToolTip');
        var isShowToolTip = 'true';
        if (element.length != 0 && element.val().toLowerCase() == 'true') {
            isShowToolTip = 'false';
        }
        if (isShowToolTip == 'true') {
            if (index >= 0) {
                span.attr("title", rowObject[index].replace('&amp;', '&').replace('&apos;', '\'').replace('&gt;', '>').replace('&lt;', '<'));
            }

            span.addClass("tooltip");
        }
        span.html(cellValue);
        span.appendTo(div);
        return div.html();
    },
    replacedHtml: function (cellValue, options, rowObject) {
        if (cellValue && cellValue != '' && cellValue != 'null') {
            return cellValue.replace('&amp;', '&').replace('&apos;', '\'').replace('&gt;', '>').replace('&lt;', '<');
        }
        return '';
    }

}

;//*******************************************************************************************************
//  
//  File:        ic.public.portfolio.js
//  Author:      Vladimir Nechypurenko
//  Description: JS for Portfolio.aspx page
//  Review:      28/10/2010 - Vladimir Nechypurenko
//               - Add Shortcuts menu
//
//*******************************************************************************************************

IC.Public.portfolioLoadPlansCache = function() {
    $.ajax({
        url: OC.MVC.util.getLink("AsyncCache", "CachePlanTasks"),
        type: 'GET',
        dataType: 'json',
        async: true,
        headers: { "cache-control": "no-cache" },
        cache: false, 
        data: null
    });
};

IC.Public.portfolio = function (options) {
       if (options) {
        this.useGenericLinks = options.useGenericLinks;
        this.hasPlan = options.hasPlan;
        this.hasNewOffers = options.hasNewOffers.toString().toLowerCase() == "true";
        this.hideAvailableOffer = options.hideAvailableOffer;
    }
};
IC.Public.portfolio.prototype = {
    useGenericLinks: false,
    openHoldingDetails: function (viewKey) {
        var data = null;
        if (viewKey) {
            data = { key: viewKey };
        }
        var url = OC.MVC.util.getLink("HoldingDetails", "Index");
        IC.Public.showLoading("");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url, data);
        //IC.Public.hideLoading();
    },

    onBannerHideClick: function (events) {
        $("#divBannerDetails").hide();
        document.cookie = "BannerCookie=IsHidden";
    },
    
    onPwdBannerHideClick: function (events) {
        $("#divPwdExpiryDetails").hide();
    },


    openPlanSummary: function (viewKey) {
        var url = OC.MVC.util.getLink("Summary", "Summary");
        var data = null;
        if (viewKey) {
            data = { viewKey: viewKey };
        }
        OC.MVC.util.loadMainContainerView(url, data);
        //IC.Public.hideLoading();
    },

    onNewHoldingClick: function (events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("Holdings", "Create");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url);
    },
    onHideZeroBalancesClick: function (events) {
        var _obj = events.data.obj;
        document.cookie = "hideZeroBalances=true";
        _obj.holdingsListGrid.setPostData({ isHideZeroBalances: true });
        _obj.holdingsListGrid.trigger("reloadGrid", [{ page: 1}]);
        _obj.btnShowZeroBalances.show();
        $(this).hide();
    },
    onShowZeroBalancesClick: function (events) {
        var _obj = events.data.obj;
        document.cookie = "hideZeroBalances=false";
        _obj.holdingsListGrid.setPostData({ isHideZeroBalances: false });
        _obj.holdingsListGrid.trigger("reloadGrid", [{ page: 1}]);
        _obj.btnHideZeroBalances.show();
        $(this).hide();
    },
    onHoldingDetailsClick: function (events) {
        var _obj = events.data.obj;
        var isEmployeePlanRow = $(this).attr("isEmployeePlanRow") == "true";
        var viewKey = "IHRN," + $(this).attr("key");
        if (isEmployeePlanRow) {
            _obj.openPlanSummary(viewKey);
        }else{
            _obj.openHoldingDetails(viewKey);
        }
    },
    onFilterGrid: function (events, viewKey) {
        var _obj = events.data.obj;
        var filterData = _obj.holdingFilter.getHoldingFilterData();
        if (viewKey == "-1" || viewKey.substring(0, 2) == "CV") {
            $("#viewName").html($("#ViewKey option:selected").text());
            _obj.holdingsListGrid.appendPostData(filterData);
            _obj.holdingsListGrid.trigger('reloadGrid', [{ page: 1}]);
        }
    },
    onEditPortfolioClick: function (events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("Holdings", "EditPortfolio");
        OC.MVC.util.loadMainContainerView(url);
    },
    onShortcutMenuItemClick: function (events) {
        var url = $(this).attr("url");
        var data = null;
        if (events.data.rowId) {
            data = { viewKey: "IHRN," + events.data.rowId };
        }
        OC.MVC.util.loadMainContainerView(url, data);
    },
    onShortcutClick: function (events) {
        var _obj = events.data.obj;
        clearTimeout(_obj.hoverEvent);
        var _div = $(".shortcutsDiv");
        var _position = $(this).offset();
        //Line commented due to position aligning based on screen resolution.
        //_div.css("left", (_position.left + $(this).width() - _div.width() - 14) + "px");
        _div.css("left", (700) + "px");
        if ((_position.top + $(this).height() + 24 + _div.height() + 72 > $(document).height())
            || (_position.top + $(this).height() + 24 + _div.height() - $(document).scrollTop() > $(window).height())) {
            _div.css("top", (_position.top - _div.height() - 24) + "px");
        }
        else {
            _div.css("top", (_position.top + $(this).height() + 2) + "px");
        }
        var isVotingAvailable = $(this).attr("isVotingAvailable") == "true";
        var isAgmQuestionsAvailable = $(this).attr("isAgmQuestionsAvailable") == "true";
        var isEmployeePlanRow = $(this).attr("isEmployeePlanRow") == "true";
        _div.find("a:contains(Voting)").toggle(isVotingAvailable);
        _div.find("a:contains(AGM Questions)").toggle(isVotingAvailable && isAgmQuestionsAvailable);

        _div.find("a:contains(Plan Summary)").toggle(isEmployeePlanRow); //Show/Hide this shortcut based on whether the current row is a employee row or not

        _div.css("visibility", "visible");
        _div.find("a").unbind("click");
        _div.find("a").bind("click", { obj: _obj, rowId: $(this).attr("key") }, _obj.onShortcutMenuItemClick);
        $("body").bind("mouseup", { obj: this }, _obj.onShortcutBlur);
    },
    onShortcutBlur: function (events) {
        var _obj = events.data.obj;
        $("body").unbind("mouseup", _obj.onShortcutBlur);
        _obj.hoverEvent = setTimeout(function () {
            var _div = $(".shortcutsDiv");
            _div.css("visibility", "hidden");
        }, 150);
    },
    onHoldingSelect: function (events, value) {
        //var _obj = events.data.obj;
        //var _value = $(this).find("select[name=ViewKey]").val();
        //var text = $("select option:selected").text().toString();
        
        //if (text.indexOf("*") > -1) {
        //    _obj.openHoldingDetails(value);
        //} else {
        //    _obj.openPlanSummary(value);
        //}

        var self = events.data.obj;
        // TODO: Need to have an attribute in options to check whether it is employee holding
        if ($("#ViewKey option:selected:Contains('Employee Plan')").length > 0) {
            self.openPlanSummary(value);
        } else {
            self.openHoldingDetails(value);
        }
    },

    checkAndDisplayOpenOffers: function () {
        var IshideAvailableOffer = this.hideAvailableOffer;
        if (IshideAvailableOffer != null) {
            if (IshideAvailableOffer.toLowerCase() == "false") {
                var url = OC.MVC.util.getLink("Offers", "OpenOffers");
                OC.MVC.util.showModalByUrl(url, {status: "OPEN", isCaptionRequired: true}, { maxHeight: 360, maxWidth: 500 }, function () {
                    $("#simplemodal-container").addClass("offermodalwrap"); //Fritz required this additional class just for offers.
                    var parentDiv = $('#simplemodal-data').parent();
                    parentDiv.css("overflow", "hidden");
                });
            
        }
    }
        //var self = this;
        //$.ajax({
        //    url: url,
        //    dataType: 'html',
        //    success: function (result) {
        //        if (result.length > 0) {
        //            self.showOpenOffersDialog(result);
        //        }
        //    },
        //    error: function (error) {
        //    }
        //})
    },

    onApplyCurrencyChange: function (eventArgs, currencyCode) {
        var self = eventArgs.data.obj;
        var filterData = self.holdingFilter.getHoldingFilterData();
        self.holdingsListGrid.appendPostData(filterData);
        self.holdingsListGrid.appendPostData({ conversionCurrency: currencyCode });
        self.currencyChanged = true;
        self.holdingsListGrid.trigger('reloadGrid', [{ page: 1 }]);
    },

    setGridColumnCurrencyCodes: function (currencyCode, isCurrencyConversionApplied) {
        var originalCurrencyAndTargetCurrencyAreSame = IC.Public.Currency.originalCurrencyAndTargetCurrencyAreSame(currencyCode);
        currencyCode = (isCurrencyConversionApplied || originalCurrencyAndTargetCurrencyAreSame) ? currencyCode : $('#hidCurrency').val();
        if (currencyCode != "-1") {
            $('#lastClosePriceColumnCurrency').text("(" + currencyCode + ")");
            $('#valueColumnCurrency').text("(" + currencyCode + ")");
        }
    },

   init: function () {
       highlightMenu('home');
       if ($("#portfolioMenuLink").attr("type") != undefined && $("#portfolioMenuLink").attr("type").toLowerCase() == "authorisedportfolio") {
            //$("#mainMenu").css("visibility", "hidden");
            $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
        } else {
            $("#mainMenu").css("visibility", "visible");
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        }

        this.currencyChanged = false;
        this.conversionCurrency = $('#conversionCurrency');
        this.conversionCurrency.unbind('applyCurrencyChange');
        this.conversionCurrency.bind('applyCurrencyChange', { obj: this }, this.onApplyCurrencyChange);

        this.myHoldingsContainer = $("#myHoldingsContainer");
        this.holdingsListGrid = $("#MyHoldingsGrid");
        this.holdingsListGrid.bind("grid_gridComplete", { obj: this }, function (events, gridContext) {
            var _obj = events.data.obj;
            if (_obj.useGenericLinks) {
                $.each($(this).find(".holdingDetailLink"), function (index, value) {
                    var href = OC.MVC.util.getGenericLink("HoldingDetails", "Index", "key=IHRN," + $(value).attr("key"));
                    $(value).attr("href", href);
                });
            }
            else {
                $(this).find(".holdingDetailLink").bind("click", { obj: _obj }, _obj.onHoldingDetailsClick);
            }

            $(this).find(".shortcuts").bind("click", { obj: _obj }, _obj.onShortcutClick);
            $(this).find(".shortcuts").bind("blur", { obj: _obj }, _obj.onShortcutBlur);

            var hasZeroBalanceHoldings = $(this).getUserDataItem('hasZeroBalanceHoldings');
            _obj.btnHideZeroBalances.hide();
            _obj.btnShowZeroBalances.hide();
            if (hasZeroBalanceHoldings !== undefined) {
                if (_obj.holdingsListGrid.getPostDataItem('isHideZeroBalances') || document.cookie.indexOf('hideZeroBalances=true') >= 0) {
                    _obj.btnShowZeroBalances.show();
                }
                else {
                    _obj.btnHideZeroBalances.show();
                }
            }

            var gridTotal = $("#gridTotal");
            gridTotal.show();

            var isCurrencyConversionApplied = IC.Public.Currency.isCurrencyConversionApplied();
            var conversionCurrency = _obj.conversionCurrency.val();
            var currencySymbol = (isCurrencyConversionApplied && conversionCurrency != "-1") ? conversionCurrency : "$";
            var totalValue = $.fmatter.util.NumberFormat($(this).getUserDataItem('totalValue'), { decimalSeparator: ".", thousandsSeparator: ",", decimalPlaces: 2 });
            var defaultCurrency = $('#hidCurrency').val();
            gridTotal.html(IC.Public.Currency.getTotalValueHtml(currencySymbol, totalValue, isCurrencyConversionApplied, defaultCurrency, $('#hidTotalValueCurrencyConversionTooltipMessage').val()));
            _obj.setGridColumnCurrencyCodes(conversionCurrency, isCurrencyConversionApplied);
            if (_obj.currencyChanged) {
                _obj.currencyChanged = false;
                IC.Public.Currency.displayErrorMessageIfFailed(defaultCurrency, $('#hidCurrencyConversionRateNotFoundErrorMessage').val(), _obj.conversionCurrency);
            }
            
            //Cluetooltip is used for Value and Tradeable Balance columns. This code would initialize the cluetooltip
            $("A.cluetooltip").each(function () {
                if ($(this).attr('rel') == ".totalValueTooltip")
                    $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 310, positionBy: 'bottomTop' });
                else if ($(this).hasClass('balanceToolTip'))
                    $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 250 });
                else 
                    $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 310 });
            });

            if (sessionStorage.showOffersModal != null) {
                if (sessionStorage.showOffersModal.toString().toLowerCase() == "true") {
                    if (_obj.hasNewOffers) {
                        //TODO: Uncomment following method call for Offers Modal
                        _obj.checkAndDisplayOpenOffers();

                        sessionStorage.showOffersModal = false;
                    }
                }
            }
            // fire background caching request first
            if (_obj.hasPlan) {
                IC.Public.portfolioLoadPlansCache();
            }
        });

        this.holdingFilter = $("form.holdingFilterForm");
        this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterGrid);
        this.holdingFilter.bind("holdingSelect", { obj: this }, this.onHoldingSelect);

        this.btnNewHolding = $("#btnNewHolding");
        this.btnNewHolding.bind("click", { obj: this }, this.onNewHoldingClick);

        this.btnHideZeroBalances = $("#btnHideZeroBalances");
        this.btnHideZeroBalances.bind("click", { obj: this }, this.onHideZeroBalancesClick);
        this.btnHideZeroBalances.hide();

        this.btnShowZeroBalances = $("#btnShowZeroBalances");
        this.btnShowZeroBalances.bind("click", { obj: this }, this.onShowZeroBalancesClick);
        this.btnShowZeroBalances.hide();

        this.btnEditPortfolio = $("#btnEditPortfolio");
        this.btnEditPortfolio.bind("click", { obj: this }, this.onEditPortfolioClick);

        $("#viewName").html($("#ViewKey option:selected").text());

        $("#lnkClose").bind('click', { obj: this }, this.onBannerHideClick);
        $("#lnkPwdClose").bind('click', { obj: this }, this.onPwdBannerHideClick);
       
   }
    
};
;//*******************************************************************************************************
//  
//  File:        ic.public.editPortfolio.js
//  Author:      Vince Hunt
//  Description: JS for EditPortfolio.aspx page
//  Review:      
//
//*******************************************************************************************************

IC.Public.editPortfolio = function(options) {
    if (options) {
    }
};

IC.Public.editPortfolio.prototype = {
    onRemoveClick: function(events) {
        var data = events.data.obj;
        IC.Public.showLoading("");
        $.ajax({
            url: OC.MVC.util.getLink("Holdings", "ConfirmEditPortfolio"),

            type: 'POST',
            data: data,
            success: function(result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function(err) {
                IC.Public.hideLoading();
                OC.MVC.util.errorMessage(err.responseText); ;
            }
        });
    },
    onNewHoldingClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("Holdings", "Create");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url);
    },    
    init: function() {
        var _obj = this;
        if($("#portfolioMenuLink").attr("type") != undefined)
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");

        this.editPortfolioGrid = $("#editPortfolioGrid");
        this.editPortfolioGrid.bind("grid_gridComplete", { obj: this }, function() {
            $('.deletePortfolioLink').each(function (index, value) { $(value).bind('click', { obj: $(value).attr('data') }, _obj.onRemoveClick); });
        });

        this.btnNewHolding = $("#btnNewHolding");
        this.btnNewHolding.bind("click", { obj: this }, this.onNewHoldingClick);
    }
};
IC.Public.portfolioList = {
    Delete: function(cellValue, options, rowObject) {
        var _this = IC.Public.holdingsList;
        var isEnabled = false;
        var index = $(this).getColIndex("IsThirdPartyImpersonateUser");
        if (index > 0) {
            isEnabled = !(rowObject[index - 1].toLowerCase() == "true");
        }

        var div = $('<div>');
        if (isEnabled) {
            var a = $('<a>');
            a.attr("href", "javascript:void(0);");
            a.addClass("deletePortfolioLink");
            a.html("Remove");
            a.attr('data', OC.MVC.JSON.ToUrl({ HrnKey: rowObject[0], Hrn: rowObject[1], RegisteredHolderName: rowObject[2], HoldingCount: rowObject[3] }));

            a.appendTo(div);
        } else {
            //Show the above message as Tooltip                
            var span = $("<span>");
            span.addClass("tooltip");
            span.attr("title", "You are viewing a third party's portfolio");
            span.html("Not Available");
            span.appendTo(div);
        }
        return div.html();
    }
};;//*******************************************************************************************************
//  
//  File:        ic.public.editPortfolio.js
//  Author:      Vince Hunt
//  Description: JS for EditPortfolio.aspx page
//  Review:      
//
//*******************************************************************************************************

IC.Public.confirmEditPortfolio = function(options) {
    if (options) {
    }
}

IC.Public.confirmEditPortfolio.prototype = {
    onCancelClick: function () {
        var url = OC.MVC.util.getLink("Holdings", "EditPortfolio");
        //Changed to prevent Antiforgery token issues
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url);
        // force an ajax call
        /*IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: null,
            success: function(result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function(err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });*/
    },
    onConfirmClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("Holdings", "EditPortfolioComplete");
        var form = $("form#ConfirmEditPortfolioForm");
        var data = form.serializeObject();
        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: { HrnKey: data.HrnKey},
            success: function(result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
                //document.location.reload();
            },
            error: function(err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    init: function() {
        $("#btnCancel").bind("click", { obj: this }, this.onCancelClick);
        $("#btnConfirm").bind("click", { obj: this }, this.onConfirmClick);

        $('html').unbind('keypress');
        $('html').bind('keypress', function(e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnConfirm').click();
                }
            }
        });
    }
};//*******************************************************************************************************
//  
//  File:        ic.public.holdingBalance.js
//  Author:      Dimitri Couwenberg
//  Description: JS for Balance History page
//  Review:      21/10/2010 Vladimir Nechypurenko
//               - format date for Filter label into "dd M yy"
//
//*******************************************************************************************************

IC.Public.holdingBalance = function(options) {
    if (options) {
    }
};

IC.Public.holdingBalance.gridFormatter = {
    numberTooltip: function(cellValue, options, rowObject) {
        if (cellValue == 'null') {
            return '';
        }

        // Check whether currency conversion is applied
        var originalCurrencyCode = IC.Public.gridFormatter.getCellValue($(this), "OriginalValueCurrency", rowObject);
        var convertedCurrencyCode = IC.Public.gridFormatter.getCellValue($(this), "ConvertedValueCurrency", rowObject);
        var currencyConversionApplied = (convertedCurrencyCode != 'null' && originalCurrencyCode != convertedCurrencyCode);

        //Format the cell value
        var displayValues = IC.Public.gridFormatter.getDisplayValues(cellValue);

        var isEmployeePlanRow = (IC.Public.gridFormatter.getCellValue($(this), "IsEmployeePlan", rowObject).toLowerCase() == "true");

        if (isEmployeePlanRow == false) {
            $("#messageHoldingDiv").hide();
        }
        else {
            $("#messageHoldingDiv").show();
        }
        if (!isEmployeePlanRow) {
            if (currencyConversionApplied) {
                return IC.Public.gridFormatter.getCurrencyConversionTooltip($(this), rowObject, displayValues.displayValue, originalCurrencyCode, convertedCurrencyCode, "Value");
            } else if (displayValues.useToolTip) {
                var div = $('<div>');
                var span = $('<span>');
                span.attr("title", displayValues.toolTipValue);
                span.addClass("tooltip");
                span.html(displayValues.displayValue);
                span.appendTo(div);
                return div.html();
            }
            return displayValues.toolTipValue;
        }

        //Construct the tooltip and format the cell value
        var uniqueId = IC.Public.gridFormatter.getCellValue($(this), "ID", rowObject);

        //We need to create a markup similar to this for cluetooltips to work
        //  <div>
        //      <a href="#" rel="[unique class name of tooltip content element]" class="[some common name for all tooltips]">
        //          [Formatted Cell Value]
        //      </a>
        //      <div class="[unique class name of tooltip content element]">
        //          [tooltip content]
        //      </div>
        //  </div>

        /****************Beginning of cluetooltip*****************/
        var clueToolTip = $('<div>');

        var a = $('<a>');
        a.attr("href", "#");
        a.attr("rel", ".tooltip" + uniqueId);
        a.attr("class", "cluetooltip valueClueTooltip");
        a.html(displayValues.displayValue);
        a.appendTo(clueToolTip);

        var toolTipContent = $('<div>');
        toolTipContent.attr("class", "tooltip" + uniqueId);

        var toolTipTitle;
        if (currencyConversionApplied) {
            toolTipContent.html(IC.Public.gridFormatter.getConvertedValueTooltipMessage($(this), rowObject, displayValues.displayValue, originalCurrencyCode, convertedCurrencyCode, "Value"));
        } else {
            toolTipTitle = $("#hidValueColumnTooltipTitle").val() + " " + $("#hidCurrency").val() + " " + displayValues.displayValue;
            toolTipContent.html("<p class='balanceTooltipTitle'>" + toolTipTitle + "</p>");
        }


        var isBalanceTradableOnly = IC.Public.gridFormatter.getCellValue($(this), "IsBalanceTradableOnly", rowObject).toLowerCase() == "true";
        var isSecuritiesSectionEnabled = IC.Public.gridFormatter.getCellValue($(this), "IsSecuritiesSectionEnabled", rowObject).toLowerCase() == "true";
        var isSplitPlanEnabled = IC.Public.gridFormatter.getCellValue($(this), "IsSplitPlanEnabled", rowObject).toLowerCase() == "true";
        var tooltipText = IC.Public.holdingBalance.gridFormatter.getValueColumnTooltipMessage(isBalanceTradableOnly, isSecuritiesSectionEnabled, isSplitPlanEnabled);

        var toolTipMessage = "<p class='balanceTooltipMessage'>" + tooltipText + "</p>";
        toolTipContent.html(toolTipContent.html() + toolTipMessage);

        toolTipContent.appendTo(clueToolTip);
        return clueToolTip.html();
        /****************End of cluetooltip*****************/
    },

    getValueColumnTooltipMessage: function (isBalanceTradableOnly, isSecuritiesSectionEnabled, isSplitPlanEnabled) {
        // [TODO] [Nov 13 2014] : Need to handle isSecuritiesSectionEnabled == false condition for isSplitPlanEnabled
        if (isSplitPlanEnabled) {
            return $('#hidValueColumnTradeablyOnlyWithSecuritiesToolTip').val();
        }
        else if (isBalanceTradableOnly && isSecuritiesSectionEnabled) {
            return $('#hidValueColumnTradeablyOnlyWithSecuritiesToolTip').val();
        } else if (!isBalanceTradableOnly && isSecuritiesSectionEnabled) {
            return $('#hidValueColumnAllBalanceWithSecuritiesToolTip').val();
        } else if (!isBalanceTradableOnly && !isSecuritiesSectionEnabled) {
            return $('#hidValueColumnAllBalanceWithoutSecuritiesToolTip').val();
        } else if (isBalanceTradableOnly && !isSecuritiesSectionEnabled)
            return $('#hidValueColumnAllBalanceWithoutSecuritiesToolTip').val();
        return "";
    },

    constructBalanceTooltip: function(cellValue, options, rowObject) {
        //Format the cell value
        var displayValues = IC.Public.gridFormatter.getNumericDisplayValues(cellValue);

        var isEmployeePlanRow = (IC.Public.gridFormatter.getCellValue($(this), "IsEmployeePlan", rowObject).toLowerCase() == "true");
        //Show this tooltip only if the current row is a EmployeePlanRow
        if (!isEmployeePlanRow)
            return displayValues.displayValue;

        var uniqueId = IC.Public.gridFormatter.getCellValue($(this), "ID", rowObject);
        var securities = IC.Public.gridFormatter.getNumericDisplayValues(IC.Public.gridFormatter.getCellValue($(this), "TradeableBalance", rowObject)).displayValue;
        var totalInPlan = IC.Public.gridFormatter.getNumericDisplayValues(IC.Public.gridFormatter.getCellValue($(this), "TotalInPlan", rowObject)).displayValue;
        var vested = IC.Public.gridFormatter.getNumericDisplayValues(IC.Public.gridFormatter.getCellValue($(this), "Vested", rowObject)).displayValue;
        var unVested = IC.Public.gridFormatter.getNumericDisplayValues(IC.Public.gridFormatter.getCellValue($(this), "Unvested", rowObject)).displayValue;
        var restricted = IC.Public.gridFormatter.getNumericDisplayValues(IC.Public.gridFormatter.getCellValue($(this), "Restricted", rowObject)).displayValue;

        var restrictedMarkup;
        if (restricted == 0)
            restrictedMarkup = "<tr><td class='planSummaryDetailsLabel'>Vested</td><td class='planSummaryDetailsValue'>" + vested + "</td></tr></table>";
        else
            restrictedMarkup = "<tr><td class='planSummaryDetailsLabel'>Vested</td><td class='planSummaryDetailsValue'>" + vested + "</td></tr>" + "<tr><td class='planSummaryDetailsLabel'>Restricted</td><td class='planSummaryDetailsValue'>" + restricted + "</td></tr></table>";

        var balanceBreakDown = "<table class='planDetailsTable'><tr class='totalInPlanRow'><td>In Plan</td><td class='planSummaryDetailsValue'>" + totalInPlan + "</td></tr>" +
            "<tr><td class='planSummaryDetailsLabel'>Unvested</td><td class='planSummaryDetailsValue'>" + unVested + "</td></tr>" +
            restrictedMarkup;

        //We need to create a markup similar to this for cluetooltips to work
        //  <div>
        //      <a href="#" rel="[unique class name of tooltip content element]" class="[some common name for all tooltips]">
        //          [Formatted Cell Value]
        //      </a>
        //      <div class="[unique class name of tooltip content element]">
        //          [tooltip content]
        //      </div>
        //  </div>

        /****************Beginning of cluetooltip*****************/
        var clueToolTip = $('<div>');

        var a = $('<a>');
        a.attr("href", "#");
        a.attr("rel", ".balancetooltip" + uniqueId);
        a.attr("class", "cluetooltip");
        a.html(displayValues.displayValue);
        a.appendTo(clueToolTip);

        var toolTipContent = $('<div>');
        toolTipContent.attr("class", "balancetooltip" + uniqueId);
        var tootipTitle = "<p class='balanceTooltipTitle'>" + $("#hidBalanceColumnTooltipTitle").val().replace("[[Balance]]", displayValues.displayValue) + "</p>";
        toolTipContent.html(tootipTitle);

        var isBalanceTradableOnly = IC.Public.gridFormatter.getCellValue($(this), "IsBalanceTradableOnly", rowObject).toLowerCase() == "true";
        var isSecuritiesSectionEnabled = IC.Public.gridFormatter.getCellValue($(this), "IsSecuritiesSectionEnabled", rowObject).toLowerCase() == "true";
        var tradeableSecuritiesMessage = IC.Public.holdingBalance.gridFormatter.getTradeableSecuritiesMessage(isBalanceTradableOnly, isSecuritiesSectionEnabled, securities);
        toolTipContent.html(toolTipContent.html() + tradeableSecuritiesMessage + "<br/>");

        toolTipContent.html(toolTipContent.html() + balanceBreakDown);
        toolTipContent.appendTo(clueToolTip);

        return clueToolTip.html();
        /****************End of cluetooltip*****************/
    },

    getTradeableSecuritiesMessage: function(isBalanceTradableOnly, isSecuritiesSectionEnabled, securities) {
        var tradeableSecuritiesMessage = "";
        if (isSecuritiesSectionEnabled) {
            tradeableSecuritiesMessage = "<p class='balanceTooltipMessage'>" + $("#hidBalanceColumnTradeableSecurities").val() + " " + securities;
            if (isBalanceTradableOnly)
                tradeableSecuritiesMessage += "<p>" + $('#hidBalanceColumnTradeableOnlyConfiguration').val() + "</p>";
            else
                tradeableSecuritiesMessage += "<p>" + $('#hidBalanceColumnAllConfiguration').val() + "</p>";
        }

        return tradeableSecuritiesMessage;
    },

    // It is better to split the formatter code for Portfolio and Balance History so that if in future any changes specific to either of the pages can be done
    constructPorfolioBalanceTooltip: function(cellValue, options, rowObject) {
        //construct the tooltip and format the cell value
        var isSecuritiesSectionEnabled = IC.Public.gridFormatter.getCellValue($(this), "IsSecuritiesSectionEnabled", rowObject) == "true";
        var isBalanceTradableOnly = IC.Public.gridFormatter.getCellValue($(this), "IsBalanceTradableOnly", rowObject) == "true";
        //Format the cell value
        var displayValues = IC.Public.gridFormatter.getNumericDisplayValues(cellValue);

        var isEmployeePlanRow = (IC.Public.gridFormatter.getCellValue($(this), "IsEmployeePlan", rowObject).toLowerCase() == "true");
        //Show this tooltip only if the current row is a EmployeePlanRow
        if (isEmployeePlanRow) {
            var uniqueId = IC.Public.gridFormatter.getCellValue($(this), "ID", rowObject);
            var tradeableBalance = IC.Public.gridFormatter.getNumericDisplayValues(IC.Public.gridFormatter.getCellValue($(this), "TradeableBalance", rowObject)).displayValue;
            var securities = displayValues.displayValue; //IC.Public.gridFormatter.getNumericDisplayValues(IC.Public.gridFormatter.getCellValue($(this), "TradeableBalance", rowObject)).displayValue;
            var totalInPlan = IC.Public.gridFormatter.getNumericDisplayValues(IC.Public.gridFormatter.getCellValue($(this), "TotalInPlan", rowObject)).displayValue;
            var vested = IC.Public.gridFormatter.getNumericDisplayValues(IC.Public.gridFormatter.getCellValue($(this), "Vested", rowObject)).displayValue;
            var unVested = IC.Public.gridFormatter.getNumericDisplayValues(IC.Public.gridFormatter.getCellValue($(this), "Unvested", rowObject)).displayValue;
            var restricted = IC.Public.gridFormatter.getNumericDisplayValues(IC.Public.gridFormatter.getCellValue($(this), "Restricted", rowObject)).displayValue;

            var restrictedMarkup;

            if (restricted == 0)
                restrictedMarkup = "<tr><td class='planSummaryDetailsLabel'>Vested</td><td class='planSummaryDetailsValue'>" + vested + "</td></tr></table>";
            else
                restrictedMarkup = "<tr><td class='planSummaryDetailsLabel'>Vested</td><td class='planSummaryDetailsValue'>" + vested + "</td></tr>" + "<tr><td class='planSummaryDetailsLabel'>Restricted</td><td class='planSummaryDetailsValue'>" + restricted + "</td></tr></table>";

            var balanceBreakDown = "<table class='planDetailsTable'><tr class='totalInPlanRow'><td>In Plan</td><td class='planSummaryDetailsValue'>" + totalInPlan + "</td></tr>" +
                "<tr><td class='planSummaryDetailsLabel'>Unvested</td><td class='planSummaryDetailsValue'>" + unVested + "</td></tr>" + restrictedMarkup;                                 


            /****************Beginning of cluetooltip*****************/
            var clueToolTip = $('<div>');

            var a = $('<a>');
            a.attr("href", "#");
            a.attr("rel", ".balancetooltip" + uniqueId);
            a.attr("class", "cluetooltip balanceToolTip");
            a.html(displayValues.displayValue);
            a.appendTo(clueToolTip);

            var toolTipContent = $('<div>');

            var toolTipMessage = "";
            toolTipContent.attr("class", "balancetooltip" + uniqueId);


            if (isSecuritiesSectionEnabled) {
                var tootipTitle = "<p class='balanceTooltipTitle'>" + $("#hidBalanceTooltipTitle").val() + "<span class='balanceValue'>" + displayValues.displayValue + "</span></p>";
                tootipTitle += "<p class='balanceTooltipTitle'>" + $("#hidTradeableSecuritiesTooltipTitle").val() + "<span class='tradeableBalanceValue'>" + tradeableBalance + "</span></p>";
                if (isBalanceTradableOnly)
                    toolTipMessage = "<p>" + $("#hidBalanceColumnTooltipMessage").val();
                else
                    toolTipMessage = "<p>" + $("#hidBalanceColumnTradableSecuritiesTooltipMessage").val();

                toolTipContent.html(tootipTitle);
                toolTipContent.html(toolTipContent.html() + toolTipMessage + "<br/><br/>");
                toolTipContent.html(toolTipContent.html() + balanceBreakDown);
            } else {
                toolTipContent.html(toolTipMessage + "<br/><br/>");
                toolTipContent.html(balanceBreakDown);
            }


            //var toolTipMessage = "<p class='balanceTooltipMessage'>" + $("#hidBalanceColumnTooltipMessage").val() + " " + securities;
            
            
            
            toolTipContent.appendTo(clueToolTip);

            return clueToolTip.html();
            /****************End of cluetooltip*****************/
        } else
            return displayValues.displayValue;
    },
    lastCloseAndValue: function(cellValue, options, rowObject) {
        var displayValue;
        var toolTipValue;

        var asAtDate = $("input[name=BalanceDate]").val();
        var balanceDate = $.datepicker.parseDate("dd/mm/yy", asAtDate);
        var notAvailableText = "n/a";

        if (balanceDate < new Date("08/03/2009")) {
            displayValue = notAvailableText;
            toolTipValue = 'There is no pricing information available prior to 03/08/2009';
        } else if (cellValue == 'null') {
            displayValue = notAvailableText;
            toolTipValue = 'There is no price available as at date ' + asAtDate;
        } else {
            displayValue = parseFloat(cellValue).toFixed(4).replace(/0{0,2}$/, "");
        }

        if (toolTipValue != undefined) {
            var div = $('<div>');
            var span = $('<span>');
            span.attr("title", toolTipValue);
            span.addClass("tooltip");
            span.html(displayValue);
            span.appendTo(div);
            displayValue = div.html();
        } else {
            displayValue = IC.Public.gridFormatter.formatLastCloseAndValue($(this), cellValue, options, rowObject, "Closing Price");
        }
        return displayValue;
    },
    
    planNameTooltip: function (cellValue, options, rowObject) {
        
        var div = $('<div>');
        var span = $('<span>');
        var index = $(this).getColIndex("SecurityDescription");
        
        if (index >= 0) {
            span.attr("title", rowObject[index].replace('&amp;', '&').replace('&apos;', '\'').replace('&gt;', '>').replace('&lt;', '<'));
        }
        span.addClass("tooltip");
        span.html(cellValue);
        span.appendTo(div);
        return div.html();
    }
};

IC.Public.holdingBalance.prototype = {
    postDate: function(events) {
        var _obj = events.data.obj;
        if (_obj.balanceDate.hasClass('error')) {
            return;
        }
        var date = $.datepicker.parseDate("dd/mm/yy", _obj.balanceDate.val());
        var now = new Date();
        var dateError = "Future date is not allowed.";
        IC.Public.removeErrorFor(_obj.balanceDate, _obj.form, dateError);

        if (date <= now) {
            var data = { Date: _obj.balanceDate.val() };
            _obj.filterDate.text($.datepicker.formatDate("dd M yy", date));
            if (date == null)
                _obj.filterDate.text($('#BalanceDateHidden').val());

            var filterData = _obj.holdingFilter.getHoldingFilterData();
            $.extend(data, filterData);
            _obj.holdingBalanceGrid.setPostData(data);
            IC.Public.registerGA(window.location.hash, _obj.holdingBalanceGrid.getPostData());
            _obj.holdingBalanceGrid.trigger('reloadGrid', [{ page: 1 }]);
        } else {
            IC.Public.displayErrorFor(_obj.balanceDate, _obj.form, dateError);
        }
    },
    onDateBlur: function(events) {
        $(this).dateValidate({ mask: "dd/mm/yy", name: events.data.name, subYears: events.data.subYears });
    },
    setGridColumnCurrencyCodes: function (currencyCode, isCurrencyConversionApplied) {
        var originalCurrencyAndTargetCurrencyAreSame = IC.Public.Currency.originalCurrencyAndTargetCurrencyAreSame(currencyCode);
        currencyCode = (isCurrencyConversionApplied || originalCurrencyAndTargetCurrencyAreSame) ? currencyCode : $('#hidCurrency').val();
        if (currencyCode != "-1") {
            $('#closingPriceColumnCurrency').text("(" + currencyCode + ")");
            $('#totalValueColumnCurrency').text("(" + currencyCode + ")");
        }
    },
    onApplyCurrencyChange: function (eventArgs, currencyCode) {
        var self = eventArgs.data.obj;
        var filterData = self.holdingFilter.getHoldingFilterData();
        self.holdingBalanceGrid.appendPostData(filterData);
        self.holdingBalanceGrid.appendPostData({ conversionCurrency: currencyCode });
        self.currencyChanged = true;
        self.holdingBalanceGrid.trigger('reloadGrid', [{ page: 1 }]);
    },
    init: function() {
        highlightMenu('home');
        if ($("#portfolioMenuLink").attr("type") != undefined && $("#portfolioMenuLink").attr("type").toLowerCase() == "authorisedportfolio") {
            //$("#mainMenu").css("visibility", "hidden");
            $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
        } else {
            $("#mainMenu").css("visibility", "visible");
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        }
        this.form = $('#frmBalanceHistory');
        this.holdingBalanceGrid = $("#HoldingsBalanceGrid");
        
        this.currencyChanged = false;
        this.conversionCurrency = $('#conversionCurrency');
        this.conversionCurrency.unbind('applyCurrencyChange');
        this.conversionCurrency.bind('applyCurrencyChange', { obj: this }, this.onApplyCurrencyChange);

        this.holdingBalanceGrid.bind("grid_gridComplete", { obj: this }, function (events, gridContext) {
            var self = events.data.obj;
            var gridTotal = $("#gridTotal");
            gridTotal.show();
            
            var isCurrencyConversionApplied = IC.Public.Currency.isCurrencyConversionApplied();
            var conversionCurrency = self.conversionCurrency.val();
            var currencySymbol = (isCurrencyConversionApplied && conversionCurrency != "-1") ? conversionCurrency : "$";
            var totalValue = $.fmatter.util.NumberFormat($(this).getUserDataItem('totalValue'), { decimalSeparator: ".", thousandsSeparator: ",", decimalPlaces: 2 });
            var defaultCurrency = $('#hidCurrency').val();
            gridTotal.html(IC.Public.Currency.getTotalValueHtml(currencySymbol, totalValue, isCurrencyConversionApplied, defaultCurrency, $('#hidTotalValueCurrencyConversionTooltipMessage').val()));
            self.setGridColumnCurrencyCodes(conversionCurrency, isCurrencyConversionApplied);
            
            if (self.currencyChanged) {
                self.currencyChanged = false;
                IC.Public.Currency.displayErrorMessageIfFailed(defaultCurrency, $('#hidCurrencyConversionErrorRateNotFound').val(), self.conversionCurrency);
            }
            
            //Cluetooltip is used for Value and Tradeable Balance columns. This code would initialize the cluetooltip
            $("A.cluetooltip").each(function () {
                if ($(this).attr('rel') == ".totalValueTooltip")
                    $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 310, positionBy: 'bottomTop' });
                else
                    $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 300 });
            });
            $("A.cluetooltip.valueClueTooltip").each(function () {
                $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 300});
            });

            //If Employee holding
            if ($("#ViewKey").val() == '-1' || $("#ViewKey option:selected:Contains('Employee Plan')").length>0) {
                $("#empBalNote").show();
            } else {
                $("#empBalNote").hide();
            }
        });

        this.holdingFilter = $("form.holdingFilterForm");
        this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.postDate);

        this.btnGo = $("#btnGo");
        this.btnGo.button();
        this.btnGo.bind("click", { obj: this }, this.postDate);

        this.filterDate = $("#filterDate");
        this.filterDateDiv = $("#filterDateDiv");
        this.balanceDate = $("[name=BalanceDate]");
        this.balanceDate.datepicker('option', { maxDate: new Date() });

        var dateValue = $.datepicker.parseDate("dd/mm/yy", this.balanceDate.val());
        this.filterDate.text($.datepicker.formatDate("dd M yy", dateValue));
    }
};

;//*******************************************************************************************************
//  
//  File:        ic.public.holdingDetails.js
//  Author:      Vladimir Nechypurenko
//  Description: JS for Holding details page
//  Review:      
//
//*******************************************************************************************************

IC.Public.holdingDetails = function(options) {
    if (options) {
    }
}

IC.Public.holdingDetails.prototype = {
    onTransactionHistoryClick: function() {
        var url = OC.MVC.util.getLink("Holdings", "TransactionsHistory");
        OC.MVC.util.loadMainContainerView(url);
    },
    onBalanceHistoryClick: function() {
        var url = OC.MVC.util.getLink("Holdings", "Balance");
        OC.MVC.util.loadMainContainerView(url);
    },
    onPaymentHistoryClick: function() {
        var url = OC.MVC.util.getLink("PaymentHistory", "Index");
        OC.MVC.util.loadMainContainerView(url);
    },
    onTaxHistoryClick: function() {
        var url = OC.MVC.util.getLink("Tax", "GetTax");
        OC.MVC.util.loadMainContainerView(url);
    },
    onPaymentInstructionsUpdateClick: function() {
        var url = OC.MVC.util.getLink("UpdatePaymentInstructions", "Index");
        OC.MVC.util.loadMainContainerView(url);
    },
    onReinvestmentPlanUpdateClick: function() {
        var url = OC.MVC.util.getLink($("#RegistryDrpControllerName").val(), "Index");
        OC.MVC.util.loadMainContainerView(url);
    },
    onTaxDetailsUpdateClick: function() {
        var url = OC.MVC.util.getLink("UpdateTaxDetails", "GetTaxDetails");
        OC.MVC.util.loadMainContainerView(url);
    },
    onFATCACRSUpdateClick: function () {
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "ForeignTaxResidencyCertificationIndex");
        OC.MVC.util.loadMainContainerView(url,null);
    },
    onCommunicationHistoryClick: function() {
        var url = OC.MVC.util.getLink("General", "Empty");
        OC.MVC.util.loadMainContainerView(url);
    },
    onCommunicationOptionsUpdate: function() {
        var url = OC.MVC.util.getLink("CommunicationsPreferences", "Preferences");
        OC.MVC.util.loadMainContainerView(url);
    },
    onAddressUpdateClick: function() {
        var url = OC.MVC.util.getLink("Address", "Edit");
        OC.MVC.util.loadMainContainerView(url);
    },
    onVotingClick: function() {
        var url = OC.MVC.util.getLink("ProxyVotingList", "Votes");
        OC.MVC.util.loadMainContainerView(url);
    },
    onPlanSummaryClick: function() {
        var url = OC.MVC.util.getLink("Summary", "Summary");
        OC.MVC.util.loadMainContainerView(url);
    },
    onOpenOffersClick: function() {
        var url = OC.MVC.util.getLink("Offers", "Index");
        OC.MVC.util.loadMainContainerView(url);
    },
    onExerciseClick: function() {
        var url = OC.MVC.util.getLink("Exercise", "Exercise");
        OC.MVC.util.loadMainContainerView(url);
    },
    onSecuritiesClick: function() {
        var url = OC.MVC.util.getLink("Securities", "SellOrTransferSecurities");
        OC.MVC.util.loadMainContainerView(url);
    },
    onAGMQuestionsClick: function(events) {
        var _obj = events.data.obj;

        if (!_obj.isLoading) {
            var url = OC.MVC.util.getLink("AnnualGeneralMeetingQuestions", "Render");
            var viewKey = _obj.holdingFilter.find("#ViewKey").val();
            var data = { viewKey: viewKey };
            OC.MVC.util.loadMainContainerView(url, data);
        }
    },
    onHoldingSelect: function(events, viewKey) {
        var _obj = events.data.obj;
        if (!_obj.isLoading) {
            var url = OC.MVC.util.getLink("HoldingDetails", "Render");
            var data = { viewKey: viewKey };
            OC.MVC.util._loadView("viewBarDetails", url, data);
        }
    },
    onApplyCurrencyChange: function (eventArgs, currencyCode) {
        var self = eventArgs.data.obj;
        if (!self.isLoading) {
            var url = OC.MVC.util.getLink("HoldingDetails", "Render");
            var viewKey = self.holdingFilter.find("#ViewKey").val();
            var data = { key: viewKey };
            
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                data: data,
                success: function (result) {
                    OC.MVC.util.loadHtml("viewBarDetails", result);
                    var currencyConversionFailied = $(result).find('#currencyConversionFailed').val();
                    if (currencyConversionFailied != undefined && currencyConversionFailied.toLowerCase() == "true") {
                        IC.Public.Currency.displayErrorMessage($('#hidCurrency').val(), $('#hidCurrencyConversionErrorRateNotFound').val(), self.conversionCurrency);
                    }
                    IC.Public.registerGA(url, { viewKey: $(result).find("[name=ViewKey]").val() });
                },
                error: function (err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },
    onBannerHideClick: function (events) {
        $("#divBannerDetails").hide();
        document.cookie = "BannerCookie=IsHidden";
    },
    init: function() {
        this.isLoading = true;
        highlightMenu('home');
        this.holdingFilter = $("form.holdingFilterForm");
        this.holdingFilter.unbind("holdingSelect");
        this.holdingFilter.bind("holdingSelect", { obj: this }, this.onHoldingSelect);

        this.conversionCurrency = $('#conversionCurrency');
        this.conversionCurrency.unbind('applyCurrencyChange');
        this.conversionCurrency.bind('applyCurrencyChange', { obj: this }, this.onApplyCurrencyChange);

        $('button[id^="btnTransactionHistory"]').bind("click", { obj: this }, this.onTransactionHistoryClick);
        $('button[id^="btnBalanceHistory"]').bind("click", { obj: this }, this.onBalanceHistoryClick);
        $("#btnPaymentHistory").bind("click", { obj: this }, this.onPaymentHistoryClick);
        $("#btnTaxHistory").bind("click", { obj: this }, this.onTaxHistoryClick);
        $("#btnPaymentInstructionsUpdate").bind("click", { obj: this }, this.onPaymentInstructionsUpdateClick);
        $("#btnReinvestmentPlanUpdate").bind("click", { obj: this }, this.onReinvestmentPlanUpdateClick);
        $("#btnTaxDetailsUpdate").bind("click", { obj: this }, this.onTaxDetailsUpdateClick);
        $("#btnFATCACRSUpdate").bind("click", { obj: this }, this.onFATCACRSUpdateClick);
        $("#btnCommunicationHistory").bind("click", { obj: this }, this.onCommunicationHistoryClick);
        $("#btnCommunicationOptionsUpdate").bind("click", { obj: this }, this.onCommunicationOptionsUpdate);
        $("#btnAddressUpdate").bind("click", { obj: this }, this.onAddressUpdateClick);
        $("#btnVoting").bind("click", { obj: this }, this.onVotingClick);
        $("#btnAGMQuestions").bind("click", { obj: this }, this.onAGMQuestionsClick);
        $("#btnPlanSummary").bind("click", { obj: this }, this.onPlanSummaryClick);
        $("#btnOpenOffers").bind("click", { obj: this }, this.onOpenOffersClick);
        $("#btnExercise").bind("click", { obj: this }, this.onExerciseClick);
        $("#btnSecurities").bind("click", { obj: this }, this.onSecuritiesClick);
        $("#lnkClose").bind('click', { obj: this }, this.onBannerHideClick);

        this.isLoading = false;
    }
};

IC.Public.holdingDetails.planSummaryList = {
    planNameTooltip: function (cellValue, options, rowObject) {
       
        var div = $('<div>');
        var span = $('<span>');
        var index = $(this).getColIndex("Plan.Name");

        var hidePlanNameToolTip = $('#hidPlanNameToolTip').val();
        var IsPlanSummary=$('#hidPlanSummary').val();
        if (IsPlanSummary == "true") {
            if (hidePlanNameToolTip != "") {
                if (hidePlanNameToolTip.toLowerCase() == "false") {

                    if (index >= 0) {
                        var val = rowObject[index];
                        rowObject[index] = val.split("viewKey").pop(-1).split(",").pop(-1);
                        var array = rowObject[index].split('--');
                        rowObject[index] = array[0];
                        span.attr("title", rowObject[index].replace('&amp;', '&').replace('&apos;', '\'').replace('&gt;', '>').replace('&lt;', '<'));
                    }

                }
                
           }
          }
           else {
                    if (index >= 0) {

                        span.attr("title", rowObject[index].replace('&amp;', '&').replace('&apos;', '\'').replace('&gt;', '>').replace('&lt;', '<'));

                    }
        }
        span.addClass("tooltip");
        span.addClass("actionLink");
        span.html(cellValue);
        span.appendTo(div);
        return div.html();
    },
    
    vestingCalcPlanNameTooltip: function (cellValue, options, rowObject) {
        var div = $('<div>');
        var span = $('<span>');
        var index = $(this).getColIndex("Plan.Name");

        if (index >= 0) {

            span.attr("title", rowObject[index].replace('&amp;', '&').replace('&apos;', '\'').replace('&gt;', '>').replace('&lt;', '<'));

        }
        span.addClass("actionLink");
        span.html(cellValue);
        span.appendTo(div);
        return div.html();
    }
};
;//*******************************************************************************************************
//  
//  File:        ic.public.newholding.js
//  Author:      Evgeny Nikiforov
//  Description: JS for NewHoding page
//  Review:      22/10/2010 Vladimir Nechypurenko
//               - Change according to the new requirements
//
//*******************************************************************************************************


IC.Public.newHolding = function(options) {
};

IC.Public.newHolding.prototype = {
    onContinueClick: function (events) {
        $("#CompanyCode").val($("#SingleHolding_CompanyCode").val());
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("Holdings", "AccountPrompt");
        _obj.validator = _obj.form.validate(IC.Public.getValidatorCfg());
        if (_obj.form.valid()) {
            IC.Public.showLoading("");
            var data = _obj.form.serialize();
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function(result) {
                    $("#" + OC.MVC.constants.mainContainer).html(result);
                    IC.Public.hideLoading();
                },
                error: function(err) {
                    $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },
    
    onCancelClick: function(eventArgs) {
        if ($("#IsSingleHoldingRegistration").val().toLowerCase() == "true") {
            window.location.href = "/OpenAccess/LinkOtherAccountsFromAddHolding" + '?' + "Email=" + $("#Email").val() + "&IssuerCode=" + $("#IssuerCode").val() + "&IsSingleHoldingRegistration=" + $("#IsSingleHoldingRegistration").val();
        } else {
            var url = OC.MVC.util.getLink("Holdings", "Index");
            OC.MVC.util.loadMainContainerView(url);
        }
    },
    
    init: function() {
        highlightMenu('home');
        var self = this;
        this.form = $("form#newHolding");
        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind('click');
        this.btnCancel.bind('click', { obj: this }, this.onCancelClick);
        this.btnContinue = $("#btnContinue");
        this.btnContinue.button();
        this.btnContinue.bind("click", { obj: this }, this.onContinueClick);
        this.divEmployeeShares = $("#divEmployeeShares");
        this.divOscarShares = $("#divOscarShares");
        this.divOscarShares.trigger("click");
        $("input[name='SingleHolding.HoldingType']").each(function() {
            $(this).bind("click", self.toggleContent);
        });
        if($("#portfolioMenuLink").attr("type") != undefined)
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        var selectedRadioOption = $("input[name = 'SingleHolding.HoldingType']").val();
        var targetRadioButton;
        if (selectedRadioOption == "employee") {
            targetRadioButton = $("input[name='SingleHolding.HoldingType'][value='Employee Equity Plan']");
            targetRadioButton.attr("checked", true);
            this.divEmployeeShares.show();
            this.divOscarShares.hide();
            $("#hidHoldingType").val("employee");

        } else if (selectedRadioOption == "oscar") {
            targetRadioButton = $("input[name='SingleHolding.HoldingType'][value='Shares']");
            targetRadioButton.attr("checked", true);
            this.divEmployeeShares.hide();
            this.divOscarShares.show();
            $("#hidHoldingType").val("oscar");

        } else {
            targetRadioButton = $("input[name='SingleHolding.HoldingType'][value='Shares']");
            targetRadioButton.attr("checked", true);
            $("#hidHoldingType").val("oscar");
            this.divEmployeeShares.hide();
            this.divOscarShares.show();
            $("#hidHoldingType").val("oscar");

            //alert("3");
        }


        //handle enter
        $('input').bind('keypress', function(e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnContinue').click();
                }
            }
        });

        var isThirdPartyImpersonateUser = $("#IsThirdPartyImpersonateUser").val();
        if (isThirdPartyImpersonateUser != undefined) {
            if (isThirdPartyImpersonateUser.toLowerCase() == "true") {
                //$("#btnContinue").attr('disabled', true);
                IC.Public.disableControls('newHolding');
                $("#btnCancel").removeAttr('disabled');
                $("#btnBackToAuthorisedPortfolio").removeAttr('disabled');
            } else {
                $("#btnContinue").removeAttr('disabled');
            }
        }

        var isAdditionalHoldingClick = $("#IsAdditionalHoldingClick").val();
        if (isAdditionalHoldingClick != undefined) {
            if (isAdditionalHoldingClick.toLowerCase() == "true") {
                IC.Public.HideMenu();
            }
        }
        $("a.pinHelpLink").cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 325 });
    },
    toggleContent: function() {

        $("div.errorContainer").empty();

        /*Show Employee Plan/oscar input fields based on which one is selected */
        this.divEmployeeShares = $("#divEmployeeShares");
        this.divOscarShares = $("#divOscarShares");
        if ($(this).attr("id") == "rb_SingleHolding.HoldingType_1") {
            this.divEmployeeShares.hide();
            this.divOscarShares.show();
            $("#hidHoldingType").val("oscar");
        } else {
            this.divEmployeeShares.show();
            this.divOscarShares.hide();
            $("#hidHoldingType").val("employee");

        }
    }
};
;IC.Public.newHoldingPrompt = function(options) {
}

IC.Public.newHoldingPrompt.prototype = {

    onDoneClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("NewAccountPrompt", "UpdateCommunicationPreferences");
        var data = { cacheKey: $("#CacheKey").val(), isEmailCommunicationPreferred: $("#IsEmailCommunicationPreferred").is(':checked') };

        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: data,
            success: function(result) {
                IC.Public.hideLoading();
                if ($("#IsSingleHoldingRegistration").val().toLowerCase() == "true") {
                    window.location.href = "/OpenAccess/LinkOtherAccountsFromAddHolding" + '?' + "Email=" + $("#Email").val() + "&IssuerCode=" + $("#IssuerCode").val() + "&IsSingleHoldingRegistration=" + $("#IsSingleHoldingRegistration").val();
                } else {
                    var url = OC.MVC.util.getLink("Holdings", "Index");
                    OC.MVC.util.loadMainContainerView(url);
                }
            },
            error: function(err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },

    init: function() {
        highlightMenu('home');

        this.btnDone = $("#btnDone");
        this.btnDone.button();
        this.btnDone.bind("click", { obj: this }, this.onDoneClick);
        this.btnDone.focus();
        var isAdditionalHoldingClick = $("#IsAdditionalHoldingClick").val();
        if (isAdditionalHoldingClick != undefined) {
            if (isAdditionalHoldingClick.toLowerCase() == "true") {
                IC.Public.HideMenu();
            }
        }
    }
}
;IC.Public.transactionsHistory = function(options) {
    if (options) {
    }
};
IC.Public.transactionsHistory.prototype = {
    onGoClick: function(events) {
        var _obj = events.data.obj;
        var filterData = _obj.holdingFilter.getHoldingFilterData();
        if (events.data.isLoading != true && _obj.securityList.css("display") != "none") {
            filterData.SecurityCode = _obj.securityList.val();
        }

        var reverseDateError = 'From date should be before to date.';
        IC.Public.removeErrorFor(_obj.dateFrom, _obj.form, reverseDateError);
        if (_obj.dateFrom.hasClass('error')) { return; }
        if (_obj.dateTo.hasClass('error')) { return; }

        var dates = { dateFrom: _obj.dateFrom.val(), dateTo: _obj.dateTo.val() };
        var dateFromValue = $.datepicker.parseDate("dd/mm/yy", dates.dateFrom);
        var dateToValue = $.datepicker.parseDate("dd/mm/yy", dates.dateTo);

        if (dateFromValue > dateToValue) {
            IC.Public.displayErrorFor(_obj.dateFrom, _obj.form, reverseDateError);
            return;
        }

        $('#filterDateFrom').text($.datepicker.formatDate("dd M yy", dateFromValue));
        $('#filterDateTo').text($.datepicker.formatDate("dd M yy", dateToValue));
        if (dateFromValue == null)
            $('#filterDateFrom').text($('#filterDateFromHidden').val());
        if (dateToValue == null)
            $('#filterDateTo').text($('#filterDateToHidden').val());

        $.extend(dates, filterData);
        _obj.grid.setPostData(dates);
        IC.Public.registerGA(window.location.hash, _obj.grid.getPostData());
        _obj.grid.trigger('reloadGrid', [{ page: 1}]);
    },
    onDateBlur: function(events) {
        $(this).dateValidate({ mask: "dd/mm/yy", name: events.data.name, subYears: events.data.subYears });
    },
    //SecurityList is hidden all the time - request to hide it.
    loadSecuritiesList: function(viewKey) {
        if (viewKey && viewKey != null) {
            var url = OC.MVC.util.getLink("HoldingFilter", "GetSecuritiesList");
            var _obj = this;
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                dataType: 'json',
                data: { viewKey: viewKey },
                type: 'POST',
                success: function(result) {
                    _obj.securityList.empty();
                    if (result && result.length > 1) {
                        _obj.securityList.show();
                        _obj.securityList.parent().show();

                        $.each(result, function(index, item) {
                            var option = $('<option>');
                            option.val(item.SecurityCode);
                            if (item.SecurityCode == "ALL") {
                                option.html(item.SecurityDescription);
                            }
                            else {
                                option.html(item.SecurityCode);
                            }
                            option.appendTo(_obj.securityList);
                        });
                    }
                    else {
                        _obj.securityList.hide();
                        _obj.securityList.parent().hide();
                    }
                    IC.Public.hideLoading();
                },
                error: function(err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },
    onApplyHoldingFilter: function (events, value) {
        // reload the view so the grid is refreshed with the correct gridmodel
        var url = OC.MVC.util.getLink("Holdings", "TransactionsHistory");
        IC.Public.showLoading("");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url, "");        
    },
    init: function() {
        highlightMenu('home');
        if ($("#portfolioMenuLink").attr("type") != undefined && $("#portfolioMenuLink").attr("type").toLowerCase() == "authorisedportfolio") {
            //$("#mainMenu").css("visibility", "hidden");
            $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
        } else {
            $("#mainMenu").css("visibility", "visible");
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        }
        this.securityList = $("select[name=SecurityCode]");
        this.securityList.unbind("change");
        this.securityList.bind("change", { obj: this }, this.onGoClick);

        if ($('#IsEmployeePlanEnabled').val() != undefined && $('#IsEmployeePlanEnabled').val().toLowerCase() == "true") {
            this.grid = $("#SecurityTransactionsGridModel");
        } else {
            this.grid = $("#TransactionsHistoryGrid");
        }

        this.form = $('#transactionsHistoryForm');
        this.holdingFilter = $("form.holdingFilterForm");
        this.holdingFilter.unbind("applyHoldingFilter");
        this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onApplyHoldingFilter);

        this.btnGo = $("#btnGo");
        this.btnGo.button();
        this.btnGo.bind("click", { obj: this }, this.onGoClick);

        this.dateFrom = $("[name=DateFrom]");
        this.dateTo = $("[name=DateTo]");
        this.dateFrom.datepicker('option', { maxDate: new Date() });
        this.dateTo.datepicker('option', { maxDate: new Date() });

        var filterData = this.holdingFilter.getHoldingFilterData();
        this.loadSecuritiesList(filterData.ViewKey);
        
        var gridLinksInitialised = false;
        var rowCount = 0;
        this.securityTransactionsGrid = $("#SecurityTransactionsGridModel");
        this.securityTransactionsGrid.bind("grid_gridComplete", { obj: this }, function (events, gridContext) {
            rowCount = gridContext;
            var _obj = events.data.obj;
            $(this).find("a:contains('View Confirmation')").each(function (index) {
                if ($(this).attr('data-transactionid') && $(this).attr('data-transactionid').length > 0) {
                    gridLinksInitialised = true;
                    $(this).bind("click", { obj: _obj }, _obj.onViewTransactionClick);
                }
            });
        });

        // Sometimes the grid is loaded before the gridComplete event can be binded, so we need to reload so that View Confirmation click event is binded
        if (rowCount > 0 && !gridLinksInitialised) {
            this.securityTransactionsGrid.trigger('reloadGrid');
        }


        IC.Public.updateCampaignsPanel('HoldingsTransactionHistory');

        // PL temporarily stop click action until we know where to go for the transaction
        //$("#transactionsHistoryGridDiv").bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onGridComplete);

    },
    
    onViewTransactionClick: function () {
        var url = OC.MVC.util.getLink("Securities", "ViewConfirmationDetails");
        var data = { transactionId: $(this).attr("data-transactionid") || "", viewKey: $("#ViewKey").val(), transactionMethod: $(this).attr("data-transactionmethod") || "" , fromTransactionHistory: true };
        IC.Public.showLoading("");
        OC.MVC.util.loadMainContainerView(url, data);
        IC.Public.hideLoading();
    },

    onGridComplete:function()
    {
        //Handle 'view confirmation' link click events
        $(".transHistoryConfirmation").each(function () {
            $(this).bind("click", function () {
                var viewKey = $(this).attr("key");
                if (viewKey) {
                    data = { key: viewKey };
                }
                var url = OC.MVC.util.getLink("Plan", "ViewConfirmation");
                IC.Public.showLoading("");
                OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url, data);        
            });
            
        });
    }

};
IC.Public.transactionsHistoryBeforeRequest = function () {
    var viewKey = $("#ViewKey").val();
    $(this).setPostDataItem("ViewKey", viewKey);
};
IC.Public.GetAmendmentListBeforeRequest = function () {
    var finYear = $("#FinancialYear").val();
    $(this).setPostDataItem("finYear", finYear);
};
;/*Contains column formatting functions for transaction grid.*/
IC.Public.transactionGrid = {
    //Formats the action column to add the confirmation link
    formatActionColumn: function (cellValue, options, rowObject) {
        var uniqueId = rowObject[0];
        var div = $('<div>');
        var a = $('<a>');
        a.attr("href", "javascript:void(0);");
        a.addClass("transHistoryConfirmation");
        a.attr("key", uniqueId);
        a.html("View Confirmation");
        a.appendTo(div);

        return div.html();
    }
};IC.Public.Currency = {    
    isCurrencyConversionFailed: function () {
        var currencyConversionFailed = true;
        $('tr.jqgrow').each(function () {
            var tr = $(this);
            var currencyConversionFailedValue = tr.getColumnValue('CurrencyConversionFailed');
            if (currencyConversionFailedValue != 'null' && currencyConversionFailedValue.toLowerCase() == 'false') {
                currencyConversionFailed = false;
                return;
            }
        });

        if ($('tr.jqgrow').length == 0)
            currencyConversionFailed = false;


        return currencyConversionFailed;
    },
    
    isCurrencyConversionApplied: function () {
        var currencyConversionApplied = false;
        $('tr.jqgrow').each(function () {
            var tr = $(this);
            var originalCurrency = tr.getColumnValue('OriginalValueCurrency');
            var convertedCurrency = tr.getColumnValue('ConvertedValueCurrency');
            if (convertedCurrency != 'null' && originalCurrency != convertedCurrency) {
                currencyConversionApplied = true;
                return;
            }
        });

        if ($('tr.jqgrow').length == 0)
            currencyConversionApplied = true;

        return currencyConversionApplied;
    },
    
    originalCurrencyAndTargetCurrencyAreSame: function (targetCurrencyCode) {
        var originalCurrencyCode = '';
        $('tr.jqgrow').each(function () {
            var tr = $(this);
            var currentOriginalCurrency = tr.getColumnValue('OriginalValueCurrency');
            if (originalCurrencyCode == '') {
                originalCurrencyCode = currentOriginalCurrency;
            }
            if (originalCurrencyCode != currentOriginalCurrency) {
                originalCurrencyCode = '';
                return;
            }
        });

        return (originalCurrencyCode != '' && targetCurrencyCode != '' && originalCurrencyCode == targetCurrencyCode);
    },
    
    getTotalValueHtml: function (currencySymbol, totalValue, isCurrencyConversionApplied, defaultCurrency, tooltipMessage) {
        var formattedTotalValue = 'Total Value: ' + currencySymbol + ' ' + totalValue;
        if (!isCurrencyConversionApplied)
            return formattedTotalValue;

        var clueToolTip = $('<div>');
        var a = $('<a>');
        a.attr("href", "#");
        a.attr("rel", ".totalValueTooltip");
        a.attr("class", "cluetooltip");
        a.html(formattedTotalValue);
        a.appendTo(clueToolTip);

        var toolTipContent = $('<div>');
        toolTipContent.attr("class", "totalValueTooltip");

        var tooltipMessageSpan = "<span class='currency-conversion-bold'>" + formattedTotalValue + "</span>";
        currencySymbol = (currencySymbol == "$") ? $('#hidCurrency').val() : currencySymbol;
        tooltipMessageSpan += "<br/><br/><span class='currency-conversion-normal'>" + tooltipMessage.replace("{0}", currencySymbol) + "</span>";
        toolTipContent.html(tooltipMessageSpan);
        toolTipContent.appendTo(clueToolTip);
        return clueToolTip.html();
    },
    
    resetSelectedCurrency: function (currencyCode) {
        IC.Public.showLoading("");
        $.ajax({
            url: OC.MVC.util.getLink("Currency", "SaveSettings"),
            type: 'POST',
            data: { currencyCode: currencyCode },
            success: function (result) {
                IC.Public.hideLoading();
                if (result && !result.IsSuccess) {
                    alert(result.ErrorMessage);
                }
                else if (result && result.IsSuccess) {
                    // Update the token if reset currency
                    IC.Public.showLoading("");
                    $.ajax({
                        url: OC.MVC.util.getLink("General", "RefreshToken"),
                        async: false,
                        type: "GET",
                        success: function (result) {
                            $("input[name='__RequestVerificationToken']").remove();
                            $('body').append(result);
                            IC.Public.hideLoading();
                        }
                    });
                }
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    
    displayErrorMessageIfFailed: function (defaultCurrency, errorMessage, conversionCurrencyDropdown) {
        if (IC.Public.Currency.isCurrencyConversionFailed()) {
            IC.Public.Currency.displayErrorMessage(defaultCurrency, errorMessage, conversionCurrencyDropdown);
        }
    },
    
    displayErrorMessage: function (defaultCurrency, errorMessage, conversionCurrencyDropdown) {
        alert(errorMessage);
        conversionCurrencyDropdown.val(defaultCurrency);
        IC.Public.Currency.resetSelectedCurrency(defaultCurrency);
    }
};;
IC.Public.updateAddress = function(options) {
}

IC.Public.updateAddress.prototype = {
    onContinueClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("Address", "Confirm");
        var form = $("form#UpdateAddressForm");
        var data = form.serialize();

        if ($('#IsOtherHoldingsVisible').val().toLowerCase() == "true") {
            var selectedOption = $("#ApplyToAllHoldings_1:checked").get(0);
            var selector = ".updatecheckbox:checked";
            if (($("#IsShowOtherHoldings:checked").val() != undefined) && (selectedOption != undefined)) {
                selector = ".updatecheckbox";
            }
            var checkedValues = $(selector).map(function() { return $(this).val(); }).get();
            if (checkedValues) {
                data += '&HoldingIDs=' + checkedValues.join(',');
            }
            data += "&ApplyToAllHoldings=" + $("[name='ApplyToAllHoldings']:checked").val();
        } else {
            data += '&HoldingIDs=' + $('#RowsToBeUpdated').val();
        }

        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: data,
            success: function(result) {
                var div = $('<div>');
                div.html(result);
                if (div.find('select[name="Country"]').length > 0) {
                    $("#viewBarDetails").html(result);
                }
                else {
                    $("#" + OC.MVC.constants.mainContainer).html(result);
                }
                IC.Public.hideLoading();
            },
            error: function(err) {
                ("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },

    onSelectedCountryChange: function(events) {
        var _obj = events.data.obj;

        _obj.divTown.hide();
        _obj.divState.hide();
        _obj.divPostcode.hide();
        _obj.divNzlPostcodeLink.hide();

        _obj.selectedCountryName.val($("select[name='Country'] option:selected").html());
        
        var selectedCountry = $(this).val();

        if ($("#CurrentRegistry").val().toLowerCase() == "aus" && selectedCountry == "AUS") {
            _obj.divTown.show();
            _obj.divState.show();
            _obj.divPostcode.show();

        }
        else if ($("#CurrentRegistry").val().toLowerCase() == "nzl" && selectedCountry == "NZL") {
            _obj.divPostcode.show();
            _obj.divNzlPostcodeLink.show();
        }
    },

    onFilterGrid: function(events, viewKey) {
        var _obj = events.data.obj;
        if (!_obj.isLoading) {
            var url = OC.MVC.util.getLink("Address", "Render");
            var data = { viewKey: viewKey };
            IC.Public.showLoading("");
            OC.MVC.util._loadView("viewBarDetails", url, data);
            //IC.Public.hideLoading();
        }
    },
    init: function() {
        this.isLoading = true;
        highlightMenu('communication');
        if($("#portfolioMenuLink").attr("type") != undefined)
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        this.selectedCountryName = $("#SelectedCountryName");
        this.divModalPopup = $("#confirm");

        this.btnContinue = $("#btnUpdate");
        this.btnContinue.button();
        this.btnContinue.bind("click", { obj: this }, this.onContinueClick);

        this.divTown = $("#divTown");
        this.divState = $("#divState");
        this.divPostcode = $("#divPostcode");
        this.divNzlPostcodeLink = $("#divNzlPostcodeLink");

        this.countryList = $("select[name='Country']");
        this.countryList.unbind("change");
        this.countryList.bind("change", { obj: this }, this.onSelectedCountryChange);

        //this.applyUpdatesGrid = $("#bdiv_ApplyHoldingsGrid");

        this.holdingFilter = $("form.holdingFilterForm");
        this.holdingFilter.unbind("applyHoldingFilter");
        this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterGrid);

        var isDisabled = $("#IsNextButtonDisabled").get(0);
        if ((isDisabled != undefined) && ($(isDisabled).val().toLowerCase() == "true")) {
            $("div.divControls").children().attr("disabled", true);
        }
        $('html').unbind('keypress');
        $('html').bind('keypress', function(e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnUpdate').click();
                }
            }
        });

        var isThirdPartyImpersonateUser = $("#IsThirdPartyImpersonateUser").val();
        if (isThirdPartyImpersonateUser != undefined) {
            if (isThirdPartyImpersonateUser.toLowerCase() == "true") {
                //$("#btnUpdate").attr('disabled', true);
                $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
                IC.Public.disableControls('UpdateAddressForm');
                $("#ViewKey").removeAttr('disabled');
                $("#btnBackToAuthorisedPortfolio").removeAttr('disabled');
            } else {
                $("#btnUpdate").removeAttr('disabled');
            }
        }

        this.isLoading = false;
    }
}
;IC.Public.customPreferences = function(options) {
}

jQuery.expr[':'].Contains = function (a, i, m) {
    return jQuery(a).text().toUpperCase().indexOf(m[3].toUpperCase()) >= 0;
};

IC.Public.customPreferences.prototype = {

    onUpdateClick: function (events) {
        $(".errorContainer ol").empty();
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        var _obj = events.data.obj;
        var checkedOption = $("input[name='EmailAddressRadioCode']:radio:checked").get(0);
        if (checkedOption != undefined) {
            if ($(checkedOption).val() == "UserName") {
                $("#EmailAddress").val("");
            }
        }
        var form = $("form#PreferencesForm");
        var data = form.serialize();
        var missingEcomm = null;
        var fromLogin = null;
        var isSingleHolding = null;
        var url = OC.MVC.util.getLink("CommunicationsPreferences", "Confirm");
        if ($('#IsSingleHolding').val() != undefined && $('#IsSingleHolding').val().toLowerCase() == "true")
            isSingleHolding = true;
        if ($('#IsNavigatedfromLoginScreen').val() != undefined && $('#IsNavigatedfromLoginScreen').val().toLowerCase() == "true")
            fromLogin = true;
        if ($('#IsMissingCommunicationDetailsEnabled').val() != undefined && $('#IsMissingCommunicationDetailsEnabled').val().toLowerCase() == "true")
            missingEcomm = true;
        if (fromLogin && missingEcomm) {
            var selector = ".updatecheckbox:checked";
            var checkedValues = $(selector).map(function () { return $(this).val(); }).get();
            if (checkedValues.length > 0) {
                data += '&HoldingIDs=' + checkedValues.join(',');
                //data += $('#RowsToBeUpdated').val();
            }

            form.validate(IC.Public.getValidatorCfg());
            if (checkedValues.length > 0 || isSingleHolding) {
                if (form.valid()) {
                    IC.Public.showLoading("");
                    $.ajax({
                        url: url,
                        type: 'POST',
                        data: data,
                        success: function (result) {
                            if (result && result.Status == "SUCCESS") {
                                var url = OC.MVC.util.getLink("CommunicationsPreferences", "ConfirmPage");
                                OC.MVC.util.loadMainContainerView(url, { cacheKey: $('#CacheKey').val() });
                            }
                            else {
                                var emailAddress = $("[name='EmailAddress']");
                                IC.Public.removeErrorFor(emailAddress, form, result.ErrorMessage);
                                IC.Public.displayErrorFor(emailAddress, form, result.ErrorMessage, 'error');
                            }
                            IC.Public.hideLoading();
                        },
                        error: function (err) {
                            $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                            IC.Public.hideLoading();
                        }

                    });

                }
            } else {
                $(".errorContainer").show();
                $(".errorContainer ol").html("<li><label class='error'>There are no holders selected to update.</label></li>");


            }
        } else {
        if ($('#IsOtherHoldingsVisible').val().toLowerCase() == "true") {
            var selectedOption = $("#ApplyToAllHoldings_1:checked").get(0);
            var selector = ".updatecheckbox:checked";
            if (($("#IsShowOtherHoldings:checked").val() != undefined) && (selectedOption != undefined)) {
                selector = ".updatecheckbox";
            }
            var checkedValues = $(selector).map(function () { return $(this).val(); }).get();
            if (checkedValues) {
                data += '&HoldingIDs=' + checkedValues.join(',');
            }
            data += "&ApplyToAllHoldings=" + $("[name='ApplyToAllHoldings']:checked").val();

            //If previously selected value is DDN and either Apply To All Holdings selected or any particular holding with DDN is ticked
            if ($('#IsAnnualReportRadioCodeDDN').val().toUpperCase() == 'FALSE' &&
                (($("[name='ApplyToAllHoldings']:checked").val().toUpperCase() == "TRUE" && $('td:Contains(All applicable communications to Australia Post Digital Mailbox, otherwise by Post)').length > 0)
                    || ($('input[type="checkbox"]', $('#col_8', $('td:Contains(All applicable communications to Australia Post Digital Mailbox, otherwise by Post)').parent())).is(':checked') == true))) {
                $("#dialog-confirm-all").dialog({
                    modal: true,
                    buttons: {
                        Ok: function () {
                            $(this).dialog("close");
                            process();
                        },
                        Cancel: function () {
                            $(this).dialog("close");
                            return;
                        }
                    }
                });
            } else {
                _obj.validator = form.validate(IC.Public.getValidatorCfg());
                if (form.valid()) {
                    IC.Public.showLoading("");
                    $.ajax({
                        url: url,
                        type: 'POST',
                        data: data,
                        success: function (result) {
                            if (result && result.Status == "SUCCESS") {
                                var url = OC.MVC.util.getLink("CommunicationsPreferences", "ConfirmPage");
                                OC.MVC.util.loadMainContainerView(url, { cacheKey: $('#CacheKey').val() });
                            }
                            else {
                                var emailAddress = $('[name="EmailAddress"]');
                                IC.Public.removeErrorFor(emailAddress, form, result.ErrorMessage);
                                IC.Public.displayErrorFor(emailAddress, form, result.ErrorMessage, 'error');
                            }
                            IC.Public.hideLoading();
                        },
                        error: function (err) {
                            IC.Public.hideLoading();
                            OC.MVC.util.errorMessage(err.responseText);
                        }
                    });
                }
            }
        } else {
            data += '&HoldingIDs=' + $('#RowsToBeUpdated').val();
            _obj.validator = form.validate(IC.Public.getValidatorCfg());
            if (form.valid()) {
                IC.Public.showLoading("");
                $.ajax({
                    url: url,
                    type: 'POST',
                    data: data,
                    success: function (result) {
                        if (result && result.Status == "SUCCESS") {
                            var url = OC.MVC.util.getLink("CommunicationsPreferences", "ConfirmPage");
                            OC.MVC.util.loadMainContainerView(url, { cacheKey: $('#CacheKey').val() });
                        }
                        else {
                            var emailAddress = $('[name="EmailAddress"]');
                            IC.Public.removeErrorFor(emailAddress, form, result.ErrorMessage);
                            IC.Public.displayErrorFor(emailAddress, form, result.ErrorMessage, 'error');
                        }
                        IC.Public.hideLoading();
                    },
                    error: function (err) {
                        IC.Public.hideLoading();
                        OC.MVC.util.errorMessage(err.responseText);
                    }
                });
            }
        }

        //        var url = OC.MVC.util.getLink("CommunicationsPreferences", "Confirm");

        var process = function (parameters) {
            _obj.validator = form.validate(IC.Public.getValidatorCfg());
            if (form.valid()) {
                IC.Public.showLoading("");
                $.ajax({
                    url: url,
                    type: 'POST',
                    data: data,
                    success: function (result) {
                        if (result && result.Status == "SUCCESS") {
                            var url = OC.MVC.util.getLink("CommunicationsPreferences", "ConfirmPage");
                            OC.MVC.util.loadMainContainerView(url, { cacheKey: $('#CacheKey').val() });
                        }
                        else {
                            var emailAddress = $('[name="EmailAddress"]');
                            IC.Public.removeErrorFor(emailAddress, form, result.ErrorMessage);
                            IC.Public.displayErrorFor(emailAddress, form, result.ErrorMessage, 'error');
                        }
                        IC.Public.hideLoading();
                    },
                    error: function (err) {
                        IC.Public.hideLoading();
                        OC.MVC.util.errorMessage(err.responseText);
                    }
                });
            }
        };
        }
    },
    onSkipClick: function (events) {
        this.doNotDisplayAgain = $("input[name='DoNotDisplayAgain']");
        if ($(this.doNotDisplayAgain).is(':checked')) {
            IC.Public.showLoading("");
            $.ajax({
                url: OC.MVC.util.getLink("CommunicationsPreferences", "SavePortfolioCampaignDoNotDisplayTickBox"),
                type: 'POST',
                success: function (result) {
                    if ($('#IsMissingFATCACRSEnabled').val().toLowerCase() == "true") {
                        if ($('#IsEmployeeLogin').val() != undefined && $('#IsEmployeeLogin').val().toLowerCase() == "true") {
                            var urlFATCA = "/Employee/" + $('#IssuerCode').val() + "/ForeignTaxResidency/ForeignTaxResidencyCertificationIndex?isRedirectedFromLoginPage=" + true;
                        } else {
                            var urlFATCA = "/ForeignTaxResidency/ForeignTaxResidencyCertificationIndex?isRedirectedFromLoginPage=" + true;
                        }
                        window.location.href = urlFATCA;
                    }
                    else if ($('#IsEmployeeLogin').val() != undefined && $('#IsEmployeeLogin').val().toLowerCase() == "true") {
                        window.location.href = "/Employee/" + $('#IssuerCode').val();

                    } else {
                        var urlHoldings = "/";
                        window.location.href = urlHoldings;
                    }
                    IC.Public.hideLoading();
                },
                error: function (err) {
                    $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                    IC.Public.hideLoading("");
                }
            });
        } else {
            if ($('#IsMissingFATCACRSEnabled').val().toLowerCase() == "true") {
                if ($('#IsEmployeeLogin').val() != undefined && $('#IsEmployeeLogin').val().toLowerCase() == "true") {
                    var urlFATCA = "/Employee/" + $('#IssuerCode').val() + "/ForeignTaxResidency/ForeignTaxResidencyCertificationIndex?isRedirectedFromLoginPage=" + true;
                } else {
                    var urlFATCA = "/ForeignTaxResidency/ForeignTaxResidencyCertificationIndex?isRedirectedFromLoginPage=" + true;
                }
                window.location.href = urlFATCA;
            }
            else if ($('#IsEmployeeLogin').val() != undefined && $('#IsEmployeeLogin').val().toLowerCase() == "true") {
                window.location.href = "/Employee/" + $('#IssuerCode').val();

            } else {
                var urlHoldings = "/";
                window.location.href = urlHoldings;
            }
        }
    },
    onFilterGrid: function (events, viewKey) {
        var _obj = events.data.obj;
        if (!_obj.isLoading) {
            var url = OC.MVC.util.getLink("CommunicationsPreferences", "Render");
            var data = { viewKey: viewKey };
            OC.MVC.util._loadView("viewBarDetails", url, data);
        }
    },
    onSettingsClick: function (events) {
        var url = OC.MVC.util.getLink("Settings", "Settings");
        OC.MVC.util.loadMainContainerView(url);
    },
    onEmailAddressRadioChange: function (events) {
        var _obj = events.data.obj;
        _obj.refreshEmailAddressControls();
    },
    refreshEmailAddressControls: function () {
        var checkedOption = $("input[name='EmailAddressRadioCode']:radio:checked").get(0);
        if (checkedOption != undefined) {
            if ($(checkedOption).val() == "UserName") {
                $("#EmailAddress").prop('disabled', true);
            }
            else if ($(checkedOption).val() == "EmailAddress") {
                $("#EmailAddress").prop('disabled', false);
            }
        }
    },
    onDoNotDisplayClick: function (events) {

        if ($(this).is(':checked')) {
            $('#btnUpdate').parent().addClass('disabled');
        } else {
            $('#btnUpdate').parent().removeClass('disabled');
        }
    },

    init: function () {
        $("#errorContainer").hide();
        var missingEcomm = null;
        var fromLogin = null;
        if ($('#IsNavigatedfromLoginScreen').val() != undefined && $('#IsNavigatedfromLoginScreen').val().toLowerCase() == "true")
            fromLogin = true;
        if ($('#IsMissingCommunicationDetailsEnabled').val() != undefined && $('#IsMissingCommunicationDetailsEnabled').val().toLowerCase() == "true")
            missingEcomm = true;
        if (fromLogin && missingEcomm) {
            IC.Public.HideMenu();
        }


        var btnSkip = $("#btnSkip");
        btnSkip.bind("click", { obj: this }, this.onSkipClick);
        highlightMenu('communication');
        this.applyUpdatesGrid = $("#bdiv_ApplyHoldingsGrid");
        
        if ($("#portfolioMenuLink").attr("type") != undefined)
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");

        this.holdingFilter = $("form.holdingFilterForm");
        this.holdingFilter.unbind("applyHoldingFilter");
        this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterGrid);

        this.btnUpdate = $("#btnUpdate");
        this.btnUpdate.unbind("click");
        this.btnUpdate.bind("click", { obj: this }, this.onUpdateClick);
        this.doNotDisplayAgain = $("input[name='DoNotDisplayAgain']");
        this.doNotDisplayAgain.unbind('click');
        this.doNotDisplayAgain.bind('click', { obj: this }, this.onDoNotDisplayClick);

        this.ShowDdnMsg = false;
        $("#dialog-confirm").hide();
        $("#dialog-confirm-all").hide();

        $("input[name='AnnualReportRadioCode']").unbind('change');
        $("input[name='AnnualReportRadioCode']").bind('change', { obj: this }, function (events) {
            var _obj = events.data.obj;
            //Display a popup msg if change from DDN (Australian Digital Post) to any other option
            if (_obj.ShowDdnMsg == false && $('#IsAnnualReportRadioCodeDDN').val().toUpperCase() == 'TRUE' && $('#AnnualReportRadioCode_DDN').is(':checked') == false) {
                //alert('Once you change your election from Australia Post Digital Mailbox you cannot re-elect this option from the Link website, please call our contact centre on 1300 554 474 if you need to reactivate this');
                $("#dialog-confirm").dialog({
                    modal: true,
                    buttons: {
                        Ok: function () {
                            $(this).dialog("close");
                        },
                        Cancel: function () {
                            $('#AnnualReportRadioCode_DDN').attr('checked', true);
                            $(this).dialog("close");
                        }
                    }
                });
                //_obj.ShowDdnMsg = true;
            }
        });

        this.lnkSettings = $("#lnkSettings");
        this.lnkSettings.unbind("click");
        this.lnkSettings.bind("click", { obj: this }, this.onSettingsClick);

        this.lnkOtherOptionsOn = $("#lnkOtherOptionsOn");
        this.lnkOtherOptionsOn.unbind("click");
        this.lnkOtherOptionsOn.bind("click", { obj: this }, function (events) {
            var _obj = events.data.obj;
            _obj.lnkOtherOptionsOn.hide();
            _obj.lnkOtherOptionsOff.show();
            $("div.divOtherOptions").show();
        });

        this.lnkOtherOptionsOff = $("#lnkOtherOptionsOff");
        this.lnkOtherOptionsOff.unbind("click");
        this.lnkOtherOptionsOff.bind("click", { obj: this }, function (events) {
            var _obj = events.data.obj;
            _obj.lnkOtherOptionsOn.show();
            _obj.lnkOtherOptionsOff.hide();
            $("div.divOtherOptions").hide();
        });

        if ($("#AnnualReportRadioCode_EEN:checked").length == 0) {
            this.lnkOtherOptionsOn.click();
        }
        else {
            this.lnkOtherOptionsOff.click();
        }

        $("input[name='EmailAddressRadioCode']").unbind('change');
        $("input[name='EmailAddressRadioCode']").bind('change', { obj: this }, this.onEmailAddressRadioChange);
        this.refreshEmailAddressControls();

        this.AnnualReportListCode = $("#AnnualReportListCode");
        this.AnnualReportListCode.unbind("click");
        this.AnnualReportListCode.bind("change", null, function () {
            $("#AnnualReportListText").val($(this).find("option:selected").text());
        });

        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnUpdate').click();
                }
            }
        });

        var isThirdPartyImpersonateUser = $("#IsThirdPartyImpersonateUser").val();
        if (isThirdPartyImpersonateUser != undefined) {
            if (isThirdPartyImpersonateUser.toLowerCase() == "true") {
                //$("#btnUpdate").attr('disabled', true);
                $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
                IC.Public.disableControls('PreferencesForm');
                $("#ViewKey").removeAttr('disabled');
            } else {
                $("#btnUpdate").removeAttr('disabled');
            }
        }
    }
}
;IC.Public.customPreferencesNzl = function(options) {
}

IC.Public.customPreferencesNzl.prototype = {
    onUpdateClick: function(events) {
        var _obj = events.data.obj;
        var checkedOption = $("input[name='EmailAddressRadioCode']:radio:checked").get(0);
        if (checkedOption != undefined) {
            if ($(checkedOption).val() == "UserName") {
                $("#EmailAddress").val("");
            }
        }
        var form = $("form#PreferencesForm");
        var data = form.serialize();

        if ($('#IsOtherHoldingsVisible').val().toLowerCase() == "true") {
            var selectedOption = $("#ApplyToAllHoldings_1:checked").get(0);
            var selector = ".updatecheckbox:checked";
            if (($("#IsShowOtherHoldings:checked").val() != undefined) && (selectedOption != undefined)) {
                selector = ".updatecheckbox";
            }
            var checkedValues = $(selector).map(function() { return $(this).val(); }).get();
            if (checkedValues) {
                data += '&HoldingIDs=' + checkedValues.join(',');
            }
            data += "&ApplyToAllHoldings=" + $("[name='ApplyToAllHoldings']:checked").val();
        } else {
            data += '&HoldingIDs=' + $('#RowsToBeUpdated').val();
        }

        var url = OC.MVC.util.getLink("CommunicationsPreferences", "Confirm");

        _obj.validator = form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function(result) {
                    if (result && result.Status == "SUCCESS") {
                        var url = OC.MVC.util.getLink("CommunicationsPreferences", "ConfirmPage");
                        OC.MVC.util.loadMainContainerView(url, { cacheKey: $('#CacheKey').val() });
                    }
                    else {
                        var emailAddress = $('[name="EmailAddress"]');
                        IC.Public.removeErrorFor(emailAddress, form, result.ErrorMessage);
                        IC.Public.displayErrorFor(emailAddress, form, result.ErrorMessage, 'error');
                    }
                    IC.Public.hideLoading();
                },
                error: function(err) {
                    IC.Public.hideLoading();
                    OC.MVC.util.errorMessage(err.responseText);
                }
            });
        }

    },
    onFilterGrid: function(events, viewKey) {
        var _obj = events.data.obj;
        if (!_obj.isLoading) {
            var url = OC.MVC.util.getLink("CommunicationsPreferences", "Render");
            var data = { viewKey: viewKey };
            OC.MVC.util._loadView("viewBarDetails", url, data);
        }
    },
    onSettingsClick: function(events) {
        var url = OC.MVC.util.getLink("Settings", "Settings");
        OC.MVC.util.loadMainContainerView(url);
    },
    onAnnualReportRadioChange: function(events) {
        var _obj = events.data.obj;
        _obj.refreshCommsOptionsControls(_obj);
    },
    refreshCommsOptionsControls: function(obj) {
        var checkedOption = $("input[name='AnnualReportRadioCode']:radio:checked").get(0);
        if (checkedOption != undefined) {
            obj.divElectronicComms.hide();
            obj.divMailedComms.hide();

            if ($(checkedOption).val() == "EEN") {
                return;
            }
            else if ($(checkedOption).val() == "E") {
                obj.divElectronicComms.show();
            }
            else if ($(checkedOption).val() == "N") {
                obj.divMailedComms.show();
            }
        }
    },
    onEmailAddressRadioChange: function(events) {
        var _obj = events.data.obj;
        _obj.refreshEmailAddressControls();
    },
    refreshEmailAddressControls: function() {
        var checkedOption = $("input[name='EmailAddressRadioCode']:radio:checked").get(0);
        if (checkedOption != undefined) {
            if ($(checkedOption).val() == "UserName") {
                $("#EmailAddress").prop('disabled', true);
            }
            else if ($(checkedOption).val() == "EmailAddress") {
                $("#EmailAddress").prop('disabled', false);
            }
        }
    },

    init: function() {
        highlightMenu('communication');
        this.applyUpdatesGrid = $("#bdiv_ApplyHoldingsGrid");

        this.holdingFilter = $("form.holdingFilterForm");
        this.holdingFilter.unbind("applyHoldingFilter");
        this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterGrid);

        this.btnUpdate = $("#btnUpdate");
        this.btnUpdate.unbind("click");
        this.btnUpdate.bind("click", { obj: this }, this.onUpdateClick);

        this.divElectronicComms = $("#divElectronicComms");
        this.divElectronicComms.hide();

        this.divMailedComms = $("#divMailedComms");
        this.divMailedComms.hide();

        $("input[name='AnnualReportRadioCode']").unbind('change');
        $("input[name='AnnualReportRadioCode']").bind('change', { obj: this }, this.onAnnualReportRadioChange);
        this.refreshCommsOptionsControls(this);

        this.lnkSettings = $("#lnkSettings");
        this.lnkSettings.unbind("click");
        this.lnkSettings.bind("click", { obj: this }, this.onSettingsClick);

        this.lnkOtherOptionsOn = $("#lnkOtherOptionsOn");
        this.lnkOtherOptionsOn.unbind("click");
        this.lnkOtherOptionsOn.bind("click", { obj: this }, function(events) {
            var _obj = events.data.obj;
            _obj.lnkOtherOptionsOn.hide();
            _obj.lnkOtherOptionsOff.show();
            $("div.divOtherOptions").show();
        });

        this.lnkOtherOptionsOff = $("#lnkOtherOptionsOff");
        this.lnkOtherOptionsOff.unbind("click");
        this.lnkOtherOptionsOff.bind("click", { obj: this }, function(events) {
            var _obj = events.data.obj;
            _obj.lnkOtherOptionsOn.show();
            _obj.lnkOtherOptionsOff.hide();
            $("div.divOtherOptions").hide();
        });

        this.lnkOtherOptionsOn.click();

        $("input[name='EmailAddressRadioCode']").unbind('change');
        $("input[name='EmailAddressRadioCode']").bind('change', { obj: this }, this.onEmailAddressRadioChange);
        this.refreshEmailAddressControls();

        this.AnnualReportListCode = $("#AnnualReportListCode");
        this.AnnualReportListCode.unbind("change");
        this.AnnualReportListCode.bind("change", null, function() {
            $("#AnnualReportListText").val($(this).find("option:selected").text())
        });

        this.PaymentAdviceListCode = $("#PaymentAdviceListCode");
        this.PaymentAdviceListCode.unbind("change");
        this.PaymentAdviceListCode.bind("change", null, function() {
            $("#PaymentAdviceListText").val($(this).find("option:selected").text())
        });

        this.MailedAnnualReportListCode = $("#MailedAnnualReportListCode");
        this.MailedAnnualReportListCode.unbind("change");
        this.MailedAnnualReportListCode.bind("change", null, function() {
            $("#MailedAnnualReportListText").val($(this).find("option:selected").text())
        });

        $('html').bind('keypress', function(e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnUpdate').click();
                }
            }
        });
    }
}
;IC.Public.communicationsconfirm = function(options) {
}

IC.Public.communicationsconfirm.prototype = {
    onBackClick: function (events) {
        var missingEcomm = null;
        var fromLogin = null;
        if ($('#IsNavigatedfromLoginScreen').val() != undefined && $('#IsNavigatedfromLoginScreen').val().toLowerCase() == "true")
            fromLogin = true;
        if ($('#IsMissingCommunicationDetailsEnabled').val() != undefined && $('#IsMissingCommunicationDetailsEnabled').val().toLowerCase() == "true")
            missingEcomm = true;
        if (missingEcomm && fromLogin) {

            var urlComm = OC.MVC.util.getLink("CommunicationsPreferences", "UpdatePageByCacheKey");
            OC.MVC.util._loadView(OC.MVC.constants.mainContainer, urlComm, { cacheKey: $('#CacheKey').val() });
        } else {
            var url = OC.MVC.util.getLink("CommunicationsPreferences", "Preferences");
            OC.MVC.util._loadView(OC.MVC.constants.mainContainer, url, { cacheKey: $('#CacheKey').val() });

        }
    },
    onConfirmClick: function (events) {
        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handleUpdateCommunicationPreferencesConfirm();
            return;
        }

        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        var formData = $('form#ConfirmForm').serializeObject();
        IC.Public.security.validateTransactionPin(formData, function () { self.handleUpdateCommunicationPreferencesConfirm(); }, function () { $("#btnConfirm").parent().addClass('disabled'); });
    },

    handleUpdateCommunicationPreferencesConfirm: function () {
        var url = OC.MVC.util.getLink("CommunicationsPreferences", "UpdateComplete");
        var form = $("form#ConfirmForm");
        var data = form.serialize();
        form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function (result) {
                    OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                },
                error: function (err) {
                    OC.MVC.util.errorMessage(err.responseText);
                }
            });
        }
    },
    
    init: function (validateTransactionPassword) {
        this.validateTransactionPassword = validateTransactionPassword;
        highlightMenu('communication');
        $("#btnBack").bind("click", { obj: this }, this.onBackClick);
        $("#btnConfirm").bind("click", { obj: this }, this.onConfirmClick);
        $("#ViewKey").attr("disabled", true);

        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnConfirm').click();
                }
            }
        });
    }
}
;
IC.Public.savePreferences = function(options) {
}

IC.Public.savePreferences.prototype = {
    onReturnClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("Communications", "Preferences");
        IC.Public.showLoading("");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url);
        //IC.Public.hideLoading();        
    },
    init: function() {
        highlightMenu('communication');
        this.btnReturnHoldings = $("#btnReturnHoldings");
        this.btnReturnHoldings.button();
        this.btnReturnHoldings.bind("click", { obj: this }, this.onReturnClick);
    }
}
;IC.Public.Statement = function () {
    this.init();
};

IC.Public.Statement.prototype = {
    init: function () {
        //ViewBar change event
        var self = this;

        this.statementTypeList = $("select[name='StatementType']");
        this.statementTypeList.unbind("change");
        this.statementTypeList.bind("change", { obj: this }, this.onGoClick);

        this.issuerList = $("select[name='Issuer']");
        this.issuerList.unbind("change");
        this.issuerList.bind("change", { obj: this }, this.onGoClick);

        this.statementsGrid = $("#StatementsGrid");

        this.statementsGrid.bind("grid_gridComplete", { obj: this }, function (events) {
            var _obj = events.data.obj;
            $("#dtFrom").val(_obj.dateFrom.val());
            $("#dtTo").val(_obj.dateTo.val());
            $(this).find(".pdfStatement").bind("click", _obj.onpdfStatementClick);
        });
        //this.form = $('#statementForm');
        //var holdingFilter = $("form.holdingFilterForm");
        //if (holdingFilter.length) {
        //    holdingFilter.unbind("applyHoldingFilter");
        //    holdingFilter.bind("applyHoldingFilter", { obj: self }, self.onFilterClick);
        //}
        this.form = $('#statementForm');
        this.holdingFilter = $("form.holdingFilterForm");
        if (this.holdingFilter.length) {
            this.holdingFilter.unbind("applyHoldingFilter");
            this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterClick);
        }

        this.btnAnnualTax = $("#btStatementAnnualTax");
        this.btnAnnualTax.button();
        this.btnAnnualTax.bind("click", { obj: this }, this.onAnnualTaxClicked);

        this.btnPaymentAdvice = $("#btnStatementPaymentAdvice");
        this.btnPaymentAdvice.button();
        this.btnPaymentAdvice.bind("click", { obj: this }, this.onPaymentAdviceClicked);

        this.btnGo = $("#btnGo");
        this.btnGo.button();
        this.btnGo.bind("click", { obj: this }, this.onGoClick);
        this.dateFrom = $("[name='DateFrom']");
        this.dateTo = $("[name='DateTo']");
        this.dateFrom.datepicker('option', { maxDate: new Date() });
        this.dateTo.datepicker('option', { maxDate: new Date() });
    },
    onFilterClick: function (events) {
        var url = OC.MVC.util.getLink("Statement", "Index");
        OC.MVC.util.loadMainContainerView(url, null);
    },
    onAnnualTaxClicked: function (events) {
        var url = OC.MVC.util.getLink("AnnualTaxStatements", "Index");
        OC.MVC.util._loadView("mainContent", url);
    },
    onPaymentAdviceClicked: function (events) {
        var url = OC.MVC.util.getLink("PaymentHistory", "Index");
        OC.MVC.util._loadView("mainContent", url);
    },
    onpdfStatementClick: function (events) {
        var data = { Id: $(this).val(), dateFrom: $("#dtFrom").val(), dateTo: $("#dtTo").val(), fileName:  $(this).attr("fileName") };

        //todo : refer to url where the advice is controlled
        var url = OC.MVC.util.getLink("Statement", "GetStatementFile");
        document.location = url + '?' + OC.MVC.JSON.ToUrl(data);
    },
    onGoClick: function (events) {
        var _obj = events.data.obj;
        var statementTypeSelected = '', issuerSelected = '';
        //var filterData = _obj.holdingFilter.getHoldingFilterData();
        if (events.data.isLoading != true)// && _obj.securityList.css("display") != "none") {
        {
            statementTypeSelected = _obj.statementTypeList.val();
            issuerSelected = _obj.issuerList.val();
        }

        var reverseDateError = 'From date should be before to date.';
        IC.Public.removeErrorFor(_obj.dateFrom, _obj.form, reverseDateError);
        if (_obj.dateFrom.hasClass('error')) {
            return;
        }
        if (_obj.dateTo.hasClass('error')) {
            return;
        }

        var dates = { dateFrom: _obj.dateFrom.val(), dateTo: _obj.dateTo.val() };
        var issuer = { issuer: issuerSelected };
        var statementType = { statementType: statementTypeSelected };
        var dateFromValue = $.datepicker.parseDate("dd/mm/yy", dates.dateFrom);
        var dateToValue = $.datepicker.parseDate("dd/mm/yy", dates.dateTo);

        if (dateFromValue > dateToValue) {
            IC.Public.displayErrorFor(_obj.dateFrom, _obj.form, reverseDateError);
            return;
        }

        //$('#filterDateFrom').text($.datepicker.formatDate("dd M yy", dateFromValue));
        //$('#filterDateTo').text($.datepicker.formatDate("dd M yy", dateToValue));

        var filterData = _obj.holdingFilter.getHoldingFilterData();
        $.extend(dates, filterData);
        _obj.statementsGrid.appendPostData(dates);
        _obj.statementsGrid.appendPostData(statementType);
        _obj.statementsGrid.appendPostData(issuer);
        IC.Public.registerGA(window.location.hash, _obj.statementsGrid.getPostData());
        _obj.statementsGrid.trigger('reloadGrid', [{ page: 1 }]);
    },
};;IC.Public.login = function (options) {
    if (options) {
        $.extend(this, options);
    }
    this.init();
};

IC.Public.login.prototype = {
    init: function () {

        var self = this;
        OC.MVC.validate.validationMsgProcess();

        if (LockBox) {
            LockBox.isInitialLoad = true;
            LockBox.remove("lastUrl");
        }
        var d = $(".container");
        d.addClass("login");              

        var divOpenBriefing = $('#divOpenBriefing');
        divOpenBriefing.addClass("callout");

        var campaignBodyLeft = $('.campaignBody').find('div.left');
        campaignBodyLeft.html(divOpenBriefing);


        var divOnlineShares = $('#divCampaign');
        var campaignBodyRight = $('#divOnlineShares').html();
        divOnlineShares.html(campaignBodyRight);

        // start:  added for AU login carousel changes
        if ($("#carouselCampaign").length) {
            var carouselCampaign = $('#carouselCampaign');
            $('.campaignBody').html(carouselCampaign);
        }
        //end.

        if ($('#IsGenericCustomCssDisabledForSingleHoldingLogin').val() != undefined &&
            $('#IsGenericCustomCssDisabledForSingleHoldingLogin').val().toLowerCase() == 'true') {
            var d = $(".container");
            d.removeClass("login");

            var singleHolding = $('#loginControls');
            singleHolding.addClass('singleHolding');
        }
        var url = OC.MVC.util.getLink("Login", "Login");
        OC.MVC.history.register("mainBody", url);
        $('#Email').focus();
        var btnNext = $("#btnNext");
        btnNext.unbind('click');
        this.companyCode = $("#CompanyCode");

        btnNext.bind('click', function (events) {
            events.preventDefault();
            var companyCodeList = OC.MVC.autocompleteRegistry.getItem("CompanyCode");
            if (companyCodeList && companyCodeList.selectedAttributes && companyCodeList.selectedAttributes.url) {
                var _url = companyCodeList.selectedAttributes.url;
                self.clearSelectedCode();
                window.open(_url, '_blank');
                companyCodeList.selectedAttributes = null;
            } else {
                var companyCodeValue = self.companyCode.val();
                if (companyCodeValue) {
                    self.clearSelectedCode();
                    window.open('/Employee/' + companyCodeValue, '_blank');
                }
            }
            return false;
        });

        var btnEmployeeLogin = $("#btnEmployeeLogin");
        btnEmployeeLogin.unbind('click');
        btnEmployeeLogin.bind('click', function (events) {
            events.preventDefault();
            window.location = '/EmployeeLogin';
        });
    },

    clearSelectedCode: function () {
        $("#CompanyCode_text").val('');
        this.companyCode.val('');
    }
};

;jQuery.validator.addMethod("equaltoproperty", function (value, element, params) {
    if (this.optional(element)) {
        return true;
    }

    var otherPropertyControl = $("#" + params.otherProperty);
    if (otherPropertyControl == null) {
        return false;
    }

    var otherPropertyValue = otherPropertyControl[0].value;
    return otherPropertyValue == value;
});


jQuery.validator.addMethod("requiredwithproperty", function (value, element, params) {
    if (this.optional(element)) {
        return true;
    }

    var otherPropertyControl = $("#" + params.otherProperty);
    if (otherPropertyControl == null) {
        return false;
    }

    var otherPropertyValue = otherPropertyControl[0].value;

    return !((otherPropertyValue === params.expectedValue) && (value === ""));
});

IC.Public.validation = {
    showValidationMessages: function (form, errors) {
        var errorContainer = form.find("#mainErrorDiv");
        if (typeof (errors) == 'object') {
            $.each(errors, function (index, value) {
                var errorElement = form.find("[name=" + value.Key + "]");
                var errorMessage = value.Value;
                var errorClass = "error";
                IC.Public.displayErrorFor(errorElement, form, errorMessage, errorClass);
            });
        }
    },

    showError: function (form, errorMessage) {
        if (errorMessage) {
            var errorContainer = form.find("#mainErrorDiv");
            var label = $("<label>");
            label.addClass("error");
            label.html(errorMessage);
            var li = $("<li>");
            li.append(label);
            $(errorContainer.find("ol").get(0)).append(li);
        }
    }
    /*
    getErrorMessage: function(values) {
    var result = '';
    $.each(values, function(index, value) {
    result += result ? "<br/>" + value : value;
    });
    return result;
    }*/
};

IC.Public.passwordValidation = {
    initialiseValidationTooltip: function () {
        $('html').click(function () {
            if ($('[data-requirePasswordHelper]')) {
                $(document).trigger('hideCluetip');
            }
        });
        setTimeout(function () {
        $('[data-requirePasswordHelper]').each(function () {
            $(this).cluetip({ local: true, hideLocal: true, multiple: true, width: 350, showTitle: false, titleAttribute: '', sticky: true });
            $(this).blur(function () { $(document).trigger('hideCluetip'); });
            $(this).click(function (event) {
                event.stopPropagation();
            });
        });
       
          }, 10);

    }
};;//*******************************************************************************************************
//  
//  File:        ic.public.expressLogin.js
//  Author:      Vladimir Nechypurenko
//  Description: JS for ExpressLoginControl.ascx page
//  Review:      
//
//*******************************************************************************************************

IC.Public.expressLogin = function(options) {
    this.init(options);
};

IC.Public.expressLogin.prototype = {
    onLoginClick: function() {
        if ($(this).parent().hasClass("disabled")) return;
        $('form').submit();
    },
    init: function (options) {
        var d = $(".container");
        d.addClass("login");
        d.addClass("expressID");


        var btnLogin = $("#btnLogin");
        btnLogin.bind("click", this.onLoginClick);
        var termsAndConditionsAccepted = $("[name=IsTermConditionsAccepted][type=checkbox]");
        termsAndConditionsAccepted.bind("change", function() {
            //if ($(this).attr("checked")) {
            if ($(this).is(":checked")) {
                btnLogin.parent("span").removeClass("disabled");
            } else {
                btnLogin.parent("span").addClass("disabled");
            }
        });
        termsAndConditionsAccepted.change();
        var isOutsideAustralia = $("#IsOutsideAustralia").val();
        OutsideAustraliaToggle(isOutsideAustralia);

        if (options.hideHeaderTitle) {
            $('span.headerTitle').hide();
        }
    }
};

function OutsideAustraliaToggle(flag) {
    if (String(flag).toLowerCase() == 'false') {
        $("#divInsideAustralia").show();
        $("#divOutsideAustralia").hide();
        $('#SingleHolding_IsOutsideAustraliaSelected').val('False');
        $("#IsOutsideAustralia").val('False');
    } else {
        $("#divInsideAustralia").hide();
        $("#divOutsideAustralia").show();
        $('#SingleHolding_IsOutsideAustraliaSelected').val('True');
        $("#IsOutsideAustralia").val('True');
        $('#SingleHolding_Postcode').val('');
    }
    //$("#mainErrorDiv").find("li").remove();
}


function OutsideAustraliaToggleEmp(flag) {
    if (String(flag).toLowerCase() == 'false') {
        $("#divInsideAustraliaEmp").show();
        $("#divOutsideAustraliaEmp").hide();
        $('#EmployeeSingleHolding_IsOutsideAustraliaSelected').val('False');
        $("#IsOutsideAustraliaEmp").val('False');
    } else {
        $("#divInsideAustraliaEmp").hide();
        $("#divOutsideAustraliaEmp").show();
        $('#EmployeeSingleHolding_IsOutsideAustraliaSelected').val('True');
        $("#IsOutsideAustraliaEmp").val('True');
        $('#EmployeeSingleHolding_Postcode').val('');
    }
};

;IC.Public.chart = {    
    buildChart: function (data, chart, messages) {
        var dataPoints = [];
        var tickPositions = [];

        dataPoints = IC.Public.chart.extractChartDataPoints(data);
        tickPositions = IC.Public.chart.extractChartTickPositions(data);

        $.each(dataPoints, function (index, dataSeries) {
            var displayBoundaryLineLegend = (dataSeries.ShowGraphLines && dataSeries.ChangeYAxisValues !== true);
            if (displayBoundaryLineLegend) {
                chart.addSeries({ name: messages.MaxPercentitleLegendText, color: IC.Public.Plans.Common.GraphLineMaxValueColor }, true);
            }
            chart.addSeries({ name: dataSeries.name, data: dataSeries.data }, false);
            if (displayBoundaryLineLegend) {
                chart.addSeries({ name: messages.MinPercentitleLegendText, color: IC.Public.Plans.Common.GraphLineMinValueColor }, true);
            }
        });

        chart.xAxis[0].update({ tickPositions: tickPositions });

        chart.redraw();
        chart.hideLoading();
    },
    
    extractChartDataPoints: function (inputData) {
        var dataPoints = [];
        
        if (inputData != null) {
            $.each(inputData.Series, function (index, series) {
                var tempSeries = [];
                $.each(series.Points, function (j, dataPoint) {
                    tempSeries.push({ x: IC.Public.chart.convertToUtc(dataPoint.XValue), y: dataPoint.YValue });
                });
                dataPoints.push({ name: inputData.Series[index].Name, data: tempSeries, ShowGraphLines: series.ShowGraphLines, ChangeYAxisValues: series.ChangeYAxisValues });
            });
        }

        return dataPoints;
    },

    extractChartTickPositions: function (inputData) {
        var tickPositions = [];
        
        if (inputData != null) {
            $.each(inputData.TickPositions, function (index, tick) {
                tickPositions.push(IC.Public.chart.convertToUtc(tick));
            });
        }

        return tickPositions;
    },

    convertToUtc: function (inputDate) {
        /*
        * month in javascript date system is zero based
        * so need to decrease month by one
        */
        var month = inputDate.substring(4, 6);
        var monthValue = parseInt(month, '10') - 1;
        return Date.UTC(inputDate.substring(0, 4), monthValue, inputDate.substring(6));
    }
};;IC.Public.applyotherholdings = function (options) {
}

IC.Public.applyotherholdings.prototype = {
    securityCodeTooltip: function (cellValue, options, rowObject) {
        var _this = IC.Public.applyotherholdings;
        var div = $('<div>');
        var span = $('<span>');
        var index = $(this).getColIndex("CodeDesc");
        if (index >= 0) {
            span.prop("title", rowObject[index]);
        }

        span.addClass("tooltip");
        span.html(cellValue);
        span.appendTo(div);
        return div.html();
    },
    onCheckChange: function (events) {
        var _obj = events.data.obj;

        if ($(this).is(":checked")) {
            _obj.radioButtonlist.show();
            $("#SomeHoldingsText").show();
            $("input[name='ApplyToAllHoldings']:checked").trigger("change");
        } else {
            _obj.radioButtonlist.hide();
            _obj.otherHoldings.hide();
            $("#SomeHoldingsText").hide();
            $(".updatecheckbox").each(function () {
                if ($(this).prop("originalValue") == "true")
                    $(this).prop("checked", true);
                else
                    $(this).prop("checked", false);
            });
        }
    },
    showMsgBar: function () {
        $("#msgBar").show();
        $("#lnkReadMore").hide();
    },
    onSelectRows: function (events) {
        var _obj = events.data.obj;
        var selectAll = events.data.selectAll;
        var chks = _obj.bdiv_ApplyHoldingsGrid.find(".updatecheckbox[type='checkbox']:enabled");
        if (chks.length > 0) {
            if (selectAll) {
                chks.prop("checked", true);
            } else {
                chks.each(function () {
                    this.checked = false;
                });
            }
        }
    },
    //check all previously selected Holdings
    onApplyHoldingsGridComplete: function (events, table, rows) {
        var _obj = events.data.obj;
        if (rows && rows.length > 0) {
            var selectedRows = _obj.RowsToBeUpdated.val().split(',');
            if ($('#IsNavigatedfromLoginScreen').val().toLowerCase() == "true") {

                //if (selectedRows.length > 1) {
                events.data.selectAll = false;
                _obj.onSelectRows(events);
                $.each(selectedRows, function (index, value) {
                    if ((index == 0) || (index % 2 == 0)) {
                        var key = value;
                        if (selectedRows[index + 1]) {
                            key += ',' + selectedRows[index + 1];
                        }

                        $("input.updatecheckbox[value*='" + key + "']").prop("checked", true);
                    }
                });
                //}
            } else {
                var chks = _obj.bdiv_ApplyHoldingsGrid.find(".updatecheckbox[type='checkbox']:enabled");
                if (chks.length == 0) {
                    _obj.chkShowHoldings.prop("checked", false);
                    _obj.chkShowHoldings.prop("disabled", true);
                    _obj.chkShowHoldings.trigger("change");
                } else if (selectedRows.length > 1) {
                    events.data.selectAll = false;
                    _obj.onSelectRows(events);
                    $.each(selectedRows, function (index, value) {
                        if ((index == 0) || (index % 2 == 0)) {
                            var key = value;
                            if (selectedRows[index + 1]) {
                                key += ',' + selectedRows[index + 1];
                            }

                            $("input.updatecheckbox[value*='" + key + "']").prop("checked", true);
                        }
                    });
                }

                var updateCheckbox = $('.updatecheckbox');
                updateCheckbox.unbind('click');
                updateCheckbox.bind('click', { obj: _obj }, _obj.processNextButton);
            }
            if (IC.Public.IsThirdPartyImpersonateUser()) {
                $("#otherHoldings input").prop("checked", true);
                IC.Public.disableControls('otherHoldings');
                $("#ViewKey").prop("disabled", false);
                $("#btnBackToAuthorisedPortfolio").prop("disabled", false);
            }
        } else {
            _obj.chkShowHoldings.prop("checked", false);
            _obj.chkShowHoldings.prop("disabled", true);
            _obj.chkShowHoldings.trigger("change");
        }
    },

    processNextButton: function (eventArgs) {
        var self = eventArgs.data.obj;
        var nextButton = $("#btnNext");
        var updateButton = $("#btnUpdate");

        if ($('.updatecheckbox').is(':checked') === true) {
            if (nextButton != undefined) {
                nextButton.parent().removeClass('disabled');
            }
            if (updateButton != undefined) {
                updateButton.parent().removeClass('disabled');
            }
            return;
        }

        if (self.isAllHoldingsNotRegistered) {
            if (nextButton != undefined) {
                nextButton.parent().addClass('disabled');
            }
            if (updateButton != undefined) {
                updateButton.parent().addClass('disabled');
            }
        }
    },

    init: function () {
        if ($('#IsNavigatedfromLoginScreen').val() != undefined && $('#IsNavigatedfromLoginScreen').val().toLowerCase() == "true") {
            $("#btnSelectall").bind("click", { obj: this, selectAll: true }, this.onSelectRows);
            $("#btnUnselectall").bind("click", { obj: this, selectAll: false }, this.onSelectRows);

            this.bdiv_ApplyHoldingsGrid = $("#bdiv_ApplyHoldingsGrid");
            this.RowsToBeUpdated = $("[name='RowsToBeUpdated']");
            if (this.RowsToBeUpdated.length > 0) {
                this.bdiv_ApplyHoldingsGrid.bind("grid_gridComplete", { obj: this }, this.onApplyHoldingsGridComplete);
            }
            $(".updatecheckbox[type='checkbox']:enabled").each(function () {
                if ($(this).prop("originalValue") == "true") {
                    $(this).prop("checked", true);
                    $(this).prop("disabled", false);
                } else
                    $(this).prop("checked", false);
            });
        } else {
            this.otherHoldings = $("#otherHoldings");
            this.otherHoldings.hide();
            $("#lnkReadMore").bind("mousedown", { obj: this }, this.showMsgBar);
            this.radioButtonlist = $("#radioButtonlist");
            this.radioButtonlist.hide();

            this.bdiv_ApplyHoldingsGrid = $("#bdiv_ApplyHoldingsGrid");
            this.RowsToBeUpdated = $("[name='RowsToBeUpdated']");
            if (this.RowsToBeUpdated.length > 0) {
                this.bdiv_ApplyHoldingsGrid.bind("grid_gridComplete", { obj: this }, this.onApplyHoldingsGridComplete);
            }

            this.chkShowHoldings = $('#IsShowOtherHoldings');
            this.chkShowHoldings.bind("change", { obj: this }, this.onCheckChange);
            this.chkShowHoldings.trigger("change");

            $("#btnSelectall").bind("click", { obj: this, selectAll: true }, this.onSelectRows);
            $("#btnUnselectall").bind("click", { obj: this, selectAll: false }, this.onSelectRows);

            var applyToAllHoldings = $("input[name='ApplyToAllHoldings']");

            applyToAllHoldings.unbind('change');
            applyToAllHoldings.bind('change', function () {
                var option = $(this).val();
                var display = $('#IsShowOtherHoldings:checked').get(0);
                if ((option != 'true') && (display != undefined)) {
                    $("#otherHoldings").show();
                } else {
                    $("#otherHoldings").hide();
                }
            });
            $("input[name='ApplyToAllHoldings']:checked").trigger("change");

            var isAllHoldingsNotRegisteredValue = $('#IsAllHoldingsNotRegistered').val();
            this.isAllHoldingsNotRegistered = isAllHoldingsNotRegisteredValue != undefined && isAllHoldingsNotRegisteredValue.toLowerCase() == "true";
        }
    }
};

;IC.Public.printableForms = function(options) {
};
IC.Public.printableForms.prototype = {
    onTaxDetailsClick: function(events) {
        var url = OC.MVC.util.getLink("UpdateTaxDetails", "GetTaxDetails");
        OC.MVC.util.loadMainContainerView(url);
    },
    onCommunicationPreferencesClick: function(events) {
        var url = OC.MVC.util.getLink("CommunicationsPreferences", "Preferences");
        OC.MVC.util.loadMainContainerView(url);
    },
    onManageUsersClick: function(events) {
        var url = OC.MVC.util.getLink("Authorisation/Authorisation", "ManageUsers");
        OC.MVC.util.loadMainContainerView(url);
    },
    
    onPaymentInstructionsClick: function(events) {
        var url = OC.MVC.util.getLink("UpdatePaymentInstructions", "Index");
        OC.MVC.util.loadMainContainerView(url);
    },
    onChangeofAddressClick: function(events) {
        var url = OC.MVC.util.getLink("Address", "Edit");
        OC.MVC.util.loadMainContainerView(url);
    },
    onReinvestmentPlansClick: function(events) {
        var url = OC.MVC.util.getLink("ReinvestmentPlans", "Index");
        OC.MVC.util.loadMainContainerView(url);
    },
    onVotingClick: function(events) {
        var url = OC.MVC.util.getLink("ProxyVotingList", "Votes");
        OC.MVC.util.loadMainContainerView(url);
    },
    init: function() {
        highlightMenu('forms');
        if ($("#portfolioMenuLink").attr("type") != undefined && $("#portfolioMenuLink").attr("type").toLowerCase() == "authorisedportfolio") {
            //$("#mainMenu").css("visibility", "hidden");
            $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
        } else {
            $("#mainMenu").css("visibility", "visible");
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        }
        $("#lnkTaxDetails").bind("click", { obj: this }, this.onTaxDetailsClick);
        $("#lnkCommunicationPreferences").bind("click", { obj: this }, this.onCommunicationPreferencesClick);
        $("#lnkManageUsers").bind("click", { obj: this }, this.onManageUsersClick);
        
        $("#lnkPaymentInstructions").bind("click", { obj: this }, this.onPaymentInstructionsClick);
        $("#lnkChangeofAddress").bind("click", { obj: this }, this.onChangeofAddressClick);
        $("#lnkReinvestmentPlans").bind("click", { obj: this }, this.onReinvestmentPlansClick);
        $("#lnkVoting").bind("click", { obj: this }, this.onVotingClick);

        $('h2.dropdownOpener a').click(function () { $(this).closest('h2').next('ul').toggle(); });

        //initially open the first one
        $('h2.dropdownOpener a').first().closest('h2').next('ul').toggle().fadeOut(100).fadeIn(700);
      
    }
};;//*******************************************************************************************************
//
//  File:        ic.controls.disabledList.js
//  Author:      Vladimir Nechypurenko
//  Description: Format for Disabled Holdings list
//  Review:      
//
//*******************************************************************************************************

IC.Controls.disabledList = {
    exclusionCriterias: [
       { code: "BrokerSponsored", errText: "Holding is broker sponsored." },
       { code: "HoldingValue", errText: "Holding value exceeds " },
       { code: "JointInvestor", errText: "Holding type is Joint or company." },
       { code: "PaymentType", errText: "Invalid Payment type selected." },
       { code: "InvestorDeceased", errText: "Holding is registered as an estate." },
       { code: "ForeignIssuer", errText: "Holding is registered against a foreign issuer." }
    ],
    getErrorText: function(code) {
        for (var i = 0; i < this.exclusionCriterias.length; ++i) {
            if (this.exclusionCriterias[i].code == code) {
                return this.exclusionCriterias[i].errText;
            }
        }
        return "";
    },
    formatExclusionCriteria: function(cellValue, options, rowObject) {
        var _this = IC.Controls.disabledList;
        var result = $('<ul>');
        result.addClass('genericList');

        if (cellValue && cellValue != '' && cellValue != 'null') {
            var errCodes = cellValue.split(',');
            var updateMaxHolderValue = 'online threshold';//'$' + $.fmatter.util.NumberFormat($('#UpdateMaxHolderValue').val(), { decimalSeparator: ".", thousandsSeparator: ",", decimalPlaces: 0 });
            $.each(errCodes, function(index, value) {
                if (value != '') {
                    var errText = _this.getErrorText(value);
                    if (errText != '') {
                        if (errCodes[index] == 'HoldingValue') {
                            errText += updateMaxHolderValue + '.';
                        }

                        var li = $('<li>');
                        var li_text = '- ' + errText;
                        li.html(li_text);
                        li.appendTo(result);
                    }
                }
            });
            return result;
        }

        return result;
    },
    regDetails: function(cellValue, options, rowObject) {
        if (cellValue && cellValue != '' && cellValue != 'null') {
            var span = $('<span>');
            span.html(cellValue);
            if (cellValue.length > 29) {
                span.addClass('tooltip');
                span.attr('title', cellValue);
            }
            return span;

        }
        return '';
    }
};
;IC.Public.addressconfirm = function (options) {
};

IC.Public.addressconfirm.prototype = {
    onBackClick: function (events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("Address", "Edit");
        IC.Public.showLoading("");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url, { cacheKey: $('#CacheKey').val() });
        //IC.Public.hideLoading();
    },
    onConfirmClick: function (events) {
        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handleAddressConfirmClick();
            return;
        }

        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        var isMultiMobileExist = $("#HrnsRequiringPinValidation_IsMultiMobileExist").val();
        if (isMultiMobileExist == "True") {
            self.gotoValidateSmsPin();
        } else {
            if (!$('div.transaction_password').length) {
                self.handleAddressConfirmClick();
                return;
            }
            var formData = $('form#confirmAddress').serializeObject();
            IC.Public.security.validateTransactionPin(formData, function () { self.handleAddressConfirmClick(); }, function () { $("#btnConfirm").parent().addClass('disabled'); });
        }
    },

    handleAddressConfirmClick: function () {
        var url = OC.MVC.util.getLink("Address", "UpdateComplete");
        var form = $("form#confirmAddress");
        var cacheKey = $("#CacheKey").val();
        var data = { cacheKey: cacheKey };
        form.validate(IC.Public.getValidatorCfg());
        IC.Public.showLoading("");
        if (form.valid()) {
            $.ajax({
                url: url, type: 'POST', data: data,
                success: function (result) {
                    $("#" + OC.MVC.constants.mainContainer).html(result);
                    IC.Public.hideLoading();
                },
                error: function (err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },
    gotoValidateSmsPin: function () {
        var url = OC.MVC.util.getLink("Address", "ConfirmValidatePin");
        var form = $("form#confirmAddress");
        var data = form.serialize();
        //data += '&cacheKey=' + $("#CacheKey").val();
        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err);
                IC.Public.hideLoading();
            }
        });
    },
    init: function (validateTransactionPassword) {
        this.validateTransactionPassword = validateTransactionPassword;
        highlightMenu('communication');
        $("#btnBack").bind("click", { obj: this }, this.onBackClick);
        $("#btnConfirm").bind("click", { obj: this }, this.onConfirmClick);
        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnConfirm').click();
                }
            }
        });

    }
}
;IC.Public.addressconfirmSmsVal = function(options) {
};

IC.Public.addressconfirmSmsVal.prototype = {
    onBackClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("Address", "Edit");
        IC.Public.showLoading("");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url, { cacheKey: $('#CacheKey').val() });
        //IC.Public.hideLoading();
    },
    onConfirmClick: function(events) {
        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handleAddressConfirmClick();
            return;
        }

        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        var formData = $('form#confirmAddress').serializeObject();
        IC.Public.security.validateTransactionPin(formData, function () { self.handleAddressConfirmClick(); }, function () { $("#btnConfirm").parent().addClass('disabled'); });
    },
    
    handleAddressConfirmClick: function() {
        var url = OC.MVC.util.getLink("Address", "UpdateComplete");
        var form = $("form#confirmAddress");
        var cacheKey = $("#CacheKey").val();
        var data = { cacheKey: cacheKey };
        form.validate(IC.Public.getValidatorCfg());
        IC.Public.showLoading("");
        if (form.valid()) {
            $.ajax({
                url: url, type: 'POST', data: data,
                success: function(result) {
                    $("#" + OC.MVC.constants.mainContainer).html(result);
                    IC.Public.hideLoading();
                },
                error: function(err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },
    init: function (validateTransactionPassword) {
        this.validateTransactionPassword = validateTransactionPassword;
        highlightMenu('communication');
        $("#btnBack").bind("click", { obj: this }, this.onBackClick);
        $("#btnConfirm").bind("click", { obj: this }, this.onConfirmClick);
        $('html').unbind('keypress');
        $('html').bind('keypress', function(e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnConfirm').click();
                }
            }
        });

    }
}
;IC.Public.checkbox = {
    checkboxColumn: function(cellValue, options, rowObject) {
        var _this = IC.Public.checkbox;
        var index = $(this).getColIndex("Enabled");
        var disabledTag = "";
        if (index >= 0) {
            disabledTag = rowObject[index] == "1" || rowObject[index] == "true" ? "" : "disabled";
        }

        if (disabledTag == "disabled") {
            $("#msgBar").attr("IsShow", "true");
        }

        var originalValue = (cellValue == "1" || cellValue == "true");
        var checked = originalValue ? "checked='checked'" : "";
        var holderKeyIndex = $(this).getColIndex("HolderKey");
        var value = options.rowId;
        if (holderKeyIndex >= 0) { 
           value = rowObject[holderKeyIndex];
        }

        return String.format("<input type='checkbox' value='{0}' {1} originalValue='{3}' class='updatecheckbox' {2}/>", value, checked, disabledTag, originalValue);
    }
}

;IC.Public.updateaddresscontrol = function(options) {
}

IC.Public.updateaddresscontrol.prototype = {
    onCheckChange: function(events) {
        if ($(this).attr('checked')) {
            $("#otherHoldings").show();
        }
        else {
            $("#otherHoldings").hide();
        }
    },
    init: function() {
        highlightMenu('communication');
        $("#otherHoldings").hide();
        this.chkShowHoldings = $("input[name='chkApplyToOtherHoldings']");
        this.chkShowHoldings.attr('checked', false);
        this.chkShowHoldings.bind("change", { obj: this }, this.onCheckChange);
    },
    securityCodeTooltip: function(cellValue, options, rowObject) {
        var _this = IC.Public.updateaddresscontrol;
        var div = $('<div>');
        var span = $('<span>');
        var index = $(this).getColIndex("CodeDesc");
        if (index >= 0) {
            span.attr("title", rowObject[index]);
        }

        span.addClass("tooltip");
        span.html(cellValue);
        span.appendTo(div);
        return div.html();
    }
}
;IC.Public.paymentHistory = function(options) {
    if (options) {
    }
}

IC.Public.paymentHistory.prototype = {
    onGoClick: function(events) {
        var _obj = events.data.obj;

        var reverseDateError = 'From date should be before to date.';
        IC.Public.removeErrorFor(_obj.dateFrom, _obj.form, reverseDateError);
        if (_obj.dateFrom.hasClass('error')) {
            return;
        }
        if (_obj.dateTo.hasClass('error')) {
            return;
        }

        var dates = { dateFrom: _obj.dateFrom.val(), dateTo: _obj.dateTo.val() };
        var dateFromValue = $.datepicker.parseDate("dd/mm/yy", dates.dateFrom);
        var dateToValue = $.datepicker.parseDate("dd/mm/yy", dates.dateTo);

        if (dateFromValue > dateToValue) {
            IC.Public.displayErrorFor(_obj.dateFrom, _obj.form, reverseDateError);
            return;
        }

        $('#filterDateFrom').text($.datepicker.formatDate("dd M yy", dateFromValue));
        $('#filterDateTo').text($.datepicker.formatDate("dd M yy", dateToValue));
        if (dateFromValue == null)
            $('#filterDateFrom').text($('#filterDateFromHidden').val());
        if (dateToValue == null)
            $('#filterDateTo').text($('#filterDateToHidden').val());

        var filterData = _obj.holdingFilter.getHoldingFilterData();
        $.extend(dates, filterData);
        _obj.paymentHistoryGrid.appendPostData(dates);
        IC.Public.registerGA(window.location.hash, _obj.paymentHistoryGrid.getPostData());
        _obj.paymentHistoryGrid.trigger('reloadGrid', [{ page: 1 }]);
    },

    onpdfAdviceClick: function(events) {
        var data = { paymentKey: $(this).val() };

        //todo : refer to url where the advice is controlled
        var url = OC.MVC.util.getLink("PaymentHistory", "PaymentAdvice");
        document.location = url + '?' + OC.MVC.JSON.ToUrl(data);
    },
    onDateBlur: function(events) {
        $(this).dateValidate({ mask: "dd/mm/yy", name: events.data.name, subYears: events.data.subYears });
    },
    init: function() {
        highlightMenu('payment');
        if ($("#portfolioMenuLink").attr("type") != undefined && $("#portfolioMenuLink").attr("type").toLowerCase() == "authorisedportfolio") {
            //$("#mainMenu").css("visibility", "hidden");
            $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
        } else {
            $("#mainMenu").css("visibility", "visible");
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        }
        this.paymentHistoryGrid = $("#PaymentHistoryGrid");
        this.holdingFilter = $("form.holdingFilterForm");
        this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onGoClick);
        this.btnGo = $("#btnGo");
        this.btnGo.button();
        this.btnGo.bind("click", { obj: this }, this.onGoClick);
        this.dateFrom = $("[name=DateFrom]");
        this.dateTo = $("[name=DateTo]");
        this.dateFrom.datepicker('option', { maxDate: new Date() });
        this.dateTo.datepicker('option', { maxDate: new Date() });

        this.paymentHistoryGrid.bind("grid_gridComplete", { obj: this }, function(events) {
            var _obj = events.data.obj;
            $(this).find(".pdfAdvice").bind("click", _obj.onpdfAdviceClick);
            
            var gridRows = $('#PaymentHistoryGrid tr').map(function () {
                return $(this);
            }).get();

            var hideSecurityCodeColumn = true;
            $.each(gridRows, function (i, gridRow) {
                var displaySecurityCode = gridRow.getColumnValue('DisplaySecurityCode').toLowerCase() == "true";
                if (displaySecurityCode) {
                    hideSecurityCodeColumn = false;
                    return false;
                }
                return true;
            });

            var securityCodeColumnIndex = $(this).getColIndex('SecurityCode') + 1;
            var securityTableColumn = $('td:nth-child(' + securityCodeColumnIndex + '),th:nth-child(' + securityCodeColumnIndex + ')');
            if (hideSecurityCodeColumn) {
                securityTableColumn.hide();
            } else {
                securityTableColumn.show();
            }
        });
    }
};

;IC.Public.image = {
    assingActions: function(actions, parent) {
        $.each(actions, function(index, value) {
            var _option = $('<option>');
            _option.val(value.url);
            _option.html(value.title);
            _option.appendTo(parent);
        });
    },
    imageColumn: function(cellValue, options, rowObject) {
        var _this = IC.Public.image;
        var allowOnlinePaymentAdvice = "false";
        var index = $(this).getColIndex("AllowOnlinePaymentAdvice");
        if (index >= 0) { allowOnlinePaymentAdvice = rowObject[index]; }
        if (allowOnlinePaymentAdvice == "true") {
            return String.format("<input type='image' value='{0}' src='/images/buttons/pdf.gif' class='pdfAdvice' title='Download Payment Advice'/>", options.rowId);
        } else {
            return "Not&nbsp;available";
        }
    },
    imageColumnStatement: function (cellValue, options, rowObject) {
        var _this = IC.Public.image;
        var path = '';
        var index = $(this).getColIndex("FileName");
        if (index >= 0) { path = rowObject[index]; }
        //if (allowOnlinePaymentAdvice == "true") {
        return String.format("<input type='image' fileName='{0}' value='{1}' src='/images/buttons/pdf.gif' class='pdfStatement' title='Download Statement'/>",path, options.rowId);
        //} else {
        //    return "Not&nbsp;available";
        //}
    },
    annualTaxStatement: function(cellValue, options, rowObject) {
        var taxYearEndColIndex = $(this).getColIndex("TaxYearEnd");
        var hrnKeyColIndex = $(this).getColIndex("HrnKey");
        var issuerCodeColIndex = $(this).getColIndex("IssuerCode");
        var securityCodeColIndex = $(this).getColIndex("SecurityCode");
        var fileNameColIndex = $(this).getColIndex("TaxStatementFileName");
        var isAtoReportColIndex = $(this).getColIndex("IsAtoReport");
        var statementDateColIndex = $(this).getColIndex("StatementDate");
        var abnNumberIndex = $(this).getColIndex("ABNNumber");
        var employeeCodeIndex = $(this).getColIndex("EmployeeCodeATO");
        var taxFileNumberIndex = $(this).getColIndex("TaxFileNumber");
        var amendmentToReportindColIndex = $(this).getColIndex("AmendmentToReportInd");
        var amendmentCountColIndex = $(this).getColIndex("AmendmentCount");
        var statementDateValueColIndex = $(this).getColIndex("StatementDateValue");
        

        var paramList = "";


        if (taxYearEndColIndex > 0)
            paramList = paramList + String.format(" taxYearEnd='{0}'", rowObject[taxYearEndColIndex]);
        if (hrnKeyColIndex > 0)
            paramList = paramList + String.format(" hrnKey='{0}'", rowObject[hrnKeyColIndex]);
        if (issuerCodeColIndex > 0)
            paramList = paramList + String.format(" issuerCode='{0}'", rowObject[issuerCodeColIndex]);
        if (securityCodeColIndex > 0)
            paramList = paramList + String.format(" securityCode='{0}'", rowObject[securityCodeColIndex]);
        if (fileNameColIndex > 0)
            paramList = paramList + String.format(" fileName='{0}'", rowObject[fileNameColIndex]);
        if (isAtoReportColIndex > 0)
            paramList = paramList + String.format(" isAtoReport='{0}'", rowObject[isAtoReportColIndex]);
        if (statementDateColIndex > 0)
            paramList = paramList + String.format(" statementDate='{0}'", rowObject[statementDateColIndex]);
        if (abnNumberIndex > 0)
            paramList = paramList + String.format(" abnNumber='{0}'", rowObject[abnNumberIndex]);
        if (employeeCodeIndex > 0)
            paramList = paramList + String.format(" employeeCode='{0}'", rowObject[employeeCodeIndex]);
        if (taxFileNumberIndex > 0)
            paramList = paramList + String.format(" taxFileNumber='{0}'", rowObject[taxFileNumberIndex]);
        if (amendmentToReportindColIndex > 0)
            paramList = paramList + String.format(" amendmentToReportInd='{0}'", rowObject[amendmentToReportindColIndex]);
        if (amendmentCountColIndex > 0)
            paramList = paramList + String.format(" amendmentCount='{0}'", rowObject[amendmentCountColIndex]);
        if (statementDateValueColIndex > 0)
            paramList = paramList + String.format(" statementDateValue='{0}'", rowObject[statementDateValueColIndex]);
        return String.format("<input type='image' src='/images/buttons/pdf.gif' class='pdfAnnualTaxStatement' {0} title='Download Tax Statement'/>", paramList);
    },
    amendmentTaxStatement: function(cellValue, options, rowObject) {
        var financialYearColIndex = $(this).getColIndex("TaxYearEnd");
        var hrnKeyColIndex = $(this).getColIndex("HrnKey");
        var issuerCodeColIndex = $(this).getColIndex("IssuerCode");
        var statementDateColIndex = $(this).getColIndex("GeneratedDate");
        var abnNumberIndex = $(this).getColIndex("ABNNumber");
        var employeeCodeIndex = $(this).getColIndex("EmployeeCode");
        var taxFileNumberIndex = $(this).getColIndex("TaxFileNumber");
        var statementDateValueIndex = $(this).getColIndex("StatementDateValue");

        var paramList = "";

        if (financialYearColIndex > 0)
            paramList = paramList + String.format(" taxYearEnd='{0}'", rowObject[financialYearColIndex]);
        if (hrnKeyColIndex > 0)
            paramList = paramList + String.format(" hrnKey='{0}'", rowObject[hrnKeyColIndex]);
        if (issuerCodeColIndex > 0)
            paramList = paramList + String.format(" issuerCode='{0}'", rowObject[issuerCodeColIndex]);
        
        paramList = paramList + String.format(" securityCode='{0}'", null);
        paramList = paramList + String.format(" fileName='{0}'", "");
        if (statementDateColIndex > 0)
            paramList = paramList + String.format(" statementDate='{0}'", rowObject[statementDateColIndex]);
        if (abnNumberIndex > 0)
            paramList = paramList + String.format(" abnNumber='{0}'", rowObject[abnNumberIndex]);
        if (employeeCodeIndex > 0)
            paramList = paramList + String.format(" employeeCode='{0}'", rowObject[employeeCodeIndex]);
        if (taxFileNumberIndex > 0)
            paramList = paramList + String.format(" taxFileNumber='{0}'", rowObject[taxFileNumberIndex]);
        if (statementDateValueIndex > 0)
            paramList = paramList + String.format(" statementDateValue='{0}'", rowObject[statementDateValueIndex]);
         paramList = paramList + String.format(" isAtoReport='{0}'", "true");
        paramList = paramList + String.format(" amendmentToReportInd='{0}'", "true");
        
        

        return String.format("<input type='image' src='/images/buttons/pdf.gif' class='pdfAnnualTaxStatement' {0} title='Download Tax Statement'/>", paramList);
    }
}

;IC.Public.gridFormatter = {
    numberTooltip: function (cellValue, options, rowObject) {

        if (cellValue == 'null') {
            return '';
        }

        //Format the cell value
        var displayValues = IC.Public.gridFormatter.getDisplayValues(cellValue);
        var isEmployeePlanRow = (IC.Public.gridFormatter.getCellValue($(this), "IsEmployeePlan", rowObject).toLowerCase() == "true");

        var originalCurrencyCode = IC.Public.gridFormatter.getCellValue($(this), "OriginalValueCurrency", rowObject);
        var convertedCurrencyCode = IC.Public.gridFormatter.getCellValue($(this), "ConvertedValueCurrency", rowObject);
        var currencyConversionApplied = (convertedCurrencyCode != 'null' && originalCurrencyCode != convertedCurrencyCode);

        if (isEmployeePlanRow) {
            //Construct the tooltip and format the cell value
            var uniqueId = IC.Public.gridFormatter.getCellValue($(this), "ID", rowObject);
            var isSecuritiesSectionEnabled = IC.Public.gridFormatter.getCellValue($(this), "IsSecuritiesSectionEnabled", rowObject) == "true";
            var isBalanceTradableOnly = IC.Public.gridFormatter.getCellValue($(this), "IsBalanceTradableOnly", rowObject) == "true";
            var IsSplitPlanEnabled = IC.Public.gridFormatter.getCellValue($(this), "IsSplitPlanEnabled", rowObject) == "true";

            /****************Beginning of cluetooltip*****************/
            var clueToolTip = $('<div>');

            var a = $('<a>');
            a.attr("href", "#");
            a.attr("rel", ".tooltip" + uniqueId);
            a.attr("class", "cluetooltip");
            a.html(displayValues.displayValue);
            a.appendTo(clueToolTip);

            var toolTipContent = $('<div>');
            toolTipContent.attr("class", "tooltip" + uniqueId);

            if (currencyConversionApplied) {
                toolTipContent.html(IC.Public.gridFormatter.getConvertedValueTooltipMessage($(this), rowObject, displayValues.displayValue, originalCurrencyCode, convertedCurrencyCode, "Value"));
            } else {
                var tooltipTitle = "<p class='balanceTooltipTitle'><b>Value: " + $("#hidCurrency").val() + " " + displayValues.displayValue + "</b></p>";
                toolTipContent.html(tooltipTitle);
            }

            var tooltipMessage = "";
            if (isBalanceTradableOnly && isSecuritiesSectionEnabled)
                tooltipMessage = "<p>" + $("#hidValueColumnTradeablyOnlyWithSecuritiesToolTip").val() + (IsSplitPlanEnabled ? " Excludes Rights" : "") + "</p>";
            else if ((isBalanceTradableOnly == false) && isSecuritiesSectionEnabled)
                tooltipMessage = "<p>" + $("#hidValueColumnAllBalanceWithSecuritiesToolTip").val() + (IsSplitPlanEnabled ? " Excludes Rights" : "") + "</p>";
            else
                tooltipMessage = "<p>" + $("#hidValueColumnAllBalanceWithoutSecuritiesToolTip").val() + (IsSplitPlanEnabled ? " Excludes Rights" : "") + "</p>";

            toolTipContent.html(toolTipContent.html() + tooltipMessage);
            toolTipContent.appendTo(clueToolTip);
            return clueToolTip.html();
            /****************End of cluetooltip*****************/
        } else {

            if (currencyConversionApplied) {
                return IC.Public.gridFormatter.getCurrencyConversionTooltip($(this), rowObject, displayValues.displayValue, originalCurrencyCode, convertedCurrencyCode, "Value");
            } else {
                if (displayValues.useToolTip) {
                    var div = $('<div>');
                    var span = $('<span>');
                    span.attr("title", displayValues.toolTipValue);
                    span.addClass("tooltip");
                    span.html(displayValues.displayValue);
                    span.appendTo(div);
                    return div.html();
                }
                return displayValues.toolTipValue;
            }
        }
    },

    numberTooltipPlanSummaryHoldings: function (cellValue, options, rowObject) {

        if (cellValue == 'null') {
            return '';
        }

        //Format the cell value
        var displayValues = IC.Public.gridFormatter.getDisplayValues(cellValue);
        var originalCurrencyCode = IC.Public.gridFormatter.getCellValue($(this), "OriginalValueCurrency", rowObject);
        var convertedCurrencyCode = IC.Public.gridFormatter.getCellValue($(this), "ConvertedValueCurrency", rowObject);
        var currencyConversionApplied = (convertedCurrencyCode != 'null' && originalCurrencyCode != convertedCurrencyCode);

        if (currencyConversionApplied) {
            return IC.Public.gridFormatter.getCurrencyConversionTooltip($(this), rowObject, displayValues.displayValue, originalCurrencyCode, convertedCurrencyCode, "Value");
        } else {
            if (displayValues.useToolTip) {
                var div = $('<div>');
                var span = $('<span>');
                span.attr("title", displayValues.toolTipValue);
                span.addClass("tooltip");
                span.html(displayValues.displayValue);
                span.appendTo(div);
                return div.html();
            }
            return displayValues.toolTipValue;
        }
    },
    getCurrencyConversionTooltip: function (grid, rowObject, formattedValue, originalCurrencyCode, convertedCurrencyCode, labelText) {
        var uniqueId = IC.Public.gridFormatter.getCellValue(grid, "UniqueId", rowObject);
        var clueToolTip = $('<div>');

        var a = $('<a>');
        a.attr("href", "#");
        a.attr("rel", ".valueTooltip" + uniqueId);
        a.attr("class", "cluetooltip");
        a.html(formattedValue);
        a.appendTo(clueToolTip);

        var toolTipContent = $('<div>');
        toolTipContent.attr("class", "valueTooltip" + uniqueId);

        var tooltipMessage = IC.Public.gridFormatter.getConvertedValueTooltipMessage(grid, rowObject, formattedValue, originalCurrencyCode, convertedCurrencyCode, labelText);
        toolTipContent.html(tooltipMessage);
        toolTipContent.appendTo(clueToolTip);
        return clueToolTip.html();
    },

    getConvertedValueTooltipMessage: function (grid, rowObject, valueToDisplay, originalCurrencyCode, convertedCurrencyCode, labelText) {
        var tooltipMessage = "<span class='currency-conversion-bold'>" + labelText + ": " + convertedCurrencyCode + " " + valueToDisplay + "</span>";
        tooltipMessage += "<br/><br/>";

        var originalValue = IC.Public.gridFormatter.getCellValue(grid, "OriginalValue", rowObject);
        originalValue = $.fmatter.util.NumberFormat(originalValue, $.jgrid.formatter.number);
        tooltipMessage += "<span class='currency-conversion-bold'>Converted from " + originalCurrencyCode + " " + originalValue + "</span><br/>";

        var conversionRate = IC.Public.gridFormatter.getCellValue(grid, "CurrencyConversionRate", rowObject);
        tooltipMessage += "<span class='currency-conversion-normal'>Exchange rate: 1.00 " + originalCurrencyCode + " = " + conversionRate + " " + convertedCurrencyCode + "</span><br/>";

        var conversionDate = IC.Public.gridFormatter.getCellValue(grid, "CurrencyConversionDate", rowObject);
        tooltipMessage += "<span class='currency-conversion-normal'>Rate as at: " + conversionDate + "</span>";
        return tooltipMessage;
    },

    lastCloseAndValue: function (cellValue, options, rowObject) {
        if (cellValue == 'null') {
            return '';
        }

        return IC.Public.gridFormatter.formatLastCloseAndValue($(this), cellValue, options, rowObject, "Last Close");
    },

    formatLastCloseAndValue: function (grid, cellValue, options, rowObject, labelText) {
        var originalCurrencyCode = IC.Public.gridFormatter.getCellValue(grid, "OriginalClosingPriceCurrency", rowObject);
        var convertedCurrencyCode = IC.Public.gridFormatter.getCellValue(grid, "ConvertedClosingPriceCurrency", rowObject);
        var currencyConversionApplied = (convertedCurrencyCode != 'null' && originalCurrencyCode != convertedCurrencyCode);

        var formattedValue = parseFloat(cellValue).toFixed(4).replace(/0{0,2}$/, "");
        if (!currencyConversionApplied)
            return formattedValue;

        var uniqueId = IC.Public.gridFormatter.getCellValue(grid, "UniqueId", rowObject);
        var clueToolTip = $('<div>');
        var a = $('<a>');
        a.attr("href", "#");
        a.attr("rel", ".closePriceTooltip" + uniqueId);
        a.attr("class", "cluetooltip");
        a.html(formattedValue);
        a.appendTo(clueToolTip);

        var toolTipContent = $('<div>');
        toolTipContent.attr("class", "closePriceTooltip" + uniqueId);

        var tooltipMessage = "<span class='currency-conversion-bold'>" + labelText + ": " + convertedCurrencyCode + " " + formattedValue + "</span>";
        tooltipMessage += "<br/><br/>";

        var originalClosingPrice = IC.Public.gridFormatter.getCellValue(grid, "OriginalClosingPrice", rowObject);
        tooltipMessage += "<span class='currency-conversion-bold'>Converted from " + originalCurrencyCode + " " + originalClosingPrice + "</span><br/>";

        var conversionRate = IC.Public.gridFormatter.getCellValue(grid, "CurrencyConversionRate", rowObject);
        tooltipMessage += "<span class='currency-conversion-normal'>Exchange rate: 1.00 " + originalCurrencyCode + " = " + conversionRate + " " + convertedCurrencyCode + "</span><br/>";

        var conversionDate = IC.Public.gridFormatter.getCellValue(grid, "CurrencyConversionDate", rowObject);
        tooltipMessage += "<span class='currency-conversion-normal'>Rate as at: " + conversionDate + "</span>";

        toolTipContent.html(tooltipMessage);
        toolTipContent.appendTo(clueToolTip);
        return clueToolTip.html();
    },

    residentsTaxAmountFormatter: function (cellValue, options, rowObject) {
        // Column 11 - Withholding Tax Code - RES, NON
        // Column 12 - Residents Tax Amount
        var displayValues = IC.Public.gridFormatter.getDisplayValues(rowObject[12]);

        if (displayValues.useToolTip) {
            var displayValue = displayValues.displayValue;
            if (rowObject[11] == 'RES') {
                displayValue += '<br/>(RWT)';
            } else if (rowObject[11] == 'NON') {
                displayValue += '<br/>(NRWT)';
            } else if (rowObject[11] == 'AIL') {
                displayValue += '<br/>(AIL)';
            }
            var div = $('<div>');
            var span = $('<span>');
            span.attr("title", displayValues.toolTipValue);
            span.addClass("tooltip");
            span.html(displayValue);
            span.appendTo(div);
            return div.html();
        }

        return cellValue;
    },
    taxCreditAmountFormatter: function (cellValue, options, rowObject) {
        // Column 13 - Imputed Tax Credit
        // Column 14 - Aus Franking Credit

        var displayValue = '';
        var toolTipValue = '';
        var useToolTip = false;

        if (parseFloat(rowObject[13]) > 0) {
            var displayValues = IC.Public.gridFormatter.getDisplayValues(rowObject[13]);

            displayValue = displayValues.displayValue + '<br/>(NZIC)';
            toolTipValue = displayValues.toolTipValue + ' (NZIC)';
            useToolTip = displayValues.useToolTip;
        }

        if (parseFloat(rowObject[14]) > 0) {
            var displayValues = IC.Public.gridFormatter.getDisplayValues(rowObject[14]);

            if (displayValue != '') {
                displayValue += '<br/>';
            }

            if (toolTipValue != '') {
                toolTipValue += '<br/>';
            }

            displayValue += displayValues.displayValue + '<br/>(AUFC)';
            toolTipValue += displayValues.toolTipValue + ' (AUFC)';
            useToolTip = useToolTip || displayValues.useToolTip;
        }

        if (displayValue == '') {
            displayValue = "0.00";
            return displayValue;
        }

        var div = $('<div>');
        var span = $('<span>');

        if (useToolTip) {
            span.addClass("taxCreditAmount");
            span.attr("toolTipValue", toolTipValue);
            span.addClass("tooltip");
        }

        span.html(displayValue);
        span.appendTo(div);
        return div.html();
    },
    getDisplayValues: function (cellValue) {
        var toolTipValue = $.fmatter.util.NumberFormat(cellValue, $.jgrid.formatter.number);
        var useToolTip = false;
        var displayValue = toolTipValue;
        var displayFullValue = $('#hidDisplayFullValue').val();
        displayFullValue = displayFullValue != undefined && displayFullValue.toLowerCase() == "true";
        if (!displayFullValue) {
            var intVal = parseInt(cellValue);
            if (intVal > 1000000) {
                if (intVal < 10000000) {
                    displayValue = $.fmatter.util.NumberFormat(intVal, { decimalSeparator: ".", thousandsSeparator: ",", decimalPlaces: 0 });
                } else {
                    displayValue = $.fmatter.util.NumberFormat(intVal / 1000000, { decimalSeparator: ".", thousandsSeparator: ",", decimalPlaces: 2 }) + 'm';
                }
                useToolTip = true;
            }
        }
        return { displayValue: displayValue, toolTipValue: toolTipValue, useToolTip: useToolTip };
    },
    getNumericDisplayValues: function (cellValue) {
        var toolTipValue = $.fmatter.util.NumberFormat(cellValue, $.jgrid.formatter.number);
        var useToolTip = false;
        var displayValue = cellValue;
        var intVal = parseInt(cellValue);
        if (intVal > 999) {
            if (intVal < 10000000) {
                displayValue = $.fmatter.util.NumberFormat(intVal, { decimalSeparator: ".", thousandsSeparator: ",", decimalPlaces: 0 });
            } else {
                displayValue = $.fmatter.util.NumberFormat(intVal / 1000000, { decimalSeparator: ".", thousandsSeparator: ",", decimalPlaces: 2 }) + 'm';
            }
            useToolTip = true;
        }
        return { displayValue: displayValue, toolTipValue: toolTipValue, useToolTip: useToolTip };
    },
    constructBalanceTooltip: function (cellValue, options, rowObject) {
        //construct the tooltip and format the cell value

        //Format the cell value
        var displayValues = IC.Public.gridFormatter.getDisplayValues(cellValue);

        var isEmployeePlanRow = (IC.Public.gridFormatter.getCellValue($(this), "IsEmployeePlan", rowObject).toLowerCase() == "true");
        //Show this tooltip only if the current row is a EmployeePlanRow
        if (isEmployeePlanRow) {
            var uniqueId = IC.Public.gridFormatter.getCellValue($(this), "ID", rowObject);

            var totalInPlan = IC.Public.gridFormatter.getDisplayValues(IC.Public.gridFormatter.getCellValue($(this), "TotalInPlan", rowObject)).displayValue;
            var vested = IC.Public.gridFormatter.getDisplayValues(IC.Public.gridFormatter.getCellValue($(this), "Vested", rowObject)).displayValue;
            var unVested = IC.Public.gridFormatter.getDisplayValues(IC.Public.gridFormatter.getCellValue($(this), "Unvested", rowObject)).displayValue;
            var restricted = IC.Public.gridFormatter.getDisplayValues(IC.Public.gridFormatter.getCellValue($(this), "Restricted", rowObject)).displayValue;

            var balanceBreakDown = "<table class='planDetailsTable'><tr class='totalInPlanRow'><td>In Plan</td><td class='planSummaryDetailsValue'>" + totalInPlan + "</td></tr>" +
                                          "<tr><td class='planSummaryDetailsLabel'>Unvested</td><td class='planSummaryDetailsValue'>" + unVested + "</td></tr>" +
                                          "<tr><td class='planSummaryDetailsLabel'>Vested</td><td class='planSummaryDetailsValue'>" + vested + "</td></tr>" +
                                          "<tr><td class='planSummaryDetailsLabel'>Restricted</td><td class='planSummaryDetailsValue'>" + restricted + "</td></tr></table>";

            //We need to create a markup similar to this for cluetooltips to work
            //  <div>
            //      <a href="#" rel="[unique class name of tooltip content element]" class="[some common name for all tooltips]">
            //          [Formatted Cell Value]
            //      </a>
            //      <div class="[unique class name of tooltip content element]">
            //          [tooltip content]
            //      </div>
            //  </div>

            /****************Beginning of cluetooltip*****************/
            var clueToolTip = $('<div>');

            var a = $('<a>');
            a.attr("href", "#");
            a.attr("rel", ".balancetooltip" + uniqueId);
            a.attr("class", "cluetooltip");
            a.html(displayValues.displayValue);
            a.appendTo(clueToolTip);

            var toolTipContent = $('<div>');
            toolTipContent.attr("class", "balancetooltip" + uniqueId);
            toolTipContent.html("<p>" + $("#hidBalanceTooltipTitle").val() + displayValues.displayValue + "</p>");
            toolTipContent.html(toolTipContent.html() + "<p>" + $("#hidBalanceColumnTooltipMessage").val() + "</p><br/>");
            toolTipContent.html(toolTipContent.html() + balanceBreakDown);

            toolTipContent.appendTo(clueToolTip);

            return clueToolTip.html();
            /****************End of cluetooltip*****************/
        } else
            return displayValues.displayValue;
    },

    getCellValue: function (grid, columnName, rowObject) {
        var cellValue = '';
        var index = grid.getColIndex(columnName);
        if (index >= 0)
            cellValue = rowObject[index];
        return cellValue;
    },
    
    wrapWords: function (cellValue, options, rowObject) {
        return "<div><span class='wrapNormal'>" + cellValue + "</span></div>";
    }
};;IC.Public.Chatbot = function (options) {
    $.extend(this, options);

    var ieVersion = this.msieversion();
    if (ieVersion > 0 && ieVersion < 10) {
        $("#botChat").hide();
    } else {
        var params = {};
        location
            .search
            .substring(1)
            .split("&")
            .forEach(function (pair) {
                var p = pair.split("=");
                params[p[0]] = p[1];
            });
        if (sessionStorage.luisToken === undefined) {
            $.ajax({
                url: IC.Public.urls.Chatbot.BotToken,
                dataType: "json",
                type: "GET",
                data: {},
            }).done(function (data) {
                if (data) {
                    sessionStorage.luisToken = data._token;
                    var directLine = new BotChat.DirectLine({
                        token: sessionStorage.luisToken
                    });
                    var botConnection = directLine;
                    sessionStorage.botUserId = Math.random();
                    botConnection.connectionStatus$
                        .subscribe(function (connectionStatus) {
                            switch (connectionStatus) {

                                case 2:
                                    sessionStorage.streamUrl = botConnection.streamUrl;
                                    sessionStorage.conversationId = botConnection.conversationId;
                                    break;
                                case 3:
                                    // ConnectionStatus.ExpiredToken:
                                    $.ajax({
                                        url: IC.Public.urls.Chatbot.BotReconnectToken,
                                        dataType: "json",
                                        type: "GET",
                                        data: {
                                            token: sessionStorage.luisToken,
                                            streamUrl: sessionStorage.streamUrl,
                                            conversationId: sessionStorage.conversationId
                                        },
                                    }).done(function (reconnect) {
                                        directLine.reconnect({
                                            token: reconnect.luisToken,
                                            streamUrl: reconnect.streamUrl,
                                            conversationId: reconnect.conversationId
                                        });
                                    });
                                    break;
                                    // last operation errored out with an expired token. Your app should supply a new one.
                            }
                        });
                    sessionStorage.isEmployee = data._isEmployee;
                    sessionStorage.issuerCode = data._issuerCode;
                    var user = {
                        id: sessionStorage.botUserId,
                        name: 'Me',
                        isEmployee: data._isEmployee, 
                        companyCode: data._issuerCode
                    };

                    var bot = {
                        id: params['botid'] || 'botid',
                        name: ""
                    };
                    BotChat.App({
                        botConnection: botConnection,
                        user: user,
                        bot: bot,
                        value: user
                    }, document.getElementById("BotChatGoesHere"));
                }
            });

        } else {
            var directLine = new BotChat.DirectLine({
                token: sessionStorage.luisToken,
                conversationId: sessionStorage.conversationId,
                //streamUrl: sessionStorage.streamUrl,
            });
            //debugger;
            var botConnection = directLine;
            var _isEmployee = sessionStorage.isEmployee;
            var _issuerCode = sessionStorage.issuerCode;
            botConnection.connectionStatus$
                .subscribe(function (connectionStatus) {
                    switch (connectionStatus) {
                        case 2:
                            sessionStorage.streamUrl = botConnection.streamUrl;
                            sessionStorage.conversationId = botConnection.conversationId;
                            break;
                        case 3:
                            $.ajax({
                                url: IC.Public.urls.Chatbot.BotReconnectToken,
                                dataType: "json",
                                type: "GET",
                                data: {
                                    token: sessionStorage.luisToken,
                                    streamUrl: sessionStorage.streamUrl,
                                    conversationId: sessionStorage.conversationId
                                },
                            }).done(function (reconnect) {
                                directLine.reconnect({
                                    token: reconnect.luisToken,
                                    streamUrl: reconnect.streamUrl,
                                    conversationId: reconnect.conversationId,
                                    _isEmployee: reconnect._isEmployee,
                                    _issuerCode: reconnect._issuerCode
                                });
                            });
                            break;
                            // last operation errored out with an expired token. Your app should supply a new one.
                    }
                });

            var user = {
                id: sessionStorage.botUserId,
                name: 'Me',
                isEmployee: _isEmployee, 
                companyCode: _issuerCode
            };
            //console.log(user);
            var bot = {
                id: params['botid'] || 'botid',
                name: ''
            };

            BotChat.App({
                botConnection: botConnection,
                user: user,
                bot: bot,
                value: user
            }, document.getElementById("BotChatGoesHere"));
        }
    }

    //setTimeout(function () {
        $(".wc-message-group-content").bind('DOMNodeInserted', function (e) {
            $("span:contains('$Index$')").parents().eq(5).attr('hidden', 'hidden');
            if ($('span:contains("Hello, I’m your VIRTUAL ASSISTANT")').parents().eq(5).attr('data-activity-id') != undefined) {
                var ActivityId = $('span:contains("Hello, I’m your VIRTUAL ASSISTANT")').parents().eq(5).attr('data-activity-id').slice(-7);
                if (ActivityId != 0000001)
                    $('span:contains("Hello, I’m your VIRTUAL ASSISTANT")').parents().eq(5).attr('hidden', 'hidden');

                if ($('span:contains("Your feedback has been received")').length > 0) {
                    //$('span:contains("Your feedback has been received")').parents().eq(5).attr('hidden', 'hidden');
                    document.getElementById('SubmitFeedback').setAttribute('disabled', 'disabled');
                }
            }
        });
    //}, 5000);

    //setTimeout(function () {
    //    if ($('#Username') != undefined)
    //        $('#Username').focus();
    //}, 1000);
};

IC.Public.Chatbot.prototype = {

    msieversion:function () {
            var ua = window.navigator.userAgent;
            var msie = ua.indexOf("MSIE ");

            if (msie > 0) // If Internet Explorer, return version number
                return parseInt(ua.substring(msie + 5, ua.indexOf(".", msie)));
            else // If another browser, return 0
                return 0;
        }
};

;IC.Public.Tax = function (options) {
    if (options) {

    }
};

IC.Public.Tax.prototype = {

    onGoClick: function (events) {
        var _obj = events.data.obj;

        var reverseDateError = 'From date should be before to date.';
        IC.Public.removeErrorFor(_obj.dateFrom, _obj.form, reverseDateError);
        if (_obj.dateFrom.hasClass('error')) { return; }
        if (_obj.dateTo.hasClass('error')) { return; }

        var dates = { dateFrom: _obj.dateFrom.val(), dateTo: _obj.dateTo.val() };
        var dateFromValue = $.datepicker.parseDate("dd/mm/yy", dates.dateFrom);
        var dateToValue = $.datepicker.parseDate("dd/mm/yy", dates.dateTo);

        if (dateFromValue > dateToValue) {
            IC.Public.displayErrorFor(_obj.dateFrom, _obj.form, reverseDateError);
            return;
        }

        $('#filterDateFrom').text($.datepicker.formatDate("dd M yy", dateFromValue));
        $('#filterDateTo').text($.datepicker.formatDate("dd M yy", dateToValue));
        if (dateFromValue == null)
            $('#filterDateFrom').text($('#filterDateFromHidden').val());
        if (dateToValue == null)
            $('#filterDateTo').text($('#filterDateToHidden').val());

        var filterData = _obj.holdingFilter.getHoldingFilterData();
        $.extend(dates, filterData);
        _obj.TaxGrid.appendPostData(dates);
        IC.Public.registerGA(window.location.hash, _obj.TaxGrid.getPostData());
        _obj.TaxGrid.trigger('reloadGrid', [{ page: 1 }]);
    },
    onDateBlur: function (events) {
        $(this).dateValidate({ mask: "dd/mm/yy", name: events.data.name, subYears: events.data.subYears });
    },
    onTooltipMouseover: function (e) {
        var tooltipHtml = e.data.tooltipHtml;

        var tooltip = $('#floatingTooltip');
        tooltip.html(tooltipHtml);

        var mouseX = e.pageX + 10;

        if (mouseX + tooltip.outerWidth(true) >= $(window).scrollLeft() + $(window).width()) {
            mouseX = $(window).scrollLeft() + $(window).width() - tooltip.outerWidth(true);
        }

        var mouseY = e.pageY + 10;

        if (mouseY + tooltip.outerHeight(true) >= $(window).scrollTop() + $(window).height()) {
            mouseY = $(window).scrollTop() + $(window).height() - tooltip.outerHeight(true);
        }

        tooltip.css({ left: mouseX, top: mouseY }).show();
    },
    onTooltipMouseout: function (e) {
        $('#floatingTooltip').hide();
    },
    init: function () {
        if ($('#floatingTooltip').length < 1) {
            $('body').append("<div class='floatingTooltip' id='floatingTooltip'></div>");
        }

        highlightMenu('payment');
        if ($("#portfolioMenuLink").attr("type") != undefined && $("#portfolioMenuLink").attr("type").toLowerCase() == "authorisedportfolio") {
            //$("#mainMenu").css("visibility", "hidden");
            $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
        } else {
            $("#mainMenu").css("visibility", "visible");
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        }
        var isThirdPartyImpersonateUser = $("#IsThirdPartyImpersonateUser").val();
        if (isThirdPartyImpersonateUser != undefined) {
            if (isThirdPartyImpersonateUser.toLowerCase() == "true") {
                //$("#btnContinue").attr('disabled', true);
                //IC.Public.disableControls('newHolding');
                //$("#btnCancel").removeAttr('disabled');
                $("#btnBackToAuthorisedPortfolio").removeAttr('disabled');
            }
        }
        this.TaxGrid = $("#TaxGrid");
        this.holdingFilter = $("form.holdingFilterForm");
        this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onGoClick);

        if ($("#CurrentRegistry").val().toLowerCase() == "nzl") {
            this.TaxGrid.unbind("grid_gridComplete");
            this.TaxGrid.bind("grid_gridComplete", { obj: this }, function (events) {
                var obj = events.data.obj;
                var gridMainContainer = $("#gbox_TaxGrid");

                var header = gridMainContainer.find('TH.ui-th-column:eq(6)');
                var div = header.find('div:eq(0)');
                div.addClass('tooltip');
                div.unbind("mouseover");
                div.bind("mouseover", { obj: obj, tooltipHtml: "<span>Payment Type (Currency / Payment Country)</span>" }, obj.onTooltipMouseover);
                div.unbind("mouseout");
                div.bind("mouseout", { obj: obj }, obj.onTooltipMouseout);

                header = gridMainContainer.find('TH.ui-th-column:eq(8)');
                div = header.find('div:eq(0)');
                div.addClass('tooltip');
                div.unbind("mouseover");
                div.bind("mouseover", { obj: obj, tooltipHtml: "<span>New Zealand Imputation Credit (NZIC)<br />Australian Franking Credits (AUFC)</span>" }, obj.onTooltipMouseover);
                div.unbind("mouseout");
                div.bind("mouseout", { obj: obj }, obj.onTooltipMouseout);

                header = gridMainContainer.find('TH.ui-th-column:eq(10)');
                div = header.find('div:eq(0)');
                div.addClass('tooltip');
                div.unbind("mouseover");
                div.bind("mouseover", { obj: obj, tooltipHtml: "<span>Non Resident Withholding Tax (NRWT)<br />Resident Withholding Tax (RWT)<br />Approved Issuer Levy(AIL)</span>" }, obj.onTooltipMouseover);
                div.unbind("mouseout");
                div.bind("mouseout", { obj: obj }, obj.onTooltipMouseout);

                var spans = obj.TaxGrid.find('SPAN.taxCreditAmount');

                for (var i = 0; i < spans.length; i++) {
                    var span = $(spans[i]);
                    span.unbind("mouseover");
                    span.bind("mouseover", { obj: obj, tooltipHtml: span.attr('toolTipValue') }, obj.onTooltipMouseover);
                    span.unbind("mouseout");
                    span.mouseout({ obj: obj }, obj.onTooltipMouseout);
                }
            });
        }

        this.btnGo = $("#btnGo");
        this.btnGo.button();
        this.btnGo.bind("click", { obj: this }, this.onGoClick);

        this.dateFrom = $("[name=DateFrom]");
        this.dateTo = $("[name=DateTo]");
        this.dateFrom.datepicker('option', { maxDate: new Date() });
        this.dateTo.datepicker('option', { maxDate: new Date() });

        IC.Public.updateCampaignsPanel('PaymentsAndTaxTax');
    }
}

;
IC.Public.settings = function(options) {
    if (options) {
    }
};

IC.Public.settings.prototype = {

    onClick: function (events) {
        var url = events.data.url;
        OC.MVC.util.loadMainContainerView(url);
    },
    
    init: function() {
        highlightMenu(null);
        if($("#portfolioMenuLink").attr("type") != undefined)
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        this.btnChangeEmail = $("#btnChangeEmail");
        this.btnChangeEmail.bind("click", { url: IC.Public.urls.Settings.changeEmail }, this.onClick);

        this.btnChangePassword = $("#btnChangePassword");
        this.btnChangePassword.bind("click", { url: IC.Public.urls.Settings.changePassword }, this.onClick);

        this.btnChangeSecurityQuestion = $("#btnChangeSecurityQuestion");
        this.btnChangeSecurityQuestion.bind("click", { url: IC.Public.urls.Settings.changeSecurityQuestion }, this.onClick);
        
        this.btnChangeTranPwd = $("#btnChangeTranPwd");
        this.btnChangeTranPwd.bind("click", { url: IC.Public.urls.Settings.changeTransactionPassword }, this.onClick);

        this.btnSmsPinRegistration = $('#btnSmsPinRegistration');
        this.btnSmsPinRegistration.bind("click", { url: IC.Public.urls.Settings.smsPinRegistration }, this.onClick);
        
        IC.Public.updateCampaignsPanel('Settings');
    }

}
;
IC.Public.changeEmail = function(options) {
};

IC.Public.changeEmail.prototype =
{
    onBackClick: function (events) {
        var url = IC.Public.urls.Settings.settings;
        OC.MVC.util.loadMainContainerView(url);
    },
    onChangeClick: function (events) {
        var _obj = events.data.obj;

        var url = IC.Public.getUrl(IC.Public.urls.Settings.changeEmailComplete);
        var form = $("form#ChangeEmailForm");
        var data = form.serializeObject();
        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    init: function () {
        highlightMenu(null);
        if($("#portfolioMenuLink").attr("type") != undefined)
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        $("#btnBack").bind("click", { obj: this }, this.onBackClick);
        $("#btnChange").bind("click", { obj: this }, this.onChangeClick);
        OC.MVC.validate.validationMsgProcess();
        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnChange').click();
                }
            }
        });
    }
}
;
IC.Public.confirmEmail = function(options) {
}

IC.Public.confirmEmail.prototype =
{
    onCancelClick: function(events) {
        var url = OC.MVC.util.getLink("Settings", "EditChangeEmail");
        var form = $("form#ConfirmEmailForm");
        var data = form.serializeObject();
        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: { newEmail: data.NewEmail },
            success: function(result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function(err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    onConfirmClick: function(events) {
        var url = OC.MVC.util.getLink("Settings", "ChangeEmailComplete");
        var form = $("form#ConfirmEmailForm");
        var data = form.serializeObject();
        form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function(result) {
                   // $("#" + OC.MVC.constants.mainContainer).html(result);
                    window.location.href = "/Login/Logout";
                    IC.Public.hideLoading();
                },
                error: function(err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });

        }
    },
    init: function() {
        highlightMenu(null);
        $("#btnCancel").bind("click", { obj: this }, this.onCancelClick);
        $("#btnConfirm").bind("click", { obj: this }, this.onConfirmClick);
        $('html').unbind('keypress');
        $('html').bind('keypress', function(e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnConfirm').click();
                }
            }
        });
    }
}
;
IC.Public.changeEmailComplete = function(options) {
}

IC.Public.changeEmailComplete.prototype =
{
    onReturnToSettings: function() {
    var url = OC.MVC.util.getLink("Settings", "Settings");
        OC.MVC.util.loadMainContainerView(url);
    },
    init: function() {
        highlightMenu(null);
        $("#btnReturnToSettings").bind("click", { obj: this }, this.onReturnToSettings);
    }
}
;IC.Public.changePassword = function(options) {
};

IC.Public.changePassword.prototype =
{
    onBackClick: function(events) {
        var url = IC.Public.urls.Settings.settings;
        OC.MVC.util.loadMainContainerView(url);
    },
    onChangeClick: function(events) {
        var _obj = events.data.obj;
        var url = IC.Public.urls.Settings.confirmPassword;
        var form = $("form#ChangePasswordForm");
        var data = form.serializeObject();
        form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url, type: 'POST', data: data,
                success: function(result) {
                    $("#" + OC.MVC.constants.mainContainer).html(result);
                    IC.Public.hideLoading();
                },
                error: function(err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });

        }
    },
    init: function() {
        highlightMenu(null);
        if($("#portfolioMenuLink").attr("type") != undefined)
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        $("#btnBack").bind("click", { obj: this }, this.onBackClick);
        $("#btnChange").bind("click", { obj: this }, this.onChangeClick);
        OC.MVC.validate.validationMsgProcess();
        $('html').unbind('keypress');
        $('html').bind('keypress', function(e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnChange').click();
                }
            }
        });
    }
}
;
IC.Public.confirmPassword = function(options) {
}

IC.Public.confirmPassword.prototype =
{
    onCancelClick: function(events) {
        var url = IC.Public.urls.Settings.changePassword;
        // force an ajax call
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: null,
            success: function(result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function(err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    onConfirmClick: function(events) {
        var url = IC.Public.urls.Settings.changePasswordSubmit;
        var form = $("form#ConfirmPasswordForm");
        var data = form.serializeObject();
        form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function(result) {
                    //$("#" + OC.MVC.constants.mainContainer).html(result);
                    window.location.href = "/Login/Logout";
                    IC.Public.hideLoading();
                },
                error: function(err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });

        }
    },
    init: function() {
        highlightMenu(null);
        $("#btnCancel").bind("click", { obj: this }, this.onCancelClick);
        $("#btnConfirm").bind("click", { obj: this }, this.onConfirmClick);
        $('html').unbind('keypress');
        $('html').bind('keypress', function(e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnConfirm').click();
                }
            }
        });
    }
}
;
IC.Public.changePasswordComplete = function(options) {
}

IC.Public.changePasswordComplete.prototype =
{
    onReturnToSettings: function() {
        var url = IC.Public.urls.Settings.settings;
        OC.MVC.util.loadMainContainerView(url);
    },
    init: function() {
        highlightMenu(null);
        $("#btnReturnToSettings").bind("click", { obj: this }, this.onReturnToSettings);
        window.location.href = "/Login/Logout";
    }
}
;IC.Public.changeSecurityQuestion = function(options) {
}

IC.Public.changeSecurityQuestion.prototype =
{
    onBackClick: function (events) {
        var url = OC.MVC.util.getLink("Settings", "Settings");
        OC.MVC.util.loadMainContainerView(url);
    },
    onChangeClick: function (events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("Settings", "ConfirmSecurityQuestion");
        var form = $("form#ChangeSecurityQuestionForm");
        var data = form.serializeObject();
        form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url, type: 'POST', data: data,
                success: function (result) {
                    $("#" + OC.MVC.constants.mainContainer).html(result);
                    IC.Public.hideLoading();
                },
                error: function (err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },
    init: function () {
        highlightMenu(null);
        if($("#portfolioMenuLink").attr("type") != undefined)
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        $("#btnBack").bind("click", { obj: this }, this.onBackClick);
        $("#btnChange").bind("click", { obj: this }, this.onChangeClick);
        OC.MVC.validate.validationMsgProcess();
        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnChange').click();
                }
            }
        });
    }
}
;
IC.Public.confirmSecurityQuestion = function(options) {
}

IC.Public.confirmSecurityQuestion.prototype =
{
    onCancelClick: function(events) {
        var url = OC.MVC.util.getLink("Settings", "EditChangeSecurityQuestion");
        var form = $("form#ConfirmSecurityQuestionForm");
        var data = form.serializeObject();
        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: { newSecurityQuestion: data.NewSecurityQuestion, newSecurityAnswer: data.NewSecurityAnswer },
            success: function(result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function(err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    onConfirmClick: function(events) {
        var url = OC.MVC.util.getLink("Settings", "ChangeSecurityQuestionComplete");
        var form = $("form#ConfirmSecurityQuestionForm");
        var data = form.serializeObject();
        form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function(result) {
                    //  $("#" + OC.MVC.constants.mainContainer).html(result);
                    window.location.href = "/Login/Logout";
                    IC.Public.hideLoading();
                },
                error: function(err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });

        }
    },
    init: function() {
        highlightMenu(null);
        $("#btnCancel").bind("click", { obj: this }, this.onCancelClick);
        $("#btnConfirm").bind("click", { obj: this }, this.onConfirmClick);
        $('html').unbind('keypress');
        $('html').bind('keypress', function(e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnConfirm').click();
                }
            }
        });
    }
}
;
IC.Public.changeSecurityQuestionComplete = function(options) {
}

IC.Public.changeSecurityQuestionComplete.prototype =
{
    onReturnToSettings: function() {
        var url = OC.MVC.util.getLink("Settings", "Settings");
        OC.MVC.util.loadMainContainerView(url);
    },
    init: function() {
        highlightMenu(null);
        $("#btnReturnToSettings").bind("click", { obj: this }, this.onReturnToSettings);
    }
}
;IC.Public.changeTransactionPassword = function () {
    this.init();
};

IC.Public.changeTransactionPassword.prototype = {
    onBackClick: function (events) {
        var url = IC.Public.urls.Settings.settings;
        OC.MVC.util.loadMainContainerView(url);
    },
    
    onChangeClick: function (events) {
        var url = IC.Public.urls.Settings.changeTransactionPasswordSubmit;
        var form = $("form#ChangeTransactionPasswordForm");
        var data = form.serializeObject();
        form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url, type: 'POST', data: data,
                success: function (result) {
                    $("#" + OC.MVC.constants.mainContainer).html(result);
                    IC.Public.hideLoading();
                },
                error: function (err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });

        }
    },

    init: function () {
        highlightMenu(null);
        if($("#portfolioMenuLink").attr("type") != undefined)
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        $("#btnBack").bind("click", { obj: this }, this.onBackClick);
        $("#btnChange").bind("click", { obj: this }, this.onChangeClick);
        OC.MVC.validate.validationMsgProcess();
        
        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnChange').click();
                }
            }
        });
    }
};;IC.Public.createTransactionPassword = function () {
    this.init();
};

IC.Public.createTransactionPassword.prototype = {
    init: function () {
        highlightMenu(null);
        $("#btnCreate").bind("click", { obj: this }, this.onCreateClick);
        OC.MVC.validate.validationMsgProcess();

        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnCreate').click();
                }
            }
        });
    },

    onCreateClick: function () {
        $('.errorContainer').html('<ol></ol>');
        
        var url = IC.Public.urls.Settings.createTransactionPasswordSubmit;
        var form = $("form#CreateTransactionPasswordForm");
        var data = form.serializeObject();
        form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url, type: 'POST', data: data,
                success: function (result) {
                    IC.Public.hideLoading();
                    $("#" + OC.MVC.constants.mainContainer).html(result);
                },
                error: function (err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });

        }
    }
};;IC.Public.updatepaymentinstructions = function(options) {
}

IC.Public.updatepaymentinstructions.prototype = {
    onNextClick: function (events) {
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        var _obj = events.data.obj;
        var form = $("form#formUpdate");
        var data = form.serialize();
        var fromLogin = null;
        var missingPayment = null;
        if ($('#IsMissingPaymentDetailsEnabled').val() != undefined && $('#IsMissingPaymentDetailsEnabled').val().toLowerCase() == "true")
            missingPayment = true;
        if ($('#IsNavigatedFromLoginScreen').val() != undefined && $('#IsNavigatedFromLoginScreen').val().toLowerCase() == "true")
            fromLogin = true;

        if (missingPayment && fromLogin) {
            var selector = ".updatecheckbox:checked";
            var checkedValues = $(selector).map(function() { return $(this).val(); }).get();
            if (checkedValues.length > 0) {
                data += '&HoldingIDs=' + checkedValues.join(',');
            } else
                data += $('#RowsToBeUpdated').val();
            var url = OC.MVC.util.getLink("UpdatePaymentInstructions", "Next");
            form.validate(IC.Public.getValidatorCfg());
            if (form.valid()) {
                IC.Public.showLoading("");
                $.ajax({
                    url: url,
                    type: 'POST',
                    data: data,
                    success: function(result) {
                        $("#" + OC.MVC.constants.mainContainer).html(result);
                        IC.Public.hideLoading();
                    },
                    error: function(err) {
                        $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                        IC.Public.hideLoading();
                    }
                });
            }
        } else {
            if ($('#IsOtherHoldingsVisible').val().toLowerCase() == "true") {
                var selectedOption = $("#ApplyToAllHoldings_1:checked").get(0);
                var selector = ".updatecheckbox:checked";
                if (($("#IsShowOtherHoldings:checked").val() != undefined) && (selectedOption != undefined)) {
                    selector = ".updatecheckbox";
                }
                var checkedValues = $(selector).map(function() { return $(this).val(); }).get();
                if (checkedValues) {
                    data += '&HoldingIDs=' + checkedValues.join(',');
                }
                data += "&ApplyToAllHoldings=" + $("[name=ApplyToAllHoldings]:checked").val();
            } else {
                data += '&HoldingIDs=' + $('#RowsToBeUpdated').val();
            }

            var url = OC.MVC.util.getLink("UpdatePaymentInstructions", "Next");
            form.validate(IC.Public.getValidatorCfg());
            if (form.valid()) {
                IC.Public.showLoading("");
                $.ajax({
                    url: url,
                    type: 'POST',
                    data: data,
                    success: function(result) {
                        $("#" + OC.MVC.constants.mainContainer).html(result);
                        IC.Public.hideLoading();
                    },
                    error: function(err) {
                        $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                        IC.Public.hideLoading();
                    }
                });
            }
        }
    },
    onSkipClick: function (events) {
        $("#confirmSkipMissingPaymentUserModal").modal('show');
        $('#simplemodal-container').css('height', 'auto');
        $.modal.setPosition();
    },

    onConfirmClick: function (events) {
        $.modal.close();
        this.doNotDisplayAgain = $("input[name=DoNotDisplayAgain]");
        if ($(this.doNotDisplayAgain).is(':checked')) {
            IC.Public.showLoading("");
            $.ajax({
                url: OC.MVC.util.getLink("UpdatePaymentInstructions", "SavePortfolioCampaignDoNotDisplayTickBox"),
                type: 'POST',
                success: function (result) {
                    if ($('#IsMissingTaxDetailsEnabled').val().toLowerCase() == "true") {
                        var urlTax = "/UpdateTaxDetails/GetTaxDetails?isRedirectedFromLoginPage=" + true;
                        window.location.href = urlTax;
                    } else if ($('#IsMissingCommunicationDetailsEnabled').val().toLowerCase() == "true") {
                        var urlComm = "/CommunicationsPreferences/Preferences?isRedirectedFromLoginPage=" + true;
                        window.location.href = urlComm;
                    } else {
                        var urlHoldings = "/";
                        window.location.href = urlHoldings;
                    }
                    IC.Public.hideLoading();
                },
                error: function (err) {
                    $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                    IC.Public.hideLoading("");
                }
            });

        } else {
            if ($('#IsMissingTaxDetailsEnabled').val().toLowerCase() == "true") {
                var urlTax = "/UpdateTaxDetails/GetTaxDetails?isRedirectedFromLoginPage=" + true;
                window.location.href = urlTax;
            } else if ($('#IsMissingCommunicationDetailsEnabled').val().toLowerCase() == "true") {
                var urlComm = "/CommunicationsPreferences/Preferences?isRedirectedFromLoginPage=" + true;
                window.location.href = urlComm;
            } else {
                var urlHoldings = "/";
                window.location.href = urlHoldings;
            }
        }
    },
    onCancelClick: function (events) {
        $.modal.close();
    },

    onFilterGrid: function(events, viewKey) {
        var _obj = events.data.obj;
        if (!_obj.isLoading) {
            var url = OC.MVC.util.getLink("UpdatePaymentInstructions", "Index");
            var data = { viewKey: viewKey };
            OC.MVC.util._loadView("mainContent", url, data);
        }
    },

    onPaymentTypeChanged: function(events) {
        var _obj = events.data.obj;
        $('div.accountDetails input').val('');
        if (!_obj.isLoading) {
            var selectedOption = $(_obj.paymentTypeList).find('option:selected');
            if ($(selectedOption).get(0) != undefined) {
                if (selectedOption.val() != "DELETE") {
                    _obj.paymentTypeDescription.val(selectedOption.text());
                } else {
                    _obj.paymentTypeDescription.val("Delete payment instruction");
                }

                var form = $("#formUpdate");
                var data = form.serializeObject();
                var url = OC.MVC.util.getLink("UpdatePaymentInstructions", "UpdatePage");
                form.validate(IC.Public.getValidatorCfg());
                if (form.valid()) {
                    IC.Public.showLoading("");
                    $.ajax({
                        url: url,
                        type: 'POST',
                        data: data,
                        success: function(result) {
                            $("#" + OC.MVC.constants.mainContainer).html(result);
                            IC.Public.hideLoading();
                        },
                        error: function(err) {
                            $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                            IC.Public.hideLoading();
                        }
                    });
                }
            }
        }
    },

    onDrpClicked: function(events) {
        var url = OC.MVC.util.getLink($("#RegistryDrpControllerName").val(), "Index");
        OC.MVC.util._loadView("mainContent", url);
    },
    onDoNotDisplayClick: function (events) {

        if ($(this).is(':checked')) {
            $('#btnNext').parent().addClass('disabled');
        } else {
            $('#btnNext').parent().removeClass('disabled');
        }
    },

    onDeleteOptionLinkClick: function(events) {
        var _obj = events.data.obj;
        var deleteOption = _obj.paymentTypeList.find('option[value=DELETE]:selected');
        if (deleteOption.length == 0) {
            if ($(deleteOption).get(0) == undefined) {
                _obj.paymentTypeList.html('<option value="DELETE"></option>' + _obj.paymentTypeList.html());
            }
            _obj.paymentTypeList.val("DELETE");
            $("#IsDeleteOptionAvailable").val('True');
            $("#pClickNextMessage").show();
            _obj.onPaymentTypeChanged(events);
        }
    },

    init: function() {
        this.isLoading = true;
        var missingPayment = true;
        var fromLogin = null;
        if ($('#IsMissingPaymentDetailsEnabled').val() != undefined && $('#IsMissingPaymentDetailsEnabled').val().toLowerCase() == "true")
            missingPayment = true;
        if ($('#IsNavigatedFromLoginScreen').val() != undefined && $('#IsNavigatedFromLoginScreen').val().toLowerCase() == "true")
            fromLogin = true;

        if (missingPayment && fromLogin) {
            IC.Public.HideMenu();
        }
        this.doNotDisplayAgain = $("input[name=DoNotDisplayAgain]");
        this.doNotDisplayAgain.unbind('click');
        this.doNotDisplayAgain.bind('click', { obj: this }, this.onDoNotDisplayClick);

        highlightMenu('payment');
        if($("#portfolioMenuLink").attr("type") != undefined)
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        this.btnNext = $('#btnNext');
        this.btnNext.unbind("click");
        this.btnNext.bind("click", { obj: this }, this.onNextClick);
        var btnSkip = $("#btnSkip");
        btnSkip.bind("click", { obj: this }, this.onSkipClick);
        $("#btnConfirm").bind('click', { obj: this }, this.onConfirmClick);
        $("#btnCancel").bind('click', { obj: this }, this.onCancelClick);
        $('#confirmSkipMissingPaymentUserModal').hide();
        this.btnDeleteOptionLink = $("#btnDeleteOptionLink");
        if (this.btnDeleteOptionLink.get(0) != undefined) {
            this.btnDeleteOptionLink.unbind("click");
            this.btnDeleteOptionLink.bind("click", { obj: this }, this.onDeleteOptionLinkClick);
        }

        this.applyUpdatesGrid = $("#bdiv_ApplyHoldingsGrid");

        this.paymentTypeDescription = $("#PaymentTypeDescription");
        this.paymentTypeList = $("#PaymentType");
        this.paymentTypeList.unbind('change');
        this.paymentTypeList.bind('change', { obj: this }, this.onPaymentTypeChanged);

        this.holdingFilter = $("form.holdingFilterForm");
        this.holdingFilter.unbind("applyHoldingFilter");
        this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterGrid);

        var selectedOption = $(this.paymentTypeList).find('option:selected');
        if (selectedOption.get(0) != undefined) {
            if (selectedOption.val() == "DELETE") {
                $("#pClickNextMessage").show();
            } else if (selectedOption.val() == "NZL") {
                $("div.nzlSpecific").show();
            } else if (selectedOption.val() == "GBR") {
                $("div.ukSpecific").show();
            }
        }
        else if ($('#hdnPaymentType').val() == "GBR") {
            $("div.ukSpecific").show();
        }
        var isDisabled = $("#IsNextButtonDisabled").get(0);
        if ((isDisabled != undefined) && ($(isDisabled).val().toLowerCase() == "true")) {
            $("div.accountDetails input").attr("disabled", true);
        }

        $("#lnkDrp").unbind('click');
        $("#lnkDrp").bind('click', { obj: this }, this.onDrpClicked);

        $('html').bind('keypress', function(e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnNext').click();
                }
            }
        });

        var isThirdPartyImpersonateUser = $("#IsThirdPartyImpersonateUser").val();
        if (isThirdPartyImpersonateUser != undefined) {
            if (isThirdPartyImpersonateUser.toLowerCase() == "true") {
                $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
                $("#btnNext").attr('disabled', true);
            } else {
                $("#btnNext").removeAttr('disabled');
            }
        }

        this.isLoading = false;
    }
};IC.Public.updatepaymentinstructionsAus = function (options) {
}

IC.Public.updatepaymentinstructionsAus.prototype = {
    onNextClick: function (events) {
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        var _obj = events.data.obj;
        var form = $("form#formUpdate");
        var data = form.serialize();
        var fromLogin = null;
        var missingPayment = null;

        if ($('#IsMissingPaymentDetailsEnabled').val() != undefined && $('#IsMissingPaymentDetailsEnabled').val().toLowerCase() == "true")
            missingPayment = true;
        if ($('#IsNavigatedFromLoginScreen').val() != undefined && $('#IsNavigatedFromLoginScreen').val().toLowerCase() == "true")
            fromLogin = true;

        if (missingPayment && fromLogin) {
            var selector = ".updatecheckbox:checked";
            var checkedValues = $(selector).map(function () { return $(this).val(); }).get();
            if (checkedValues.length > 0) {
                data += '&HoldingIDs=' + checkedValues.join(',');
            } else
                data += '&HoldingIDs=' + $('#RowsToBeUpdated').val();

            var url = OC.MVC.util.getLink("UpdatePaymentInstructionsAus", "NextAus");
            form.validate(IC.Public.getValidatorCfg());
            if (form.valid()) {
                IC.Public.showLoading("");
                $.ajax({
                    url: url,
                    type: 'POST',
                    data: data,
                    success: function (result) {
                        $("#" + OC.MVC.constants.mainContainer).html(result);
                        IC.Public.hideLoading();
                    },
                    error: function (err) {
                        $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                        IC.Public.hideLoading();
                    }
                });
            }
        } else {

            if ($('#IsOtherHoldingsVisible').val().toLowerCase() == "true") {
                var selectedOption = $("#ApplyToAllHoldings_1:checked").get(0);
                var selector = ".updatecheckbox:checked";
                if (($("#IsShowOtherHoldings:checked").val() != undefined) && (selectedOption != undefined)) {
                    selector = ".updatecheckbox";
                }
                var checkedValues = $(selector).map(function () { return $(this).val(); }).get();
                if (checkedValues) {
                    data += '&HoldingIDs=' + checkedValues.join(',');
                }
                data += "&ApplyToAllHoldings=" + $("[name=ApplyToAllHoldings]:checked").val();
            } else {
                data += '&HoldingIDs=' + $('#RowsToBeUpdated').val();
            }

            var url = OC.MVC.util.getLink("UpdatePaymentInstructionsAus", "NextAus");
            form.validate(IC.Public.getValidatorCfg());
            if (form.valid()) {
                IC.Public.showLoading("");
                $.ajax({
                    url: url,
                    type: 'POST',
                    data: data,
                    success: function (result) {
                        $("#" + OC.MVC.constants.mainContainer).html(result);
                        IC.Public.hideLoading();
                    },
                    error: function (err) {
                        $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                        IC.Public.hideLoading();
                    }
                });
            }
        }
    },

    onSkipClick: function (events) {
        $("#confirmSkipMissingPaymentUserModal").modal('show');
        $('#simplemodal-container').css('height', 'auto');
        $.modal.setPosition();
    },

    onConfirmClick: function (events) {
        $.modal.close();
        this.doNotDisplayAgain = $("input[name=DoNotDisplayAgain]");
        if ($(this.doNotDisplayAgain).is(':checked')) {
            IC.Public.showLoading("");
            $.ajax({
                url: OC.MVC.util.getLink("UpdatePaymentInstructionsAus", "SavePortfolioCampaignDoNotDisplayTickBox"),
                type: 'POST',
                success: function (result) {
                    if ($('#IsMissingTaxDetailsEnabled').val().toLowerCase() == "true") {
                        if ($('#IsEmployeeLogin').val() != undefined && $('#IsEmployeeLogin').val().toLowerCase() == "true") {
                            var urlTax = "/Employee/" + $('#IssuerCode').val() + "/UpdateTaxDetails/GetTaxDetails?isRedirectedFromLoginPage=" + true;
                        } else {
                            var urlTax = "/UpdateTaxDetails/GetTaxDetails?isRedirectedFromLoginPage=" + true;
                        }
                        window.location.href = urlTax;
                    } else if ($('#IsMissingCommunicationDetailsEnabled').val().toLowerCase() == "true") {
                        if ($('#IsEmployeeLogin').val() != undefined && $('#IsEmployeeLogin').val().toLowerCase() == "true") {
                            var urlComm = "/Employee/" + $('#IssuerCode').val() + "/CommunicationsPreferences/Preferences?isRedirectedFromLoginPage=" + true;
                        } else {
                            var urlComm = "/CommunicationsPreferences/Preferences?isRedirectedFromLoginPage=" + true;
                        }
                        window.location.href = urlComm;
                    }
                    else if ($('#IsMissingFATCACRSEnabled').val().toLowerCase() == "true") {
                        if ($('#IsEmployeeLogin').val() != undefined && $('#IsEmployeeLogin').val().toLowerCase() == "true") {
                            var urlFATCA = "/Employee/" + $('#IssuerCode').val() + "/ForeignTaxResidency/ForeignTaxResidencyCertificationIndex?isRedirectedFromLoginPage=" + true;
                        } else {
                            var urlFATCA = "/ForeignTaxResidency/ForeignTaxResidencyCertificationIndex?isRedirectedFromLoginPage=" + true;
                        }
                        window.location.href = urlFATCA;
                    }
                    else {
                        if ($('#IsEmployeeLogin').val() != undefined && $('#IsEmployeeLogin').val().toLowerCase() == "true") {
                            {
                                window.location.href = "/Employee/" + $('#IssuerCode').val();
                            }
                        } else {
                            var urlHoldings = "/";
                            window.location.href = urlHoldings;
                        }
                    }
                    IC.Public.hideLoading();
                },
                error: function (err) {
                    $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                    IC.Public.hideLoading("");
                }
            });

        } else {
            if ($('#IsMissingTaxDetailsEnabled').val().toLowerCase() == "true") {
                if ($('#IsEmployeeLogin').val() != undefined && $('#IsEmployeeLogin').val().toLowerCase() == "true") {
                    var urlTax = "/Employee/" + $('#IssuerCode').val() + "/UpdateTaxDetails/GetTaxDetails?isRedirectedFromLoginPage=" + true;
                } else {
                    var urlTax = "/UpdateTaxDetails/GetTaxDetails?isRedirectedFromLoginPage=" + true;
                }
                window.location.href = urlTax;
            } else if ($('#IsMissingCommunicationDetailsEnabled').val().toLowerCase() == "true") {
                if ($('#IsEmployeeLogin').val() != undefined && $('#IsEmployeeLogin').val().toLowerCase() == "true") {
                    var urlComm = "/Employee/" + $('#IssuerCode').val() + "/CommunicationsPreferences/Preferences?isRedirectedFromLoginPage=" + true;
                } else {
                    var urlComm = "/CommunicationsPreferences/Preferences?isRedirectedFromLoginPage=" + true;
                }
                window.location.href = urlComm;
            }
            else if ($('#IsMissingFATCACRSEnabled').val().toLowerCase() == "true") {
                if ($('#IsEmployeeLogin').val() != undefined && $('#IsEmployeeLogin').val().toLowerCase() == "true") {
                    var urlFATCA = "/Employee/" + $('#IssuerCode').val() + "/ForeignTaxResidency/ForeignTaxResidencyCertificationIndex?isRedirectedFromLoginPage=" + true;
                } else {
                    var urlFATCA = "/ForeignTaxResidency/ForeignTaxResidencyCertificationIndex?isRedirectedFromLoginPage=" + true;
                }
                window.location.href = urlFATCA;
            }
            else {
                if ($('#IsEmployeeLogin').val() != undefined && $('#IsEmployeeLogin').val().toLowerCase() == "true") {
                    window.location.href = "/Employee/" + $('#IssuerCode').val();
                }
                else {
                    var urlHoldings = "/";
                    window.location.href = urlHoldings;
                }
            }
        }
    },
    onCancelClick: function (events) {
        $.modal.close();
    },

    onFilterGrid: function (events, viewKey) {
        var _obj = events.data.obj;
        if (!_obj.isLoading) {
            var url = OC.MVC.util.getLink("UpdatePaymentInstructionsAus", "IndexAus");
            var data = { viewKey: viewKey };
            var transactionPinModelCache = $("#TransactionPinModelCacheKey");
            if (transactionPinModelCache) {
                if (transactionPinModelCache.val().length != 0) {
                    data = { viewKey: viewKey, pinCacheKey: transactionPinModelCache.val() };
                }
            }
            OC.MVC.util._loadView("mainContent", url, data);
        }
    },

    onPaymentTypeChanged: function (events) {
        var _obj = events.data.obj;
        $('div.accountDetails input').val('');
        if (!_obj.isLoading) {
            var selectedOption = $(_obj.paymentTypeList).find('option:selected');
            if ($(selectedOption).get(0) != undefined) {
                if (selectedOption.val() != "DELETE") {
                    _obj.paymentTypeDescription.val(selectedOption.text());
                } else {
                    _obj.paymentTypeDescription.val("Delete payment instruction");
                }

                var form = $("#formUpdate");
                var data = form.serializeObject();
                var url = OC.MVC.util.getLink("UpdatePaymentInstructionsAus", "UpdatePage");
                form.validate(IC.Public.getValidatorCfg());
                if (form.valid()) {
                    IC.Public.showLoading("");
                    $.ajax({
                        url: url,
                        type: 'POST',
                        data: data,
                        success: function (result) {
                            $("#" + OC.MVC.constants.mainContainer).html(result);
                            IC.Public.hideLoading();
                        },
                        error: function (err) {
                            $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                            IC.Public.hideLoading();
                        }
                    });
                }
            }
        }
    },

    onDrpClicked: function (events) {
        var url = OC.MVC.util.getLink($("#RegistryDrpControllerName").val(), "Index");
        OC.MVC.util._loadView("mainContent", url);
    },

    onDoNotDisplayClick: function (events) {

        if ($(this).is(':checked')) {
            $('#btnNext').parent().addClass('disabled');
        } else {
            $('#btnNext').parent().removeClass('disabled');
        }
    },
    onDeleteOptionLinkClick: function (events) {
        var _obj = events.data.obj;
        var deleteOption = _obj.paymentTypeList.find('option[value=DELETE]:selected');
        if (deleteOption.length == 0) {
            if ($(deleteOption).get(0) == undefined) {
                _obj.paymentTypeList.html('<option value="DELETE"></option>' + _obj.paymentTypeList.html());
            }
            _obj.paymentTypeList.val("DELETE");
            $("#IsDeleteOptionAvailable").val('True');
            $("#pClickNextMessage").show();
            _obj.onPaymentTypeChanged(events);
        }
    },

    init: function () {
        this.isLoading = true;
        var missingPayment = null;
        var fromLogin = null;
        if ($('#IsMissingPaymentDetailsEnabled').val() != undefined && $('#IsMissingPaymentDetailsEnabled').val().toLowerCase() == "true")
            missingPayment = true;
        if ($('#IsNavigatedFromLoginScreen').val() != undefined && $('#IsNavigatedFromLoginScreen').val().toLowerCase() == "true")
            fromLogin = true;


        if (missingPayment && fromLogin) {
            IC.Public.HideMenu();
        }
        this.doNotDisplayAgain = $("input[name=DoNotDisplayAgain]");
        this.doNotDisplayAgain.unbind('click');
        this.doNotDisplayAgain.bind('click', { obj: this }, this.onDoNotDisplayClick);

        highlightMenu('payment');
        if ($("#portfolioMenuLink").attr("type") != undefined)
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        this.btnNext = $('#btnNext');
        this.btnNext.unbind("click");
        this.btnNext.bind("click", { obj: this }, this.onNextClick);
        $('#confirmSkipMissingPaymentUserModal').hide();

        $("#btnConfirm").bind('click', { obj: this }, this.onConfirmClick);
        $("#btnCancel").bind('click', { obj: this }, this.onCancelClick);

        var btnSkip = $("#btnSkip");
        btnSkip.bind("click", { obj: this }, this.onSkipClick);

        this.btnDeleteOptionLink = $("#btnDeleteOptionLink");
        if (this.btnDeleteOptionLink.get(0) != undefined) {
            this.btnDeleteOptionLink.unbind("click");
            this.btnDeleteOptionLink.bind("click", { obj: this }, this.onDeleteOptionLinkClick);
        }

        this.applyUpdatesGrid = $("#bdiv_ApplyHoldingsGrid");

        this.paymentTypeDescription = $("#PaymentTypeDescription");
        this.paymentTypeList = $("#PaymentType");
        this.paymentTypeList.unbind('change');
        this.paymentTypeList.bind('change', { obj: this }, this.onPaymentTypeChanged);

        this.holdingFilter = $("form.holdingFilterForm");
        this.holdingFilter.unbind("applyHoldingFilter");
        this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterGrid);

        var selectedOption = $(this.paymentTypeList).find('option:selected');
        if (selectedOption.get(0) != undefined) {
            if (selectedOption.val() == "DELETE") {
                $("#pClickNextMessage").show();
            } else if (selectedOption.val() == "NZL") {
                $("div.nzlSpecific").show();
            } else if (selectedOption.val() == "GBR") {
                $("div.ukSpecific").show();
            }
        }
        else if ($('#hdnPaymentType').val() == "GBR") {
            $("div.ukSpecific").show();
        }
        var isDisabled = $("#IsNextButtonDisabled").get(0);
        if ((isDisabled != undefined) && ($(isDisabled).val().toLowerCase() == "true")) {
            $("div.accountDetails input").attr("disabled", true);
        }

        $("#lnkDrp").unbind('click');
        $("#lnkDrp").bind('click', { obj: this }, this.onDrpClicked);

        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnNext').click();
                }
            }
        });

        if (IC.Public.IsThirdPartyImpersonateUser()) {
            $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
            $("#formUpdate input").attr("checked", "Checked");
            $("#IsShowOtherHoldings").trigger('change');
            IC.Public.disableControls('formUpdate');
            $("#ViewKey").removeAttr('disabled');
            $("#btnBackToAuthorisedPortfolio").removeAttr("disabled");
        } else {
            $("#btnNext").removeAttr('disabled');
        }
        this.isLoading = false;
    }
};IC.Public.checkboxupdateinstructions = {
    assingActions: function(actions, parent) {
        $.each(actions, function(index, value) {
            var _option = $('<option>');
            _option.val(value.url);
            _option.html(value.title);
            _option.appendTo(parent);
        });
    },
    checkboxColumn: function(cellValue, options, rowObject) {
        var _this = IC.Public.checkbox;
        return String.format("<input type='checkbox' value='{0}' checked='checked' class='updatecheckbox'/>", options.rowId);
    }
}

;IC.Public.updateinstructionsconfirm = function (options) {
}
IC.Public.updateinstructionsconfirm.prototype = {
    onBackClick: function (events) {
        var cacheKey = $("#CacheKey").val();
        var url = OC.MVC.util.getLink("UpdatePaymentInstructions", "UpdatePageByCacheKey");
        IC.Public.showLoading("");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url, { cacheKey: cacheKey });
        //IC.Public.hideLoading();
    },

    onBackAusClick: function (events) {
        var cacheKey = $("#CacheKey").val();
        var url = OC.MVC.util.getLink("UpdatePaymentInstructionsAus", "UpdatePageByCacheKey");
        IC.Public.showLoading("");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url, { cacheKey: cacheKey });
        //IC.Public.hideLoading();
    },

    onConfirmClick: function (events) {
        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handleUpdatePaymentInstructionConfirmClick();
            return;
        }
        
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        var formData = $('form#UpdatePaymentInstructionForm').serializeObject();
        IC.Public.security.validateTransactionPassword(formData, function () { self.handleUpdatePaymentInstructionConfirmClick(); }, function () { $("#btnConfirm").parent().addClass('disabled'); });
    },

    onConfirmAusClick: function (events) {
        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handleUpdatePaymentInstructionAusConfirmClick();
            return;
        }
        
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        var isMultiMobileExist = $("#HrnsRequiringPinValidation_IsMultiMobileExist").val();
        if (isMultiMobileExist == "True") {
            self.gotoValidateSmsPin();
        } else {
            if ($('form#UpdatePaymentInstructionForm').children().length <= 0)
            {
                self.handleUpdatePaymentInstructionAusConfirmClick();
                return;
            }
            var formData = $('form#UpdatePaymentInstructionForm').serializeObject();
            IC.Public.security.validateTransactionPin(formData, function () { self.handleUpdatePaymentInstructionAusConfirmClick(); }, function () { $("#btnConfirm").parent().addClass('disabled'); });
        }
    },
    
    handleUpdatePaymentInstructionConfirmClick: function () {
        var url = OC.MVC.util.getLink("UpdatePaymentInstructions", "UpdateComplete");
        var cacheKey = $("#CacheKey").val();
        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: { cacheKey: cacheKey },
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err);
                IC.Public.hideLoading();
            }
        });
    },

    handleUpdatePaymentInstructionAusConfirmClick: function () {
        var url = OC.MVC.util.getLink("UpdatePaymentInstructionsAus", "UpdateCompleteAus");
        var cacheKey = $("#CacheKey").val();
        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: { cacheKey: cacheKey },
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err);
                IC.Public.hideLoading();
            }
        });
    },

    gotoValidateSmsPin: function () {
        var url = OC.MVC.util.getLink("UpdatePaymentInstructionsAus", "NextAusValidatePin");
        var form = $("form#UpdatePaymentInstructionForm");
        var data = form.serialize();
        data += '&cacheKey=' + $("#CacheKey").val();
        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err);
                IC.Public.hideLoading();
            }
        });
    },
    init: function (validateTransactionPassword) {
        this.validateTransactionPassword = validateTransactionPassword;
        highlightMenu('communication');
        $("#btnBack").bind("click", { obj: this }, this.onBackClick);
        $("#btnConfirm").bind("click", { obj: this }, this.onConfirmClick);

        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnConfirm').click();
                }
            }
        });
    },

    initAus: function (validateTransactionPassword) {
    this.validateTransactionPassword = validateTransactionPassword;
    highlightMenu('communication');
    $("#btnBack").bind("click", { obj: this }, this.onBackAusClick);
    $("#btnConfirm").bind("click", { obj: this }, this.onConfirmAusClick);

    $('html').unbind('keypress');
    $('html').bind('keypress', function (e) {
        if (e.keyCode == 13) {
            if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                //Do Nothing
            } else {
                $('#btnConfirm').click();
            }
        }
    });
}
}
;IC.Public.updateinstructionsconfirmSmsVal = function (options) {
}
IC.Public.updateinstructionsconfirmSmsVal.prototype = {
    onBackClick: function (events) {
        var cacheKey = $("#CacheKey").val();
        var url = OC.MVC.util.getLink("UpdatePaymentInstructions", "UpdatePageByCacheKey");
        IC.Public.showLoading("");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url, { cacheKey: cacheKey });
        //IC.Public.hideLoading();
    },

    onBackAusClick: function (events) {
        var cacheKey = $("#CacheKey").val();
        var url = OC.MVC.util.getLink("UpdatePaymentInstructionsAus", "UpdatePageByCacheKey");
        IC.Public.showLoading("");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url, { cacheKey: cacheKey });
        //IC.Public.hideLoading();
    },

    onConfirmClick: function (events) {
        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handleUpdatePaymentInstructionConfirmClick();
            return;
        }
        
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        var formData = $('form#UpdatePaymentInstructionForm').serializeObject();
        IC.Public.security.validateTransactionPassword(formData, function () { self.handleUpdatePaymentInstructionConfirmClick(); }, function () { $("#btnConfirm").parent().addClass('disabled'); });
    },

    onConfirmAusClick: function (events) {
        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handleUpdatePaymentInstructionAusConfirmClick();
            return;
        }
        
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        var formData = $('form#UpdatePaymentInstructionForm').serializeObject();
        IC.Public.security.validateTransactionPin(formData, function () { self.handleUpdatePaymentInstructionAusConfirmClick(); }, function () { $("#btnConfirm").parent().addClass('disabled'); });
        //self.gotoValidateSmsPin();
    },
    
    handleUpdatePaymentInstructionConfirmClick: function () {
        var url = OC.MVC.util.getLink("UpdatePaymentInstructions", "UpdateComplete");
        var cacheKey = $("#CacheKey").val();
        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: { cacheKey: cacheKey },
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err);
                IC.Public.hideLoading();
            }
        });
    },

    handleUpdatePaymentInstructionAusConfirmClick: function () {
        var url = OC.MVC.util.getLink("UpdatePaymentInstructionsAus", "UpdateCompleteAus");
        var cacheKey = $("#CacheKey").val();
        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: { cacheKey: cacheKey },
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err);
                IC.Public.hideLoading();
            }
        });
    },
    gotoValidateSmsPin: function () {
        var url = OC.MVC.util.getLink("UpdatePaymentInstructionsAus", "NextAusValidatePin");
        var cacheKey = $("#CacheKey").val();
        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: { cacheKey: cacheKey },
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err);
                IC.Public.hideLoading();
            }
        });
    },
    init: function (validateTransactionPassword) {
        this.validateTransactionPassword = validateTransactionPassword;
        highlightMenu('communication');
        $("#btnBack").bind("click", { obj: this }, this.onBackClick);
        $("#btnConfirm").bind("click", { obj: this }, this.onConfirmClick);

        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnConfirm').click();
                }
            }
        });
    },

    initAus: function (validateTransactionPassword) {
    this.validateTransactionPassword = validateTransactionPassword;
    highlightMenu('communication');
    $("#btnBack").bind("click", { obj: this }, this.onBackAusClick);
    $("#btnConfirm").bind("click", { obj: this }, this.onConfirmAusClick);

    $('html').unbind('keypress');
    $('html').bind('keypress', function (e) {
        if (e.keyCode == 13) {
            if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                //Do Nothing
            } else {
                $('#btnConfirm').click();
            }
        }
    });
}
}
;IC.Public.proxyvotinglist = function (options) {
    if (options) {
    }
}

IC.Public.proxyvotinglist.prototype = {

    onFilterGrid: function (events) {
        var _obj = events.data.obj;
        var filterData = _obj.holdingFilter.getHoldingFilterData();
        _obj.proxyVotingGrid.appendPostData(filterData);
        IC.Public.registerGA(window.location.hash, _obj.proxyVotingGrid.getPostData());
        _obj.proxyVotingGrid.trigger('reloadGrid');
    },

    onVotingActionClick: function (events) {
        var meetingKey = $(this).attr("key");
        var data = null;
        if (meetingKey) {
            data = { meetingKey: meetingKey, IsDeclarationAccepted: false };
        }
        var url = OC.MVC.util.getLink("VotingType", "Index");
        IC.Public.showLoading("");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url, data);
        //IC.Public.hideLoading();
    }

   ,
    onDownloadClick: function (events) {
        events.preventDefault();
        this.action = events.data.obj.action;
        var url = $(this).attr("cellvalue");
        if (url && url != '') {
            window.open(url, "_blank");
        }
    }
   ,
    onAskQuestionClick: function (events) {
        this.action = events.data.obj.action;
        var key = $(this).attr("key");
        var url = OC.MVC.util.getLink("AnnualGeneralMeetingQuestions", "Index");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url, { key: key });
    }

   ,
    onCommPrefUpdateClick: function (events) {
        this.action = events.data.obj.action;
        var url = OC.MVC.util.getLink("ProxyVotingCommunications", "Index");
        OC.MVC.util.loadMainContainerView(url + "?MeetingKey=" + $("#commPrefUpdate").attr("meeting"));
    },
    init: function () {
        highlightMenu('voting');
        this.holdingFilter = $("form.holdingFilterForm");
        if ($("#portfolioMenuLink").attr("type") != undefined && $("#portfolioMenuLink").attr("type").toLowerCase() == "authorisedportfolio") {
            //$("#mainMenu").css("visibility", "hidden");
            $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
        } else {
            $("#mainMenu").css("visibility", "visible");
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        }
        this.btnCommPrefUpdate = $('#commPrefUpdate');
        this.btnCommPrefUpdate.unbind("click", { obj: this }, this.onCommPrefUpdateClick);
        this.btnCommPrefUpdate.bind("click", { obj: this }, this.onCommPrefUpdateClick);

        this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterGrid);
        this.proxyVotingGrid = $("#ProxyVotingListGrid");
        this.proxyVotingGrid.bind("grid_gridComplete", { obj: this }, function (events) {
            var _obj = events.data.obj;
            var linkDisplay = $(this).find("[aria-describedby='ProxyVotingListGrid_IsCommPreferenceLink']");
            var voteLink = $(".VotingActionLink");
            if (linkDisplay.length > 0 && voteLink.length > 0) {
                if (linkDisplay.first().text() == "true") {
                    $("#commPrefUpdate").show();
                    var meetingKey = voteLink.first().attr('key');
                    $("#commPrefUpdate").attr("meeting", meetingKey);
                }
                else {
                    $("#commPrefUpdate").hide();
                }
            }
            else {
                $("#commPrefUpdate").hide();
            }

            _obj.action = $(this).find(".VotingActionLink").text();
            $(this).find(".VotingActionLink").bind("click", { obj: _obj }, _obj.onVotingActionClick);

            _obj.action = $(this).find(".DownloadLink").text();
            $(this).find(".DownloadLink").bind("click", { obj: _obj }, _obj.onDownloadClick);

            _obj.action = $(this).find(".AskQuestion").text();
            $(this).find(".AskQuestion").bind("click", { obj: _obj }, _obj.onAskQuestionClick);

            var noRecordsTable = $('#gview_ProxyVotingListGrid').find('table.noRecords');
            if (noRecordsTable != undefined)
                noRecordsTable.html('<tr><td>' + $('#hdnNoRecordsFoundMessage').val() + '</td></tr>');
        });
        //        var pageCode = $("#pageCode").val();
        //Voting Only
        //        if (pageCode != 'VO') {
        //            IC.Public.updateCampaignsPanel('OtherVoting');
        //        }
    }
};IC.Public.votingactionlink = {
    votingactionlink: function (cellvalue, options, rowObject) {
        var _this = IC.Public.votingactionlink;
        var div = $('<div>');
        var key = '';
        var isEnabled = false;
        var index = $(this).getColIndex("ID");
        if (index >= 0) {
            key = rowObject[index];
        }
        index = $(this).getColIndex("IsThirdPartyImpersonateUser");
        if (index > 0) {
            isEnabled = !(rowObject[index].toLowerCase() == "true");
        }
        var allowVotingCol = $(this).getColIndex("AllowOnlineVoting");
        if (rowObject[allowVotingCol] == 'false') {
            var notOnlineLabelCol = $(this).getColIndex("NotOnlineLabelResourceText");
            div.append(rowObject[notOnlineLabelCol]);
        }
        else if (isEnabled && (rowObject[17] != null && rowObject[17].trim().length > 0)) {
            var voteActionLinkCol = $(this).getColIndex("VoteLinkResourceText");
            var a = $('<a>');
            a.attr("href", "javascript:void(0);");
            a.addClass("VotingActionLink");
            a.attr("key", key);
            a.html(rowObject[voteActionLinkCol]);
            a.appendTo(div);
            var br = $('<br>');
            br.appendTo(div);
        }

        var IsAgmQuestionsAvailableCol = $(this).getColIndex("IsAgmQuestionsAvailable");
        if (rowObject[IsAgmQuestionsAvailableCol] == 'true') {
            if (isEnabled) {
                var askQuestionLinkCol = $(this).getColIndex("AskQuestionLinkResourceText");
                var askquestion = $('<a>');
                askquestion.attr("class", "AskQuestion");
                askquestion.attr("href", "javascript:void(0);");
                askquestion.html(rowObject[askQuestionLinkCol]);
                askquestion.attr("key", key);
                askquestion.appendTo(div);
            }
        }

        if (!isEnabled) {
            var tooltipTitleCol = $(this).getColIndex("TooltipTitleResourceText");
            var notAvailableLabelCol = $(this).getColIndex("NotAvailableLabelResourceText");
            var span = $("<span>");
            span.addClass("tooltip");
            span.attr("title", rowObject[tooltipTitleCol]);
            span.html(rowObject[notAvailableLabelCol]);
            span.appendTo(div);
        }

        var webcastUrlCol = $(this).getColIndex("WebcastUrl");
        var webcastUrl = rowObject[webcastUrlCol];
        if (webcastUrl != "null") {
            var webcastLinkCol = $(this).getColIndex("WebcastLinkResourceText");
            var webcastLink = $('<a>');
            webcastLink.attr("href", "http://" + webcastUrl);
            webcastLink.attr("target", "_blank");
            webcastLink.html(rowObject[webcastLinkCol]);
            webcastLink.appendTo(div);
            var br = $('<br>');
            br.appendTo(div);
        }

        return div.html();
    }
};IC.Public.votingtype = function(options) {
}

IC.Public.votingtype.prototype = {
    onUpdateClick: function(events) {
        var _obj = events.data.obj;
        _obj.goToNextStep();
    },
    goToNextStep: function() {
        var form = $("form#VotingTypeForm");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ProxyVotingDirections", "Index");
        IC.Public.showLoading();
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    onBackClick: function(events) {
        var _obj = events.data.obj;
        var form = $("form#VotingTypeForm");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("VotingType", "Index");
        IC.Public.showLoading();
        $.ajax({
            url: url,
            type: 'POST',
            data: { meetingKey: data.MeetingKey, IsDeclarationAccepted: true },
            success: function(result) {
                OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                IC.Public.hideLoading();
            },
            error: function(err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
        //var _obj = events.data.obj;
        //var url = OC.MVC.util.getLink("ProxyVotingList", "Index");
        //OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url);
    },

    onCancelClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("ProxyVotingList", "Votes");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url);
    },

    init: function () {
        var isSkipStep = $('#hdnSkipStep').val();
        if (isSkipStep != undefined && isSkipStep.toLocaleLowerCase() == "true") {
            this.goToNextStep();
        }
        highlightMenu('voting');
        this.btnNext = $('#btnNext');
        this.btnNext.bind("click", { obj: this }, this.onNextClick);
        $("#btnUpdate").bind("click", { obj: this }, this.onUpdateClick);
        this.btnCancel = $('#btnCancel');
        this.btnCancel.bind("click", { obj: this }, this.onCancelClick);
        this.btnBack = $('#btnBack');
        this.btnBack.bind("click", { obj: this }, this.onBackClick);
        //IC.Public.updateCampaignsPanel('OtherVoting');
    }
}
;IC.Public.downloadlink = {
    downloadlink: function(cellvalue, options, rowObject) {
        var div = $('<div>');
        if (cellvalue == '' || cellvalue == 'null') {
            var instructionCol = $(this).getColIndex("VotingInstruction");
            var instructionUrl = rowObject[instructionCol];
            if (instructionUrl != '' && instructionUrl != 'null') {
                var _this = IC.Public.downloadlink;
                var a = $('<a>');
                a.attr("href", instructionUrl);
                a.addClass("DownloadLink");
                a.attr("cellvalue", instructionUrl);
                a.attr("title", instructionUrl);
                a.attr("key", options.rowId);
                a.html("Download");
                a.appendTo(div);
                return div.html();
            }
            else {
                return '';
            }
        }

        var _this = IC.Public.downloadlink;
        var a = $('<a>');
        a.attr("href", cellvalue);
        a.addClass("DownloadLink");
        a.attr("cellvalue", cellvalue);
        a.attr("title", cellvalue);
        a.attr("key", options.rowId);
        a.html("Download");
        a.appendTo(div);
        return div.html();
    },
    
    noticeOfMeetingLink: function (cellvalue, options, rowObject) {
        if (cellvalue == '' || cellvalue == 'null') return '';

        var div = $('<div>');
        var a = $('<a>');
        a.attr("href", cellvalue);
        a.addClass("DownloadLink");
        a.attr("cellvalue", cellvalue);
        a.attr("title", cellvalue);
        a.attr("key", options.rowId);
        a.html("Download");
        a.appendTo(div);
        return div.html();
    }
};IC.Public.proxyvotingdirections = function(options) {
    if (options) {
    }
}

IC.Public.proxyvotingdirections.prototype = {
    EqualToHoldingBalance: "1",
    LessThanHoldingBalance: "2",
    GreaterThanHoldingBalance: "3",

    onTotalVotesComboChanged: function(events) {
        var _obj = events.data.obj;

        if ($(this).val() == _obj.EqualToHoldingBalance) {
            _obj.TotalNumberVotes.setNumVal(_obj.HoldingBalance);
            _obj.rowVotingInstructionCheckboxUnderstand.hide();
            _obj.rowTotalNumberOfVotes.hide();
            var HoldingBalance = $("#HoldingBalance").val();
            $.each($("#divResolutions").find("td:nth-child(3)").find("input"), function() {
                $(this).setNumVal(HoldingBalance);
            })
        }
        if ($(this).val() == _obj.LessThanHoldingBalance) {
            _obj.rowVotingInstructionCheckboxUnderstand.hide();
            _obj.rowTotalNumberOfVotes.show();
        }
        if ($(this).val() == _obj.GreaterThanHoldingBalance) {
            _obj.rowVotingInstructionCheckboxUnderstand.show();
            _obj.rowTotalNumberOfVotes.show();
        }

        $("#divResolutions").find("td:nth-child(n+4) input").val(0);
    },

    onTotalNumberVotesChanged: function(events) {
        var _obj = events.data.obj;
        var newNumberVotes = _obj.TotalNumberVotes.getNumVal();

        var selectedValueTotalVotesCombo = _obj.TotalVotesCombo.val();
        if (selectedValueTotalVotesCombo == _obj.LessThanHoldingBalance) {
            if (newNumberVotes < 0 || newNumberVotes > parseInt(_obj.HoldingBalance)) {
                return false;
            }
        }

        if (selectedValueTotalVotesCombo == _obj.GreaterThanHoldingBalance) {
            if (newNumberVotes < 0 || newNumberVotes < parseInt(_obj.HoldingBalance)) {
                return false;
            }
        }

        //reset all votes
        $("#divResolutions").find("td:nth-child(n+4) input").val(0);
        $.each($("#divResolutions").find("td:nth-child(3)").find("input"), function() {
            $(this).setNumVal(newNumberVotes);
        });

    },

    onNextClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("ProxyVotingDirections", "Next");
        var form = $("form#ProxyVotingDirectionsForm");
        var data = form.serialize();
        form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading();
            $.ajax({
                url: url, type: 'POST', data: data,
                success: function(result) {
                    OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                },
                error: function(err) {
                    IC.Public.hideLoading();
                    OC.MVC.util.errorMessage(err.responseText);
                }
            });

        }
    },

    onBackClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("ProxyVotingDirections", "Back");
        var form = $("form#ProxyVotingDirectionsForm");
        var data = form.serializeObject();
        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: data,
            success: function(result) {
                OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
            },
            error: function(err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });

    },

    onCancelClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("ProxyVotingList", "Votes");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url);
    },

    RecalculateRemainingVotes: function(element) {
        var remainingVotes = this.TotalNumberVotes.getNumVal();
        var rowid = element.attr("rowid");
        var sum = 0;
        if (element && element.length > 0 && rowid) {
            $("#divResolutions").find("input.calc[type=text][rowid=" + rowid + "]").map(function() {
                sum += $(this).getNumVal();
            });
            var inputRemaining = $("#divResolutions").find("input.readonly[type=text][rowid=" + rowid + "]");
            remainingVotes -= sum;
            inputRemaining.setNumVal(remainingVotes);
            if (remainingVotes < 0) {
                inputRemaining.addClass('input-validation-error');
            }
            else {
                inputRemaining.removeClass('input-validation-error');
            }
        }
    },
    OnRecalculateRemainingVotes: function(events) {
        var _obj = events.data.obj;
        _obj.RecalculateRemainingVotes($(this));
    },

    OnRadioButtonClick: function(events) {
        events.preventDefault();
    },

    OnRadioButtonMouseDown: function(events) {
        if ($(this).parent().parent().find("input[type=radio]:checked").val() == $(this).val()) {
            $(this).removeAttr("checked");
            $(this).prop("checked", false);
        }
        else {
            $(this).attr("checked", "checked");
            $(this).prop("checked", true);
        }
    },

    init: function() {
        highlightMenu('voting');
        OC.MVC.validate.validationMsgProcess();
        this.HoldingBalance = $("#HoldingBalance").val();
        this.TotalVotesCombo = $("select[name=TotalVotesType]");
        this.TotalVotesCombo.bind("change", { obj: this }, this.onTotalVotesComboChanged);

        this.rowVotingInstructionCheckboxUnderstand = $("#rowVotingInstructionCheckboxUnderstand");
        this.rowTotalNumberOfVotes = $("#rowTotalNumberOfVotes");
        this.btnNext = $('#btnUpdate');
        this.btnNext.unbind("click");
        this.btnNext.bind("click", { obj: this }, this.onNextClick);

        this.TotalNumberVotes = $("#TotalNumberOfVotes");
        this.TotalNumberVotes.bind("change", { obj: this }, this.onTotalNumberVotesChanged);

        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind("click");
        this.btnCancel.bind("click", { obj: this }, this.onCancelClick);
        this.btnBack = $('#btnBack');
        this.btnBack.unbind("click");
        this.btnBack.bind("click", { obj: this }, this.onBackClick);

        var inputs = $(".resolutionRow input[type=text]:not([class=readonly])");
        inputs.unbind("keyup");
        inputs.bind("keyup", { obj: this }, this.OnRecalculateRemainingVotes);
        var _obj = this;
        $.each($("#divResolutions").find("input.readonly[type=text]"), function(index, element) {
            _obj.RecalculateRemainingVotes($(element));
        });

        var radiobuttons = $(".resolutionRow input[type=radio]");
        radiobuttons.bind("mousedown", { obj: this }, this.OnRadioButtonMouseDown);
        radiobuttons.bind("click", { obj: this }, this.OnRadioButtonClick);
        //IC.Public.updateCampaignsPanel('OtherVoting');
    }
};IC.Public.proxyvotingdeclaration = function(options) {
}

IC.Public.proxyvotingdeclaration.prototype = {

    onOkClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("VotingType", "VotingType");
        var form = $("#fmVotingDeclaration");
        var data = form.serialize();
        IC.Public.showLoading();
        $.ajax({
            url: url, type: 'POST', data: data,
            success: function(result) {
                OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
            },
            error: function(err) {
                IC.Public.hideLoading();
                OC.MVC.util.errorMessage(err.responseText);
            }
        });
    },

    onCancelClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("ProxyVotingList", "Votes");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url);
    },

    init: function() {
        highlightMenu('voting');

        this.btnCancel = $('#btnCancel');
        this.btnCancel.bind("click", { obj: this }, this.onCancelClick);
        this.btnOk = $('#btnOk');
        this.btnOk.bind("click", { obj: this }, this.onOkClick);

        //OC.MVC.validate.validationMsgProcess();
        //IC.Public.updateCampaignsPanel('OtherVoting');
    }
};IC.Public.proxyvotingholder = function(options) {
    if (options) {
    }
}

IC.Public.proxyvotingholder.prototype = {
    divNames: null,
    divBodyCorporate: null,
    onNextClick: function(events) {
        var _obj = events.data.obj;
        var form = $("form#ProxyVotingHolderForm");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ProxyVotingHolder", "Next");

        form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url, type: 'POST', data: data,
                success: function(result) {
                    OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                    IC.Public.hideLoading();
                },
                error: function(err) {
                    IC.Public.hideLoading();
                    OC.MVC.util.errorMessage(err.responseText);
                }
            });

        }
    },

    onBackClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("ProxyVotingDirections", "Confirm");
        var form = $("form#ProxyVotingHolderForm");
        var data = form.serializeObject();
        IC.Public.showLoading();
        $.ajax({
            url: url, type: 'POST', data: data,
            success: function(result) {
                OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                IC.Public.hideLoading();
            },
            error: function(err) {
                IC.Public.hideLoading();
                OC.MVC.util.errorMessage(err.responseText);
            }
        });

    },

    onCancelClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("ProxyVotingList", "Votes");
        IC.Public.showLoading("");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url);
        //IC.Public.hideLoading();
    },

    onProxyTypeChanged: function(events) {
        var _obj = events.data.obj;
        var checkedvalue = $(this).val();
        if (checkedvalue == 1) {
            $("#AnotherPerson").hide();
        }
        else {
            $("#AnotherPerson").show();
        }
    }

    ,
    onProxyHolderTypeChanged: function(events) {
        var _obj = events.data.obj;
        var selectedValue = $(this).val();

        this.divNames = $("#Names");
        this.divBodyCorporate = $("#BodyCorporate");

        if (selectedValue == 'Body_Corporate') {
            this.divNames.hide();
            this.divBodyCorporate.show();
        }
        else if (selectedValue == 'Name') {
            this.divNames.show();
            this.divBodyCorporate.hide();
        }
        else {
            this.divNames.hide();
            this.divBodyCorporate.hide();
        }


    },
    onContactUsClick: function(events) {
        var url = OC.MVC.util.getLink("OpenAccess", "ContactUs");
        window.open(url, '_blank');
    },
    init: function() {

        this.rbProxyHolder = $("#rb_ProxyHolder_1");
        this.rbProxyHolder.show();
        this.labelProxyHolder = $("label[for=rb_ProxyHolder_1]");
        this.labelProxyHolder.show();

        this.btnNext = $('#btnNext');
        this.btnNext.bind("click", { obj: this }, this.onNextClick);
        this.btnCancel = $('#btnCancel');
        this.btnCancel.bind("click", { obj: this }, this.onCancelClick);
        this.btnBack = $('#btnBack');
        this.btnBack.bind("click", { obj: this }, this.onBackClick);
        this.ContactUs = $('#lnkContactUs');
        this.ContactUs.bind("click", { obj: this }, this.onContactUsClick);

        //divs

        this.ProxyType = $("input[name=ProxyType]");
        this.ProxyType.bind("change", { obj: this }, this.onProxyTypeChanged);

        this.divNames = $("#Names");
        this.divBodyCorporate = $("#BodyCorporate");
        var preDefinedProxyHolder = $("input[name=preDefinedProxyHolder]");

        //alert(preDefinedProxyHolder.val());


        preDefinedProxyHolder.bind("change", { obj: this }, this.onProxyHolderTypeChanged);
        var selectDefinedProxyHolder = $("input[name=preDefinedProxyHolder]:checked");
        if (selectDefinedProxyHolder.length > 0) {
            selectDefinedProxyHolder.trigger("change");
        }

        $('input').unbind('keypress');
        $('input').bind('keypress', function(e) {
            if (e.which >= 32) {
                var key = String.fromCharCode(e.which);
                if (/[a-zA-Z0-9- ]/.test(key)) {
                    return true;
                }
                else {
                    return false;
                }
            }
            return true;
        });
    }
}

;IC.Public.proxyvotingconfirm = function(options) {
    if (options) {
    }
}

IC.Public.proxyvotingconfirm.prototype = {
    onNextClick: function(events) {
        if ($(this).parent().hasClass("disabled")) return;
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("ProxyVotingConfirm", "Confirm");
        if (events.data.resubmit) {
            url = OC.MVC.util.getLink("ProxyVotingConfirm", "ReConfirm");
        }
        var form = $("form#ProxyVotingConfirmForm");
        var data = form.serializeObject();
        if (form.valid()) {
            IC.Public.showLoading();
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function(result) {
                    OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                },
                error: function(err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },
    onPreviousStep: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("ProxyVotingDirections", "Confirm");
        var form = $("form#ProxyVotingConfirmForm");
        var data = form.serializeObject();
        OC.MVC.util.loadMainContainerView(url, data);
    },
    onBackClick: function(events) {
        var _obj = events.data.obj;
        var DirectVoteType = $("#IsDirectVoteType");
        var url = OC.MVC.util.getLink("ProxyVotingConfirm", "Back");

        var form = $("form#ProxyVotingConfirmForm");
        var data = form.serializeObject();
        if (form.valid()) {
            IC.Public.showLoading();
            $.ajax({
                url: url, type: 'POST', data: data,
                success: function(result) {
                    OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                },
                error: function(err) {
                    IC.Public.hideLoading();
                    OC.MVC.util.errorMessage(err.responseText);
                }
            });

        }
    },

    onCancelClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("ProxyVotingList", "Votes");
        IC.Public.showLoading("");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url);
        //IC.Public.hideLoading();
    },

    init: function() {
        highlightMenu('voting');
        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind("click");
        this.btnCancel.bind("click", { obj: this }, this.onCancelClick);

        this.btnBack = $('#btnBack');
        this.btnBack.unbind("click");
        this.btnBack.bind("click", { obj: this }, this.onBackClick);

        this.btnNext = $('#btnNext');
        this.btnNext.unbind("click");
        this.btnNext.bind("click", { obj: this }, this.onNextClick);

        var IsForwardToPreviousPage = $("[name=IsForwardToPreviousPage]");

        this.btnOk = $('#btnOk');
        if (this.btnOk.length > 0) {
            this.btnOk.unbind("click");
            if (IsForwardToPreviousPage.length > 0) {
                this.btnOk.bind("click", { obj: this }, this.onPreviousStep);
            }
            else {
                this.btnOk.bind("click", { obj: this, resubmit: true }, this.onNextClick);
            }
        }
        //IC.Public.updateCampaignsPanel('OtherVoting');
    }
}

;IC.Public.annualgeneralmeeting = function(options) {
    if (options) {
    }
}

IC.Public.annualgeneralmeeting.prototype = {
    onBackClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("ProxyVotingList", "Votes");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url);
    },

    onSubmit: function(events) {
        var _obj = events.data.obj;
        var form = $("form#AnnualGeneralMeetingQuestionsForm");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("AnnualGeneralMeetingQuestions", "Confirm");
        form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url, type: 'POST', data: data,
                success: function(result) {
                    $("#" + OC.MVC.constants.mainContainer).html(result);
                    IC.Public.hideLoading();
                },
                error: function(err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },

    onMeetingChanged: function(events) {
        var _obj = events.data.obj;

        if (!_obj.isLoading) {
            var selectedOption = $(_obj.meetingList).find('option:selected');
            if ($(selectedOption).get(0) != undefined) {
                var url = OC.MVC.util.getLink("AnnualGeneralMeetingQuestions", "Render");
                var data = { viewKey: _obj.viewKey, meetingKey: selectedOption.val() };
                OC.MVC.util.loadMainContainerView(url, data);
            }
        }
    },

    init: function() {
        highlightMenu('voting');

        this.viewKey = $("#ViewKey").val();

        this.meetingList = $("#MeetingKey");
        this.meetingList.unbind('change');
        this.meetingList.bind('change', { obj: this }, this.onMeetingChanged);

        this.btnBack = $('#btnBack');
        this.btnBack.bind("click", { obj: this }, this.onBackClick);

        this.btnSubmit = $('#btnSubmit');
        this.btnSubmit.bind("click", { obj: this }, this.onSubmit);
    }
};IC.Public.thankyouquestion = function(options) {
}

IC.Public.thankyouquestion.prototype = {
    onReturnMeetingListClick: function (events) {
        var url;
        if ($("#IsGroupVoting").val().toLowerCase() === "true") {
            url = OC.MVC.util.getLink("VotingType", "ThankYou");
        } else {
            url = OC.MVC.util.getLink("ProxyVotingList", "Votes");
        }
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url);
    },

    onSubmitAnotherQuestionsClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("AnnualGeneralMeetingQuestions", "Index");
        var data = { key: $('[name=MeetingKey]').val() };
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url, data);
    },

    init: function() {
        highlightMenu('voting');
        $("#btnReturnMeetingList").bind("click", { obj: this }, this.onReturnMeetingListClick);
        $("#btnSubmitAnotherQuestions").bind("click", { obj: this }, this.onSubmitAnotherQuestionsClick);
    }
}
;IC.Public.proxyVotingReceipt = function(options) {
    if (options) {
    }
}

IC.Public.proxyVotingReceipt.prototype = {
    onReceiptClick: function () {
        var data = { transactionID: $("[name=TransactionID]").val(),
            transactionCode: $("[name=TransactionCode]").val()
        };
        window.open(OC.MVC.util.getLink("ProxyVotingConfirm", "DownloadReceipt") + '?' + OC.MVC.JSON.ToUrl(data), data.transactionCode, null, null);
    },
    onUpdateCommClick: function() {
        var url = OC.MVC.util.getLink("ProxyVotingCommunications", "Index");
        OC.MVC.util.loadMainContainerView(url + "?MeetingKey=" + $("#ProxyVotingHolderModel_MeetingKey").val());
        
    },
    onVotingActionClick: function (events) {
        var meetingKey = $("#AssociatedMeetingID").val();
        var data = null;
        if (meetingKey) {
            data = { meetingKey: meetingKey, IsDeclarationAccepted: false, IsGroupedMeetingActive: true };
        }
        var url = OC.MVC.util.getLink("VotingType", "Index");
        IC.Public.showLoading("");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url, data);
    },
    onReturnToMeetingListClick: function (events) {
        OC.MVC.util.loadMainContainerView(OC.MVC.util.getLink("ProxyVotingList", "Votes"));
    },
    onThankYouClick: function (events) {
        var url;
        if ($("#IsUpdateComm").val().toUpperCase() === "TRUE") {
            url = OC.MVC.util.getLink("ProxyVotingCommunications", "Index");
            OC.MVC.util.loadMainContainerView(url + "?MeetingKey=" + $("#ProxyVotingHolderModel_MeetingKey").val());

        } else {
            url = OC.MVC.util.getLink("VotingType", "ThankYou");
            OC.MVC.util.loadMainContainerView(url + "?MeetingKey=" + $("#ProxyVotingHolderModel_MeetingKey").val()); 
        }
        
    },
    init: function () {
        var downLoadReceipt = $("#downLoadReceipt");
        downLoadReceipt.unbind("click");
        downLoadReceipt.bind("click", this.onReceiptClick);
        if ($("#IsUpdateComm").val().toUpperCase() === "TRUE") {
            var updateComm = $("#updateComm");
            updateComm.unbind("click");
            updateComm.bind("click", this.onUpdateCommClick);
        }
        $("#btnOK").bind("click", this.onReturnToMeetingListClick);
        $("#btnNextMeeting").bind("click", this.onVotingActionClick);
        $("#btnThankYou").bind("click", this.onThankYouClick);

        $("#btnDone").bind("click", function () {
            var customUrl = $(this).parent('.orangeButton').attr('url');
            if (customUrl != "" && customUrl != null) {
                window.location = customUrl;
            } else {
                OC.MVC.util.loadMainContainerView(OC.MVC.util.getLink("ProxyVotingList", "Votes"));
            }

        });

        //IC.Public.updateCampaignsPanel('OtherVoting');
    }
};
IC.Public.preferenceVoting = function (formName) {
    this.formObj = $('FORM[name=' + formName + ']');
    this.init();
};

IC.Public.preferenceVoting.prototype = {
    init: function () {
        this.registerEvents();
    },
    registerEvents: function () {
        this.formObj.unbind('submit');
        this.formObj.bind('submit', { obj: this }, this.onSubmit);

        this.btnNext = $('#btnUpdate');
        this.btnNext.unbind("click");
        this.btnNext.bind("click", { obj: this }, this.onNextClick);

        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind("click");
        this.btnCancel.bind("click", { obj: this }, this.onCancelClick);

        this.btnBack = $('#btnBack');
        this.btnBack.unbind("click");
        this.btnBack.bind("click", { obj: this }, this.onBackClick);

    },
    onSubmit: function (events) {
        var self = events.data.obj;
        var url = this.action;

        IC.Public.showLoading();
        $.ajax({
            url: url,
            type: 'POST',
            data: self.formObj.serializeObject(),
            success: function (result) {
                OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });

        return false;
    },
    onNextClick: function (events) {
        var self = events.data.obj;
        if (!self.isNextClicked) {
            self.isNextClicked = true;
            self.formObj.submit();
        }
    },
    onBackClick: function (events) {
        var self = events.data.obj;
        if (!self.isBackClicked) {
            self.isBackClicked = true;
            var data = self.formObj.serializeObject();
            var url = OC.MVC.util.getLink("VotingType", "Index");
            IC.Public.showLoading();
            $.ajax({
                url: url,
                type: 'POST',
                data: { meetingKey: data.MeetingKey, IsDeclarationAccepted: true },
                success: function (result) {
                    OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                    IC.Public.hideLoading();
                },
                error: function (err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },
    onCancelClick: function (events) {
        var url = OC.MVC.util.getLink("ProxyVotingList", "Votes");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url);
    }

};

;
IC.Public.preferenceVotingConfirm = function (formName) {
    this.formObj = $('FORM[name=' + formName + ']');
    this.init();
};

IC.Public.preferenceVotingConfirm.prototype = {
    init: function () {
        this.registerEvents();
    },
    registerEvents: function () {
        this.formObj.unbind('submit');
        this.formObj.bind('submit', { obj: this }, this.onSubmit);

        this.btnNext = $('#btnUpdate');
        this.btnNext.unbind("click");
        this.btnNext.bind("click", { obj: this }, this.onNextClick);

        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind("click");
        this.btnCancel.bind("click", { obj: this }, this.onCancelClick);

        this.btnBack = $('#btnBack');
        this.btnBack.unbind("click");
        this.btnBack.bind("click", { obj: this }, this.onBackClick);
    },
    onSubmit: function (events) {
        var self = events.data.obj;
        var url = this.action;

        IC.Public.showLoading();
        $.ajax({
            url: url,
            type: 'POST',
            data: self.formObj.serializeObject(),
            success: function (result) {
                OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });

        return false;
    },
    onNextClick: function (events) {
        var self = events.data.obj;
        if (!self.isNextClicked) {
            self.isNextClicked = true;
            self.formObj.submit();
        }
    },
    onBackClick: function (events) {
        var self = events.data.obj;
        if (!self.isBackClicked) {
            self.isBackClicked = true;
            var data = $.extend(self.formObj.serializeObject(), { IsDeclarationAccepted: true });
            var url = OC.MVC.util.getLink("PreferenceVoting", "Index");
            IC.Public.showLoading();
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function(result) {
                    OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                    IC.Public.hideLoading();
                },
                error: function(err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },
    onCancelClick: function (events) {
        var url = OC.MVC.util.getLink("ProxyVotingList", "Votes");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url);
    }
};

;IC.Public.proxyThankYou = function(options) {
    if (options) {
    }
}

IC.Public.proxyThankYou.prototype = {
  
    onReturnToMeetingListClick: function (events) {
        var url = $("#ReturnToMeetingListURL").val();
        if (url == "ProxyVotingList/Votes") {
            OC.MVC.util.loadMainContainerView(OC.MVC.util.getLink("ProxyVotingList", "Votes"));
        }
        else {
            window.location.href = url;
        }
    },
    init: function () {
        $("#btnReturnToMeetingList").bind("click", this.onReturnToMeetingListClick);
    }
};IC.Public.proxyVotingCommunication = function (options) {
}

IC.Public.proxyVotingCommunication.prototype = {

    onUpdateClick: function (events) {
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        var _obj = events.data.obj;
        var form = $("form#ProxyVotingCommunicationForm");
        var data = form.serialize();
        var url = OC.MVC.util.getLink("ProxyVotingCommunications", "Confirm");
        form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function (result) {
                    if (result && result.Status == "ERROR") {
                        var emailAddress = $('[name=ChangeEmail]');
                        IC.Public.removeErrorFor(emailAddress, form, result.ErrorMessage);
                        IC.Public.displayErrorFor(emailAddress, form, result.ErrorMessage, 'error');
                    }
                    else {
                        $("#" + OC.MVC.constants.mainContainer).html(result);
                    }
                    IC.Public.hideLoading();
                },
                error: function (err) {
                    IC.Public.hideLoading();
                    OC.MVC.util.errorMessage(err.responseText);
                }
            });
        }
    },

    onCancelClick: function (events) {

        if ($(this).parent().hasClass("disabled")) return;
            var _obj = events.data.obj;
            var url = OC.MVC.util.getLink("ProxyVotingCommunications", "CancleOrThankYou");
            
            var form = $("form#ProxyVotingCommunicationForm");
            var data = form.serializeObject();
            if (form.valid()) {
                IC.Public.showLoading();
                $.ajax({
                    url: url,
                    type: 'POST',
                    data: data,
                    success: function(result) {
                        if (result.redirectToUrl == "True")
                            window.location.href = result.redirectUrl;
                        else
                            OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                    },
                    error: function(err) {
                        OC.MVC.util.errorMessage(err.responseText);
                        IC.Public.hideLoading();
                    }
                });
            }
    },

    init: function () {
        $("#errorContainer").hide();
        if ($("#portfolioMenuLink").attr("type") != undefined)
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");

        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind("click");
        this.btnCancel.bind("click", { obj: this }, this.onCancelClick);

        this.btnUpdate = $("#btnUpdate");
        this.btnUpdate.unbind("click");
        this.btnUpdate.bind("click", { obj: this }, this.onUpdateClick);

        var isThirdPartyImpersonateUser = $("#IsThirdPartyImpersonateUser").val();
        if (isThirdPartyImpersonateUser != undefined) {
            if (isThirdPartyImpersonateUser.toLowerCase() == "true") {
                //$("#btnUpdate").attr('disabled', true);
                $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
                IC.Public.disableControls('PreferencesForm');
                $("#ViewKey").removeAttr('disabled');
            } else {
                $("#btnUpdate").removeAttr('disabled');
            }
        }
    }
}
;IC.Public.proxyVotingCommunicationConfirm = function (options) {
}

IC.Public.proxyVotingCommunicationConfirm.prototype = {

    

    onThankyouClick: function (events) {
        if ($(this).parent().hasClass("disabled")) return;
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("ProxyVotingCommunications", "CancleOrThankYou");

        var form = $("form#ProxyVotingCommunicationConfirmForm");
        var data = form.serializeObject();
        if (form.valid()) {
            IC.Public.showLoading();
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function (result) {
                    if(result.redirectToUrl == "True")
                        window.location.href = result.redirectUrl;
                    else
                    OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                },
                error: function (err) {
                    OC.MVC.util.errorMessage(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },

    init: function () {
        $("#errorContainer").hide();
        if ($("#portfolioMenuLink").attr("type") != undefined)
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");

        this.btnThankyou = $('#btnThankyou');
        this.btnThankyou.unbind("click");
        this.btnThankyou.bind("click", { obj: this }, this.onThankyouClick);

        var isThirdPartyImpersonateUser = $("#IsThirdPartyImpersonateUser").val();
        if (isThirdPartyImpersonateUser != undefined) {
            if (isThirdPartyImpersonateUser.toLowerCase() == "true") {
                //$("#btnUpdate").attr('disabled', true);
                $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
                IC.Public.disableControls('PreferencesForm');
                $("#ViewKey").removeAttr('disabled');
            } else {
                $("#btnUpdate").removeAttr('disabled');
            }
        }
    }
}
;
IC.Public.annualtaxstatements = function (options) {
};

IC.Public.annualtaxstatements.prototype = {
    OnGoClick: function (events) {

        var _obj = events.data.obj;

        var data = { finYear: parseInt(_obj.finYearList.val()) };

        $('#filterDateFrom').text(data.finYear - 1);
        $('#filterDateTo').text(data.finYear);

        var filterData = _obj.holdingFilter.getHoldingFilterData();
        $.extend(data, filterData);

        _obj.annualTaxStatementsGrid.setPostData(data);
        IC.Public.registerGA(window.location.hash, _obj.annualTaxStatementsGrid.getPostData());
        _obj.annualTaxStatementsGrid.trigger('reloadGrid', [{ page: 1 }]);
    },

    onFilterGrid: function (events, viewKey) {
        //var _obj = events.data.obj;
        //var filterData = _obj.holdingFilter.getHoldingFilterData();
        //$("#viewName").html($("#ViewKey option:selected").text());
        //_obj.annualTaxStatementsGrid.appendPostData(filterData);
        //_obj.annualTaxStatementsGrid.trigger('reloadGrid', [{ page: 1 }]);
        var _obj = events.data.obj;
        if (!_obj.isLoading) {
            var url = OC.MVC.util.getLink("AnnualTaxStatements", "Render");
            var data = { viewKey: viewKey };
            IC.Public.showLoading("");
            OC.MVC.util._loadView("viewBarDetails", url, data);
            //IC.Public.hideLoading();
        }
    },

    onPdfClick: function (events) {
        IC.Public.showLoading();
        var form = $("form#AnnualTaxForm");

        var data = {
            taxYearEnd: $(this).attr('taxYearEnd'),
            hrnKey: $(this).attr('hrnKey'),
            issuerCode: $(this).attr('issuerCode'),
            securityCode: $(this).attr('SecurityCode'),
            fileName: $(this).attr('fileName'),
            isAtoReport: $(this).attr('isAtoReport'),
            statementDate: $(this).attr('statementDateValue'),
            abnNumber: $(this).attr('abnNumber'),
            employeeCode: $(this).attr('employeeCode'),
            taxFileNumber: $(this).attr('taxFileNumber'),
            amendmentToReportInd: $(this).attr('amendmentToReportInd'),
            amendmentCount: $(this).attr('amendmentCount')
        };

        var errorMessage = "The document you have requested cannot be located at this time. Please try again later or contact Link Market\n Services on";

        if ($("#CurrentRegistry").val().toLowerCase() == "nzl") {
            errorMessage += " 09&nbsp;375-5998";
        } else {
            errorMessage += " 1300&nbsp;554&nbsp;474 or (02)&nbsp;8280&nbsp;7111";
        }
        if ($('#IsEmployeeLogin').val().toUpperCase() == 'TRUE') {
            errorMessage = 'The document you have requested cannot be downloaded this time. Please try again later.';
        }
        IC.Public.removeErrorFor(undefined, form, errorMessage);
        if (data.amendmentToReportInd == "false") {
            $.ajax({
                url: OC.MVC.util.getLink("AnnualTaxStatements", "PrepareTaxStatement"),
                type: 'POST',
                data: data,
                success: function (result) {
                    IC.Public.hideLoading();

                    if (result == 'True') {
                        data = {
                            taxYearEnd: data.taxYearEnd,
                            hrnKey: data.hrnKey,
                            issuerCode: data.issuerCode,
                            securityCode: data.securityCode,
                            fileName: data.fileName,
                            isAtoReport: data.isAtoReport,
                            statementDate: data.statementDate,
                            abnNumber: data.abnNumber,
                            employeeCode: data.employeeCode,
                            taxFileNumber: data.taxFileNumber,
                            amendmentToReportInd: "false"
                        };
                        window.location = OC.MVC.util.getLink("AnnualTaxStatements", "GetTaxStatement") + '?' + OC.MVC.JSON.ToUrl(data);
                    }
                    else {
                        IC.Public.displayErrorFor(undefined, form, errorMessage);
                    }
                },
                error: function (err) {
                    IC.Public.hideLoading();
                    IC.Public.displayErrorFor(undefined, form, errorMessage);
                }
            });
        }
        else if (data.amendmentToReportInd == "true" && data.amendmentCount == 1) {
            $.ajax({
                url: OC.MVC.util.getLink("AnnualTaxStatements", "PrepareTaxStatement"),
                type: 'POST',
                data: data,
                success: function (result) {
                    IC.Public.hideLoading();

                    if (result == 'True') {
                        data = {
                            taxYearEnd: data.taxYearEnd,
                            hrnKey: data.hrnKey,
                            issuerCode: data.issuerCode,
                            securityCode: "",
                            fileName: "",
                            isAtoReport: "true",
                            statementDate: data.statementDate,
                            abnNumber: data.abnNumber,
                            employeeCode: data.employeeCode,
                            taxFileNumber: data.taxFileNumber,
                            amendmentToReportInd: "true"
                        };
                        window.location = OC.MVC.util.getLink("AnnualTaxStatements", "GetTaxStatement") + '?' + OC.MVC.JSON.ToUrl(data);
                    }
                    else {
                        IC.Public.displayErrorFor(undefined, form, errorMessage);
                    }
                },
                error: function (err) {
                    IC.Public.hideLoading();
                    IC.Public.displayErrorFor(undefined, form, errorMessage);
                }
            });
        }
        else {

            //Find how many amendments are there. If there are more than one open popup, otherwise directly generate the statement by storing in physical location.
            IC.Public.hideLoading();



            var url = OC.MVC.util.getLink("AnnualTaxStatements", "OpenAtoAmendmentsModal");

            OC.MVC.util.showModalByUrl(url, { viewKey: $("#ViewKey").val(), finYear: data.taxYearEnd, hrnKey: data.hrnKey, issuerCode: data.issuerCode, securityCode: data.securityCode, fileName: data.fileName, statementDate: data.statementDate, abnNumber: data.abnNumber, employeeCode: data.employeeCode, taxFileNumber: data.taxFileNumber }, { minHeight: 150, maxHeight: 300, maxWidth: 500, minWidth: 700 }, function () {

                $("#simplemodal-container").addClass("vestingschedulemodalwrap");
                var parentDiv = $('#simplemodal-data').parent();
                parentDiv.css("overflow", "hidden");

            });


        }
    },

    init: function () {

        if ($("#IsEmployeeNoDataMessage").val().toLowerCase() == 'true') {
            $("#atoGridOther").hide();
            $("#displayTaxText").hide();
            $("#divErrorContainer").show();
        }
        else {
            $("#atoGridOther").show();
            $("#displayTaxText").show();
            $("#divErrorContainer").hide();
        }

        highlightMenu('payment');
        if ($("#portfolioMenuLink").attr("type") != undefined && $("#portfolioMenuLink").attr("type").toLowerCase() == "authorisedportfolio") {
            //$("#mainMenu").css("visibility", "hidden");
            $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
        } else {
            $("#mainMenu").css("visibility", "visible");
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        }
        this.btnGo = $("#btnGo");
        this.btnGo.button();
        this.finYearList = $("[name=FinancialYear]");
        var f = $("form#AnnualTaxForm");

        this.annualTaxStatementsGrid = $("#AnnualTaxStatementsGrid");
        this.btnGo.bind("click", { obj: this }, this.OnGoClick);

        this.holdingFilter = $("form.holdingFilterForm");
        this.holdingFilter.unbind("applyHoldingFilter");
        this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterGrid);

        this.annualTaxStatementsGrid.unbind("grid_gridComplete");
        this.annualTaxStatementsGrid.bind("grid_gridComplete", { obj: this }, function (events) {
            if ($("#IsEmployeePlan").val().toLowerCase() == "true" && $("#AnnualTaxStatementsGrid").parent().children().hasClass("noRecords")) {
                $("#atoGridOther").hide();
                $("#displayTaxText").hide();
                $("#divErrorContainer").show();
            }
            else {
                $("#atoGridOther").show();
                $("#displayTaxText").show();
                $("#divErrorContainer").hide();
            }
            var _obj = events.data.obj;
            $(this).find(".pdfAnnualTaxStatement").unbind("click");
            $(this).find(".pdfAnnualTaxStatement").bind("click", { obj: f }, _obj.onPdfClick);
        });
        IC.Public.updateCampaignsPanel('PaymentsAndTaxAnnualTaxStatements');

        var btnCancel = $("#btnCancel");
        btnCancel.bind("click", { obj: this }, this.onCancelClick);


    },
};

IC.Public.AmendmentTaxstatements = function (options) {
};
IC.Public.AmendmentTaxstatements.prototype = {
    amendmentInit: function () {

        var btnDownloadCurrentStatement = $("#btnDownloadCurrentStatement");

        var btnCancel = $("#btnCancel");
        btnCancel.bind("click", { obj: this }, this.onCancelClick);
        this.AtoAmendmentsGrid = $("#AtoAmendmentsGrid");

        this.AtoAmendmentsGrid.unbind("grid_gridComplete");
        this.AtoAmendmentsGrid.bind("grid_gridComplete", { obj: this }, function (events) {
            var obj = events.data.obj;
            $(this).find(".pdfAnnualTaxStatement").unbind("click");
            $(this).find(".pdfAnnualTaxStatement").bind("click", { obj: obj }, obj.onPdfClick);
        });
        btnDownloadCurrentStatement.unbind("click");
        btnDownloadCurrentStatement.bind("click", { obj: this }, this.btnDownloadCurrentStatement);
    },
    onCancelClick: function (events) {
        //var url = OC.MVC.util.getLink("AnnualTaxStatements", "Index");
        //OC.MVC.util.loadMainContainerView(url);
        $.modal.close();

    },
    btnDownloadCurrentStatement: function (events) {

        //Open recently visited document from the table 
        IC.Public.showLoading();
        var _obj = events.data.obj;


        var data = {
            taxYearEnd: $("[name=FinancialYear]").val(),
            hrnKey: $("[name=HrnKey]").val(),
            issuerCode: $("[name=IssuerCode]").val(),
            statementDate: $("[name=StatementDateValue]").val(),
            abnNumber: $("[name=ABNNumber]").val(),
            employeeCode: $("[name=EmployeeCode]").val(),
            taxFileNumber: $("[name=TaxFileNumber]").val()
        };

        var errorMessage = "The document you have requested cannot be located at this time. Please try again later or contact Link Market<br /> Services on";

        if ($("#CurrentRegistry").val().toLowerCase() == "nzl") {
            errorMessage += " 09&nbsp;375-5998";
        }
        else {
            errorMessage += " 1300&nbsp;554&nbsp;474 or (02)&nbsp;8280&nbsp;7111";
        }

        IC.Public.removeErrorFor(undefined, undefined, errorMessage);
        $.ajax({
            url: OC.MVC.util.getLink("AnnualTaxStatements", "DownloadCurrentAtoAmendmentTaxStatementFile"),
            type: 'POST',
            data: data,
            success: function (result) {
                IC.Public.hideLoading();

                if (result == 'True') {
                    data = {
                        taxYearEnd: data.taxYearEnd,
                        hrnKey: data.hrnKey,
                        issuerCode: data.issuerCode,
                        securityCode: "",
                        fileName: "",
                        isAtoReport: "true",
                        statementDate: data.statementDate,
                        abnNumber: data.abnNumber,
                        employeeCode: data.employeeCode,
                        taxFileNumber: data.taxFileNumber,
                        amendmentToReportInd: "true"
                    };
                    window.location = OC.MVC.util.getLink("AnnualTaxStatements", "GetTaxStatement") + '?' + OC.MVC.JSON.ToUrl(data);
                } else {
                    IC.Public.displayErrorFor(undefined, undefined, errorMessage);
                }
            },
            error: function () {
                IC.Public.hideLoading();
                IC.Public.displayErrorFor(undefined, undefined, errorMessage);
            }
        });
    },
    onPdfClick: function (events) {
        IC.Public.showLoading();


        var data = {
            taxYearEnd: $(this).attr('taxYearEnd'),
            hrnKey: $(this).attr('hrnKey'),
            issuerCode: $(this).attr('issuerCode'),
            securityCode: $(this).attr('SecurityCode'),
            fileName: $(this).attr('fileName'),
            isAtoReport: $(this).attr('isAtoReport'),
            statementDate: $(this).attr('statementDateValue'),
            abnNumber: $(this).attr('abnNumber'),
            employeeCode: $(this).attr('employeeCode'),
            taxFileNumber: $(this).attr('taxFileNumber'),
            amendmentToReportInd: $(this).attr('amendmentToReportInd')

        };


        var errorMessage = "The document you have requested cannot be located at this time. Please try again later or contact Link Market<br /> Services on";

        if ($("#CurrentRegistry").val().toLowerCase() == "nzl") {
            errorMessage += " 09&nbsp;375-5998";
        }
        else {
            errorMessage += " 1300&nbsp;554&nbsp;474 or (02)&nbsp;8280&nbsp;7111";
        }

        IC.Public.removeErrorFor(undefined, undefined, errorMessage);
        $.ajax({
            url: OC.MVC.util.getLink("AnnualTaxStatements", "PrepareTaxStatement"),
            type: 'POST',
            data: data,
            success: function (result) {
                IC.Public.hideLoading();

                if (result == 'True') {
                    data = {
                        taxYearEnd: data.taxYearEnd,
                        hrnKey: data.hrnKey,
                        issuerCode: data.issuerCode,
                        securityCode: "",
                        fileName: "",
                        isAtoReport: "true",
                        statementDate: data.statementDate,
                        abnNumber: data.abnNumber,
                        employeeCode: data.employeeCode,
                        taxFileNumber: data.taxFileNumber,
                        amendmentToReportInd: "true"
                    };
                    window.location = OC.MVC.util.getLink("AnnualTaxStatements", "GetTaxStatement") + '?' + OC.MVC.JSON.ToUrl(data);
                } else {
                    IC.Public.displayErrorFor(undefined, undefined, errorMessage);
                }
            },
            error: function (err) {
                IC.Public.hideLoading();
                IC.Public.displayErrorFor(undefined, undefined, errorMessage);
            }
        });


    }
};IC.Public.updateTaxDetails = function(options) {
}

IC.Public.updateTaxDetails.prototype = {
    onUpdateClick: function (events) {
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("UpdateTaxDetails", "ConfirmPage");
        var form = $("form#UpdateTaxDetailsForm");

        if ($("#IsNonAustralianResident:checked").get(0) != undefined) {
            $("input.hiddenAusResidentProperty").val("True");
            $("input[name$='ABN']").removeClass("required");
            $("input[name$='TFN']").removeClass("required");
        }
        else {
            $("input.hiddenAusResidentProperty").val("False");
            $("input[name$='ABN']").addClass("required");
            $("input[name$='TFN']").addClass("required");
        }

        var data = form.serialize();
        var fromLogin = null;
        var missingTax = null;
        if ($('#IsNavigatedfromLoginScreen').val() != undefined && $('#IsNavigatedfromLoginScreen').val().toLowerCase() == "true")
            fromLogin = true;
        if ($('#IsMissingTaxDetailsEnabled').val() != undefined && $('#IsMissingTaxDetailsEnabled').val().toLowerCase() == "true")
            missingTax = true;
        if(fromLogin && missingTax) {
            var selector = ".updatecheckbox:checked";
            var checkedValues = $(selector).map(function() { return $(this).val(); }).get();
            if (checkedValues) {
                data += '&HoldingIDs=' + checkedValues.join(',');
            }
            //data += $('#RowsToBeUpdated').val();

            form.validate(IC.Public.getValidatorCfg());
            if (form.valid()) {
                IC.Public.showLoading("");
                $.ajax({
                    url: url,
                    type: 'POST',
                    data: data,
                    success: function(result) {
                        if (result && result.Status == undefined && result.ErrorMessage == undefined) {
                            $("#" + OC.MVC.constants.mainContainer).html(result);
                        }
                        else if (result && result.Status && result.Status == "SUCCESS") {
                            $("#" + OC.MVC.constants.mainContainer).html(result);
                        }
                        else {
                            //var emailAddress = $("[name='EmailAddress']");
                            IC.Public.removeErrorFor("", form, result.ErrorMessage);
                            IC.Public.displayErrorFor("", form, result.ErrorMessage, 'error');
                        }
                        IC.Public.hideLoading();
                    },
                    error: function(err) {
                        $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                        IC.Public.hideLoading("");
                    }
                });

            }
        }
        else {
        var isOtherHoldingsVisible = $('#IsOtherHoldingsVisible').val();
        if (isOtherHoldingsVisible && isOtherHoldingsVisible.toLowerCase() == "true") {
            var selectedOption = $("#ApplyToAllHoldings_1:checked").get(0);
            var selector = ".updatecheckbox:checked";
            if (($("#IsShowOtherHoldings:checked").val() != undefined) && (selectedOption != undefined)) {
                selector = ".updatecheckbox";
            }
            var checkedValues = $(selector).map(function() { return $(this).val(); }).get();
            if (checkedValues) {
                data += '&HoldingIDs=' + checkedValues.join(',');
            }
            data += "&ApplyToAllHoldings=" + $("[name=ApplyToAllHoldings]:checked").val();
        } else {
            data += '&HoldingIDs=' + $('#RowsToBeUpdated').val();
        }

        form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function(result) {
                    if (result && result.Status == undefined && result.ErrorMessage == undefined) {
                        $("#" + OC.MVC.constants.mainContainer).html(result);
                    }
                    else if (result && result.Status && result.Status == "SUCCESS") {
                        $("#" + OC.MVC.constants.mainContainer).html(result);
                    }
                    else {
                        //var emailAddress = $("[name='EmailAddress']");
                        IC.Public.removeErrorFor(null, form, result.ErrorMessage);
                        IC.Public.displayErrorFor(null, form, result.ErrorMessage, 'error');
                    }
                    IC.Public.hideLoading();
                },
                error: function(err) {
                    $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                    IC.Public.hideLoading("");
                }
            });

        }
        }
    },

    onSkipClick: function () {
        this.doNotDisplayAgain = $("input[name=DoNotDisplayAgain]");
        //if ($(this.doNotDisplayAgain).attr("checked")) {
        if ($(this.doNotDisplayAgain).is(":checked")) {
            IC.Public.showLoading("");
            $.ajax({
                url: OC.MVC.util.getLink("UpdateTaxDetails", "SavePortfolioCampaignDoNotDisplayTickBox"),
                type: 'POST',
                success: function (result) {
                    if ($('#IsMissingCommunicationDetailsEnabled').val().toLowerCase() == "true") {
                        if ($('#IsEmployeeLogin').val() != undefined && $('#IsEmployeeLogin').val().toLowerCase() == "true") {
                            var urlComm = "/Employee/" + $('#IssuerCode').val() + "/CommunicationsPreferences/Preferences?isRedirectedFromLoginPage=" + true;
                        } else {
                            var urlComm = "/CommunicationsPreferences/Preferences?isRedirectedFromLoginPage=" + true;
                        }
                        window.location.href = urlComm;
                    }
                    else if ($('#IsMissingFATCACRSEnabled').val().toLowerCase() == "true") {
                        if ($('#IsEmployeeLogin').val() != undefined && $('#IsEmployeeLogin').val().toLowerCase() == "true") {
                            var urlFATCA = "/Employee/" + $('#IssuerCode').val() + "/ForeignTaxResidency/ForeignTaxResidencyCertificationIndex?isRedirectedFromLoginPage=" + true;
                        } else {
                            var urlFATCA = "/ForeignTaxResidency/ForeignTaxResidencyCertificationIndex?isRedirectedFromLoginPage=" + true;
                        }
                        window.location.href = urlFATCA;
                    } else {
                        if ($('#IsEmployeeLogin').val() != undefined && $('#IsEmployeeLogin').val().toLowerCase() == "true") {
                            window.location.href = "/Employee/" + $('#IssuerCode').val();
                        } else {
                            var urlHoldings = "/";
                            window.location.href = urlHoldings;
                        }
                    }
                    IC.Public.hideLoading();
                },
                error: function (err) {
                    $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                    IC.Public.hideLoading("");
                }
            });

        } else {
            if ($('#IsMissingCommunicationDetailsEnabled').val().toLowerCase() == "true") {
                if ($('#IsEmployeeLogin').val() != undefined && $('#IsEmployeeLogin').val().toLowerCase() == "true") {
                    var urlComm = "/Employee/" + $('#IssuerCode').val() + "/CommunicationsPreferences/Preferences?isRedirectedFromLoginPage=" + true;
                } else {
                    var urlComm = "/CommunicationsPreferences/Preferences?isRedirectedFromLoginPage=" + true;
                }
                window.location.href = urlComm;
            }
            else if ($('#IsMissingFATCACRSEnabled').val().toLowerCase() == "true") {
                if ($('#IsEmployeeLogin').val() != undefined && $('#IsEmployeeLogin').val().toLowerCase() == "true") {
                    var urlFATCA = "/Employee/" + $('#IssuerCode').val() + "/ForeignTaxResidency/ForeignTaxResidencyCertificationIndex?isRedirectedFromLoginPage=" + true;
                } else {
                    var urlFATCA = "/ForeignTaxResidency/ForeignTaxResidencyCertificationIndex?isRedirectedFromLoginPage=" + true;
                }
                window.location.href = urlFATCA;
            }
            else {
                if ($('#IsEmployeeLogin').val() != undefined && $('#IsEmployeeLogin').val().toLowerCase() == "true") {
                    window.location.href = "/Employee/" + $('#IssuerCode').val();
                } else {
                    var urlHoldings = "/";
                    window.location.href = urlHoldings;
                }
            }
        }
    },

    onHolderIsNonAusResidentCheck: function(events) {
        var _obj = events.data.obj;
        //if ($(this).attr("checked")) {
        if ($(this).is(":checked")) {
            _obj.divCountry.show();
        }
        else {
            _obj.divCountry.hide();
        }
    },

    onHolderIsNzlResidentCheck: function(events) {
        var _obj = events.data.obj;
        //if ($(this).attr("checked")) {
        if ($(this).is(":checked")) {
            _obj.divCountry.hide();
            _obj.divResWht.show();
            _obj.resWhtCodeChange(_obj);
        }
        else {
            _obj.divCountry.show();
            _obj.divResWht.hide();
            _obj.enableControls();
        }
    },

    onFilterSelect: function(events, viewKey) {
        var _obj = events.data.obj;
        if (!_obj.isLoading) {
            var url = OC.MVC.util.getLink("UpdateTaxDetails", "GetTaxDetails");
            var data = { key: viewKey };
            OC.MVC.util._loadView(OC.MVC.constants.mainContainer, url, data);
        }
    },

    onResWhtCodeChange: function(events) {
        var _obj = events.data.obj;
        _obj.resWhtCodeChange(_obj);
    },

    resWhtCodeChange: function(_obj) {
        if ($("#IsResWhtExempt").val().toLowerCase() != "true") {
            var resWhtCode = _obj.ResWhtCode.find("option:selected").val();

            if (resWhtCode == "XMP") {
                _obj.disableControls();
                $("#divResWhtExempt").show();
            }
            else {
                _obj.enableControls();
                $("#divResWhtExempt").hide();
            }
        }
    },

    onDoNotDisplayClick: function (events) {

        if ($(this).is(':checked')) {
            $('#btnUpdate').parent().addClass('disabled');
        } else {
            $('#btnUpdate').parent().removeClass('disabled');
        }
    },

    disableControls: function() {
        $("input[name$='IRD']").attr('disabled', true);
        $("#IsNewZealandResident").attr('disabled', true);
        $("#divOtherHoldings").hide();
        $("#btnUpdate").parent("span").addClass("disabled");
        $("#btnUpdate").attr('disabled', true);
    },

    enableControls: function() {
        $("input[name$='IRD']").removeAttr('disabled');
        $("#IsNewZealandResident").removeAttr('disabled');
        $("#divOtherHoldings").show();
        $("#btnUpdate").parent("span").removeClass("disabled");
        $("#btnUpdate").removeAttr('disabled');
    },

    onActionClick: function (events) {
        var url = events.data.url;
        OC.MVC.util.loadMainContainerView(url, null);
    },

    init: function() {
        this.isLoading = true;
        var fromLogin = null;
        var missingTax = null;
        if ($('#IsNavigatedfromLoginScreen').val() != undefined && $('#IsNavigatedfromLoginScreen').val().toLowerCase() == "true")
            fromLogin = true;
        if ($('#IsMissingTaxDetailsEnabled').val() != undefined && $('#IsMissingTaxDetailsEnabled').val().toLowerCase() == "true")
            missingTax = true;
        if (fromLogin && missingTax) {
            IC.Public.HideMenu();
        }
        this.doNotDisplayAgain = $("input[name='DoNotDisplayAgain']");
        this.doNotDisplayAgain.unbind('click');
        this.doNotDisplayAgain.bind('click', { obj: this }, this.onDoNotDisplayClick);

        highlightMenu('payment');
        if($("#portfolioMenuLink").attr("type") != undefined)
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        $("#btnUpdate").bind("click", { obj: this }, this.onUpdateClick);
        this.divCountry = $("#divCountryofTaxResidence");
        var btnSkip = $("#btnSkip");
        btnSkip.bind("click", { obj: this }, this.onSkipClick);
        var baseCountryCode = $("#BaseCountryCode").val();
        if (baseCountryCode && baseCountryCode.toLowerCase() == "nzl") {
            this.divResWht = $("#divResWht");

            if ($("#IsResWhtExempt").val().toLowerCase() == "true") {
                this.disableControls();
            }
            else {
                this.ResWhtCode = $("#ResWhtCode");
                this.ResWhtCode.unbind("change");
                this.ResWhtCode.bind("change", { obj: this }, this.onResWhtCodeChange);
                this.resWhtCodeChange(this);
            }

            this.holderIsNzlResident = $("input[name='IsNewZealandResident']");
            this.holderIsNzlResident.unbind("change");
            this.holderIsNzlResident.bind("change", { obj: this }, this.onHolderIsNzlResidentCheck);
            //if ($(this.holderIsNzlResident).attr("checked")) {
            if ($(this.holderIsNzlResident).is(":checked")) {
                this.divCountry.hide()
                this.divResWht.show();
                this.resWhtCodeChange(this);
            }
            else {
                this.divCountry.show();
                this.divResWht.hide();
                this.enableControls();
            }
        }
        else {
            this.holderIsNonAusResident = $("input[name='IsNonAustralianResident']");
            this.holderIsNonAusResident.unbind("change");
            this.holderIsNonAusResident.bind("change", { obj: this }, this.onHolderIsNonAusResidentCheck);
            //if ($(this.holderIsNonAusResident).attr("checked")) {
            if ($(this.holderIsNonAusResident).is(":checked")) {
                this.divCountry.show()
            }
            else {
                this.divCountry.hide();
            }
        }

        this.holdingFilter = $("form.holdingFilterForm");
        this.holdingFilter.unbind("applyHoldingFilter");
        this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterSelect);

        this.ResWhtExemptAnchorLink = $("#divResidence").find('a');
        $(this.ResWhtExemptAnchorLink).bind('click', { obj: this.ResWhtExemptAnchorLink, url: $(this.ResWhtExemptAnchorLink).attr('actionurl') }, this.onActionClick);

        if (($("#IsOtherHoldingsEnabled").get(0) != undefined) && ($("#IsOtherHoldingsEnabled").val() == 'False')) {
            $("#divResidence").hide();
        }
        $('html').unbind('keypress');
        $('html').bind('keypress', function(e) {
            if (e.keyCode == 13) {
                if ($("#btnUpdate").is(":disabled")) {
                    return false;
                }
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnUpdate').click();
                }
            }
        });

        var isThirdPartyImpersonateUser = $("#IsThirdPartyImpersonateUser").val();
        if (isThirdPartyImpersonateUser != undefined) {
            if (isThirdPartyImpersonateUser.toLowerCase() == "true") {
                //$("#btnUpdate").attr('disabled', true);
                $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
                IC.Public.disableControls('UpdateTaxDetailsForm');
                $("#ViewKey").removeAttr('disabled');
            } else {
                $("#btnUpdate").removeAttr('disabled');
            }
        }

        this.isLoading = false;
    }
}
;IC.Public.updatetaxdetailsconfirm = function(options) {
}

IC.Public.updatetaxdetailsconfirm.prototype = {
    onBackClick: function (events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("UpdateTaxDetails", "UpdatePageByCacheKey");
        IC.Public.showLoading("");
        OC.MVC.util.loadMainContainerView(url, { cacheKey: $('#CacheKey').val() });
        IC.Public.hideLoading();
    },
    onConfirmClick: function (events) {
        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handleUpdateTaxDetailsConfirmClick();
            return;
        }
        
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        var formData = $('form#UpdateTaxDetailsForm').serializeObject();
        IC.Public.security.validateTransactionPin(formData, function () { self.handleUpdateTaxDetailsConfirmClick(); }, function () { $("#btnConfirm").parent().addClass('disabled'); });
    },

    handleUpdateTaxDetailsConfirmClick: function () {
        var url = OC.MVC.util.getLink("UpdateTaxDetails", "Complete");
        var data = { cacheKey: $("[name=CacheKey]").val() };
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading("");
            }
        });
    },

    init: function (validateTransactionPassword) {
        this.validateTransactionPassword = validateTransactionPassword;
        $("#btnBack").bind("click", { obj: this }, this.onBackClick);
        $("#btnConfirm").bind("click", { obj: this }, this.onConfirmClick);

        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnConfirm').click();
                }
            }
        });
    }
}
;//*******************************************************************************************************
//  
//  File:        ic.public.taxdetails.js
//  Author:      Vladimir Nechypurenko
//  Description: Tax Details partial view script
//  
//*******************************************************************************************************

IC.Public.taxDetails = function (options) {
    if (options) {
        this.key = options.key;
    }
    this.init();
};
IC.Public.taxDetails.prototype = {
    key: '',
    enableControls: function(value, key, ol) {
        if (ol.length == 1) { //if ol will be nested into another ol - we will be in trouble :)
            var controls = ol.find("[refValue='" + value + "'][key='" + key + "']");
            controls.removeAttr('disabled');
            ol.find("._required[refValue='" + value + "'][key='" + key + "']").addClass("required");
            ol.find("._required[refValue='" + value + "'][key='" + key + "']").removeClass("_required");

            controls = ol.find("[refValue!='" + value + "'][key='" + key + "'][type!='radio']");
            controls.attr('disabled', true);
            controls.attr('value', '');
            ol.find(".required[refValue!='" + value + "'][key='" + key + "'][type!='radio']").addClass("_required");
            ol.find(".required[refValue!='" + value + "'][key='" + key + "'][type!='radio']").removeClass("required");

            $(ol).find('input[refvalue="Exemption"]').addClass("_required");
            $(ol).find('input[refvalue="Exemption"]').removeClass("required");

            //Remove all error validation classes for controls of not selected Radio button
            ol.find(".input-validation-error[refValue!='" + value + "'][key='" + key + "']").removeClass("input-validation-error");
            ol.find(".error[refValue!='" + value + "'][key='" + key + "']").removeClass("error");

            if (value == "TFN") {
                ol.find(".tfnType").css('display', 'block');
            }
            else {
                ol.find(".tfnType").css('display', 'none');
            }

            if (value == "Exemption") {
                ol.find(".ui-datepicker-trigger").removeAttr("disabled");
            }
            else {
                ol.find(".ui-datepicker-trigger").attr("disabled", true);
            }
        }
    },
    onRadioBtnClick: function(events) {
        var _obj = events.data.obj;
        var value = $(this).val();
        var key = $(this).attr("key");
        var ol = $(this).parents("ol"); //find ol list
        _obj.enableControls(value, key, ol);
    },
    onExceptionTypeSelect: function(events) {
        var _obj = events.data.obj;
        var value = $(this).val();
        var ol = $(this).parents("ol"); //find ol list
        if (value == _obj.minorExemptionTypeUniqueCode) {
            ol.find(".dob").show();
            ol.find('Input[refvalue="Exemption"]').addClass("required");
            ol.find('Input[refvalue="Exemption"]').removeClass("_required");
        }
        else {
            ol.find(".dob").hide();
            ol.find('Input[refvalue="Exemption"]').addClass("_required");
            ol.find('Input[refvalue="Exemption"]').removeClass("required");
        }
    },
    init: function() {
        //*********** Add client validators *****************

        var isNonAusResident = $("#IsNonAustralianResident:checked").get(0) != undefined;

        var abn_field = $("input[name$='ABN']");
        abn_field.attr("title", "Please enter ABN or TFN before proceeding.");
        abn_field.addClass("required");

        var tfn_field = $("input[name$='TFN']");
        tfn_field.attr("title", "Please enter ABN or TFN before proceeding.");
        tfn_field.addClass("required");

        var dob_field = $("input[name$='DOB']");
        dob_field.attr("title", "Please enter valid Date of Birth before proceeding.");
        dob_field.addClass("required");

        //**************************************************

        var taxDetails = $("div.taxDetails");
        var isThirdPartyImpersonateUser = $("#IsThirdPartyImpersonateUser").val();
        if (isThirdPartyImpersonateUser != undefined) {
            if (isThirdPartyImpersonateUser.toLowerCase() == "true") {
                //$("#btnContinue").attr('disabled', true);
                //IC.Public.disableControls('newHolding');
                //$("#btnCancel").removeAttr('disabled');
                $("#btnBackToAuthorisedPortfolio").removeAttr('disabled');
            }
        }
        var radioBtns = taxDetails.find("input[type='radio'][key='" + this.key + "']");
        var radioBtn = taxDetails.find("input[type='radio'][key='" + this.key + "']:checked");
        var _obj = this;
        $.each(radioBtns, function(index, item) {
            $(item).bind("click", { obj: _obj }, _obj.onRadioBtnClick);
        });

        this.minorExemptionTypeUniqueCode = $('#MinorExemptionTypeUniqueCode').val();
        $("select[name$='ExemptionType']").bind("change", { obj: this }, this.onExceptionTypeSelect);

        taxDetails.find('.required[type=text]').addClass("_required");
        taxDetails.find('.required[type=text]').removeClass("required");

        OC.MVC.validate.validationMsgProcess();
    }
};
;IC.Public.contactUs = function(options) {
};

IC.Public.contactUs.prototype = {
    onSendEmailClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("OpenAccess", "ContactUsSendEmail");
        var form = $("form#ContactUsEmailForm");
        $("#IssuerName").val($("#IssuerCode_text").val());
        var data = form.serialize();

        form.validate(IC.Public.getValidatorCfg());

        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function(result) {
                    $("#" + OC.MVC.constants.mainContainer).html(result);
                    IC.Public.hideLoading();
                },
                error: function(err) {
                    $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                    IC.Public.hideLoading("");
                }
            });
        }
    },

    onClearEmailClick: function(events) {
        $("#IssuerCode_text").val("");
        $("select[name='MessageType']").removeAttr('checked');
        $("#MessageType_Enquiry").attr('checked', 'checked');
        $("select[name='EnquiryType']").val("");
        $("#Hrn").val("");
        $("#PersonName").val("");
        $("#Address").val("");
        $("#EmailAddress").val("");
        $("#PhoneNumber").val("");
        $("#Message").val("");
        $("#mainErrorDiv").find("li").remove();
        $(".error").removeClass("error");
    },

    onIssuerCodeBlur: function (events) {
        $("#Hrn").val('');
    },

    init: function() {
        this.isLoading = true;
        if ($("#portfolioMenuLink").attr("type") != undefined && $("#portfolioMenuLink").attr("type").toLowerCase() == "authorisedportfolio") {
            //$("#mainMenu").css("visibility", "hidden");
            $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
        } else {
            $("#mainMenu").css("visibility", "visible");
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        }
        var d = $(".container");
        d.removeClass("login");
        $('#IssuerCode').removeClass("required");
        $("#btnSendEmail").bind("click", { obj: this }, this.onSendEmailClick);
        $("#btnClearEmail").bind("click", { obj: this }, this.onClearEmailClick);
        $('#IssuerCode_text').bind('blur', { obj: this }, this.onIssuerCodeBlur);
        this.isLoading = false;

        setTimeout(function() {
            $("#Hrn").val('');
            $("#IssuerCode_text").val('');
        }, 1500);
    }
};
;//*******************************************************************************************************
//  
//  File:        ic.public.reinvestmentPlansFormat.js
//  Author:      Vladimir Nechypurenko
//  Description: Format JS functions for ReinvestmentPlans.aspx page
//  Review:      
//
//*******************************************************************************************************

IC.Public.reinvestmentPlansFormat = {
    Action: function(cellValue, options, rowObject) {
        var key = '';
        var index = $(this).getColIndex("ID");
        if (index >= 0) {
            key = rowObject[index];
        }
        var isEnabled = false;
        index = $(this).getColIndex("IsEnabled");
        if (index >= 0) {
            isEnabled = (rowObject[index].toLowerCase() == "true");
        }

        index = $(this).getColIndex("IsThirdPartyImpersonateUser");
        if (index >= 0 && isEnabled) {
            isEnabled = !(rowObject[index].toLowerCase() == "true");
        }

        var planStatusCode = "";
        index = $(this).getColIndex("ReinvestmentPlan.PlanStatus.Code");
        if (index >= 0) {
            planStatusCode = rowObject[index];
        }

        if (planStatusCode == "ACT") {
            if (isEnabled) {
                var a = $('<a>');
                a.attr("href", "javascript:void(0);");
                a.addClass("reinvestmentAction");

                if (!cellValue || cellValue.toLowerCase() === "none") {
                    a.html("Create <br/> Instruction");
                }
                else {
                    a.html("Edit <br/> Instruction");
                }

                a.attr("key", key);

                return a.outerHTML();
            }
            else {
                //Get the disabledMessage
                var disabledMessage = IC.Public.reinvestmentPlansFormat.getDisabledMessage($(this), rowObject);
                
                //Show the above message as Tooltip                
                var span = $("<span>");
                span.addClass("tooltip");
                span.attr("title", disabledMessage);
                span.html("Not Available");
                return span.outerHTML();
            }
        }
        else {
            return "Suspended Plan";
        }
    },

    formDownload: function(cellValue, options, rowObject) {
        if (!cellValue || cellValue == "null") {
            return "";
        }
        else {
            var SecurityCode = "";
            var index = $(this).getColIndex("IssuerCode");
            if (index >= 0) {
                SecurityCode = rowObject[index];
            }

            var a = $("<a>");
            a.attr("target", "_blank");
            a.attr("href", "/ReinvestmentPlans/DownloadForm?issuerCode=" + SecurityCode + "&fileName=" + cellValue);
            a.html("download");
            return a.outerHTML();
        }
    },

    Participation: function (cellValue, options, rowObject) {
        if (!cellValue || cellValue == "null") {
            return cellValue;
        }
        else {
            var span = $("<span>");
            span.addClass("tooltip");
            span.attr("title", cellValue);
            span.html(cellValue);
            return span.outerHTML();
        }
    },
    
    getDisabledMessage: function(gridObj, rowObject) {
        var disabledMessage = $('#UpdateMaxHolderValueExceededTitle').val();
        var dmIndex = gridObj.getColIndex("RestrictionType");
        if (dmIndex >= 0) {
            var restrictionType = rowObject[dmIndex];
            disabledMessage = IC.Public.getRestrictedTypeMessage(restrictionType);
        }

        return disabledMessage;
    }
};//*******************************************************************************************************
//  
//  File:        ic.public.reinvestmentPlans.js
//  Author:      Vladimir Nechypurenko
//  Description: JS for ReinvestmentPlans.aspx page
//  Review:      
//
//*******************************************************************************************************

IC.Public.reinvestmentPlans = function(options) {
    if (options) {
    }
    this.init();
};


IC.Public.reinvestmentPlans.prototype = {
    onActionClick: function() {
        var url = OC.MVC.util.getLink("ReinvestmentPlans", "Instruction");
        
        var data = { key: $(this).attr("key") || "", isEmployee:  $(this).parent().parent().getColumnValue('IsEmployeePlan')};
        
        IC.Public.showLoading("");
        OC.MVC.util.loadMainContainerView(url, data);
        IC.Public.hideLoading();
    },
    onFilterGrid: function(events) {
        var _obj = events.data.obj;
        var filterData = _obj.holdingFilter.getHoldingFilterData();
        _obj.reinvestmentPlansGrid.appendPostData(filterData);
        IC.Public.registerGA(window.location.hash, _obj.reinvestmentPlansGrid.getPostData());
        _obj.reinvestmentPlansGrid.trigger('reloadGrid',[{page:1}]);
    },
    init: function() {
        highlightMenu('payment');
        if ($("#portfolioMenuLink").attr("type") != undefined && $("#portfolioMenuLink").attr("type").toLowerCase() == "authorisedportfolio") {
            //$("#mainMenu").css("visibility", "hidden");
            $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
        } else {
            $("#mainMenu").css("visibility", "visible");
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        }
        this.reinvestmentPlansGrid = $("#ReinvestmentPlansGrid");
        this.reinvestmentPlansGrid.bind("grid_gridComplete", { obj: this }, function(events, gridContext) {
            var _obj = events.data.obj;
            $(this).find(".reinvestmentAction").bind("click", { obj: _obj }, _obj.onActionClick);
        });
        
        this.holdingFilter = $("form.holdingFilterForm");
        this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterGrid);
        
        this.reinvestmentPlansGrid.unbind(OC.MVC.grid.events.loadComplete);
        this.reinvestmentPlansGrid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onLoadComplete);
    },

    onLoadComplete: function (events, gridObj, data) {
        var self = events.data.obj;
        self.processEPlanBalanceValues();
    },

    processEPlanBalanceValues: function () {
        //ReinvestmentPlansGrid_IsEmployeePlan
        $('#ReinvestmentPlansGrid tr').each(function () {
            var isEmployeePlan = $(this).getColumnValue('IsEmployeePlan');
            if (isEmployeePlan != undefined && isEmployeePlan.toLowerCase() == "true") {
                var balanceTd = $(this).getColumnCell('Balance');
                balanceTd.text('');
            }
        });
    }
};//*******************************************************************************************************
//  
//  File:        ic.public.reinvestmentPlanInstruction.js
//  Author:      Vladimir Nechypurenko
//  Description: JS for ReinvestmentPlanInstruction.aspx page
//  Review:      
//
//*******************************************************************************************************

IC.Public.reinvestmentPlanInstruction = function(options) {
    if (options) {
    }
    this.init();
}


IC.Public.reinvestmentPlanInstruction.prototype = {
    PartialOption: "Partial",
    onRadioBtnClick: function(events) {
        var _obj = events.data.obj;

        if ($(this).val() === _obj.PartialOption) {
            _obj.partialPaticipation.show();
        }
        else {
            _obj.partialPaticipation.hide();
        }
    },
    onContinueClick: function(events) {
        if ($(this).parent().hasClass("disabled")) return;
        var form = $("form#ReinvestmentPlanInstruction");
        var url = OC.MVC.util.getLink("ReinvestmentPlans", "Confirm");
        var data = form.serializeObject();
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: "POST",
            data: data,
            success: function (result) {
                OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                //The following has been commented in accordance with Controller changes
//                if (result && result.Status == "SUCCESS") {
//                    var url = OC.MVC.util.getLink("ReinvestmentPlans", "ConfirmPage");
//                    OC.MVC.util.loadMainContainerView(url, { cache: result.Response });
//                }
//                else {
//                    OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
//                }
                IC.Public.hideLoading();
            },
            error: function(err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading("");
            }
        });

    },
    onCancelClick: function(events) {
        var url = OC.MVC.util.getLink("ReinvestmentPlans", "Index");
        OC.MVC.util.loadMainContainerView(url);
    },
    onParticipationTypeSelect: function(events) {
        var _obj = events.data.obj;
        _obj.assignFormat();
        _obj.NumberToParticipate.setNumVal(0);
    },
    assignFormat: function() {
        if (this.ParticipationType.val() == "PER") {
            this.NumberToParticipate.numeric({ format: "#,0.######", thousandsChar: ',', minValue: '0', maxValue: '100' });
        }
        else {
            this.NumberToParticipate.numeric({ format: "#,0", thousandsChar: ',', minValue: '0' });
        }
    },
    init: function() {
        highlightMenu('payment');
        this.partialPaticipation = $("#partialPaticipation");
        this.partialPaticipation.html($("#partialPaticipationTemplate").html());

        $("#partialPaticipationTemplate").remove();

        var planOption = $("input[name=PlanOption]");
        planOption.bind("click", { obj: this }, this.onRadioBtnClick);

        var btnContinue = $("#btnContinue");
        btnContinue.bind("click", { obj: this }, this.onContinueClick);

        var termsAndConditionsAccepted = $("[name=TermsAndConditionsAccepted][type=checkbox]");
        var self = this;
        var hasUnregisteredHrnsValue = $('#HasUnregisteredHrns').val();
        this.hasRegisteredHrns = hasUnregisteredHrnsValue != undefined && hasUnregisteredHrnsValue.toLowerCase() == 'false';
        termsAndConditionsAccepted.bind("change", function() {
            var enableContinueButton = $('input:checkbox[name="TermsAndConditionsAccepted"]').is(':checked') && self.hasRegisteredHrns;
            if (enableContinueButton) {
                btnContinue.parent("span").removeClass("disabled");
            }
            else {
                btnContinue.parent("span").addClass("disabled");
            }
        });
        termsAndConditionsAccepted.change();

        var btnCancel = $("#btnCancel");
        btnCancel.bind("click", { obj: this }, this.onCancelClick);

        var checked = $("input[name=PlanOption]:checked");
        if (checked.length > 0) {
            $(checked).click();
        }

        this.ParticipationType = $("input[name=ParticipationType]");
        if (this.ParticipationType.length == 0) {
            this.ParticipationType = $("select[name=ParticipationType]");
            this.ParticipationType.bind("change", { obj: this }, this.onParticipationTypeSelect);
        }
        this.NumberToParticipate = $("#NumberToParticipate");
        this.NumberToParticipate.addClass("numeric");

        this.assignFormat();

        this.NumberToParticipate.setNumVal(this.NumberToParticipate.val());
        $('html').unbind('keypress');
        $('html').bind('keypress', function(e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnContinue').click();
                }
            }
        });
        OC.MVC.validate.validationMsgProcess();
        
        $("a.cluetooltip").cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 350, cluetipClass: "eplandrp" });
    }
};//*******************************************************************************************************
//  
//  File:        ic.public.reinvestmentPlanConfirm.js
//  Author:      Vladimir Nechypurenko
//  Description: JS for ReinvestmentPlanConfirm.aspx page
//  Review:      
//
//*******************************************************************************************************

IC.Public.reinvestmentPlanConfirm = function (options) {
    if (options) {
        this.numErrors = options.numErrors || 0;
        if (this.numErrors == '') {
            this.numErrors = 0;
        }
        else {
            this.numErrors = parseInt(this.numErrors);
        }
        this.validateTransactionPassword = options.validateTransactionPassword;
    }
    this.init();
}


IC.Public.reinvestmentPlanConfirm.prototype = {
    onContinueClick: function (events) {
        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handleReinvestmentPlanConfirm();
            return;
        }

        if ($(this).parent().hasClass("disabled")) return;

        var isMultiMobileExist = $("#HrnsRequiringPinValidation_IsMultiMobileExist").val();
        if (isMultiMobileExist == "True") {
            self.gotoValidateSmsPin();
        } else {
            if ($('form#ReinvestmentPlanInstruction').children().length <= 4) {
                self.handleReinvestmentPlanConfirm();
                return;
            }
            var formData = $('form#ReinvestmentPlanInstruction').serializeObject();
            IC.Public.security.validateTransactionPin(formData, function () { self.handleReinvestmentPlanConfirm(); }, function () { $("#btnContinue").parent().addClass('disabled'); });
        }
    },

    handleReinvestmentPlanConfirm: function () {
        IC.Public.showLoading("");
        var url = OC.MVC.util.getLink("ReinvestmentPlans", "Complete");
        var data = { cache: $("#CacheKey").val() };
        $.ajax({
            url: url,
            data: data,
            type: "POST",
            success: function (result) {
                OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                IC.Public.hideLoading();
            }
        });
    },

    gotoValidateSmsPin: function () {
        var url = OC.MVC.util.getLink("ReinvestmentPlans", "ConfirmValidatePin");
        var form = $("form#ReinvestmentPlanInstruction");
        var data = form.serialize();
        //data += '&cacheKey=' + $("#CacheKey").val();
        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err);
                IC.Public.hideLoading();
            }
        });
    },
    onBackClick: function () {
        var url = OC.MVC.util.getLink("ReinvestmentPlans", "Instruction");
        var data = { key: $("input[name=Key]").val(), cache: $("#CacheKey").val(), isEmployee: $('#IsEmployeePlan').val().toLowerCase() == "true" };
        
        OC.MVC.util.loadMainContainerView(url, data);
    },
    onCancelClick: function () {
        var url = OC.MVC.util.getLink("ReinvestmentPlans", "Index");
        OC.MVC.util.loadMainContainerView(url);
    },
    init: function () {
        var btnContinue = $("#btnContinue");
        btnContinue.bind("click", { obj: this }, this.onContinueClick);
        if (this.numErrors >= 1) {
            btnContinue.html("Re-submit");
        }

        if (this.numErrors > 3) {
            btnContinue.parent("span").addClass("disabled");
        }

        var btnBack = $("#btnBack");
        btnBack.bind("click", { obj: this }, this.onBackClick);

        var btnCancel = $("#btnCancel");
        btnCancel.bind("click", { obj: this }, this.onCancelClick);

        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnContinue').trigger('click');
                }
            }
        });
    }
}
;//*******************************************************************************************************
//  
//  File:        ic.public.reinvestmentPlanConfirm.js
//  Author:      Vladimir Nechypurenko
//  Description: JS for ReinvestmentPlanConfirm.aspx page
//  Review:      
//
//*******************************************************************************************************

IC.Public.reinvestmentPlanConfirmSmsVal = function (options) {
    if (options) {
        this.numErrors = options.numErrors || 0;
        if (this.numErrors == '') {
            this.numErrors = 0;
        }
        else {
            this.numErrors = parseInt(this.numErrors);
        }
        this.validateTransactionPassword = options.validateTransactionPassword;
    }
    this.init();
}


IC.Public.reinvestmentPlanConfirmSmsVal.prototype = {
    onContinueClick: function (events) {
        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handleReinvestmentPlanConfirm();
            return;
        }

        if ($(this).parent().hasClass("disabled")) return;

        var formData = $('form#ReinvestmentPlanInstruction').serializeObject();
        IC.Public.security.validateTransactionPin(formData, function () { self.handleReinvestmentPlanConfirm(); }, function () { $("#btnContinue").parent().addClass('disabled'); });
    },

    handleReinvestmentPlanConfirm: function () {
        IC.Public.showLoading("");
        var url = OC.MVC.util.getLink("ReinvestmentPlans", "Complete");
        var data = { cache: $("#CacheKey").val() };
        $.ajax({
            url: url,
            data: data,
            type: "POST",
            success: function (result) {
                OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                IC.Public.hideLoading();
            }
        });
    },

    onBackClick: function () {
        var url = OC.MVC.util.getLink("ReinvestmentPlans", "Instruction");
        var data = { key: $("input[name=Key]").val(), cache: $("#CacheKey").val(), isEmployee: $('#IsEmployeePlan').val().toLowerCase() == "true" };
        
        OC.MVC.util.loadMainContainerView(url, data);
    },
    onCancelClick: function () {
        var url = OC.MVC.util.getLink("ReinvestmentPlans", "Index");
        OC.MVC.util.loadMainContainerView(url);
    },
    init: function () {
        var btnContinue = $("#btnContinue");
        btnContinue.bind("click", { obj: this }, this.onContinueClick);
        if (this.numErrors >= 1) {
            btnContinue.html("Re-submit");
        }

        if (this.numErrors > 3) {
            btnContinue.parent("span").addClass("disabled");
        }

        var btnBack = $("#btnBack");
        btnBack.bind("click", { obj: this }, this.onBackClick);

        var btnCancel = $("#btnCancel");
        btnCancel.bind("click", { obj: this }, this.onCancelClick);

        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnContinue').trigger('click');
                }
            }
        });
    }
}
;IC.Public.dividendDonations = function (options) {
    if (options) {
    }
    this.init();
};


IC.Public.dividendDonations.prototype = {
    onActionClick: function () {
        var url = OC.MVC.util.getLink("DividendDonations", "Donate");
        var data = { key: $(this).attr("key") || "", isEmployee: $(this).parent().parent().getColumnValue('IsEmployeePlan') };

        IC.Public.showLoading("");
        OC.MVC.util.loadMainContainerView(url, data);
        IC.Public.hideLoading();
    },
    onFilterGrid: function (events) {
        var _obj = events.data.obj;
        var filterData = _obj.holdingFilter.getHoldingFilterData();
        _obj.dividendDonationsGrid.appendPostData(filterData);
        IC.Public.registerGA(window.location.hash, _obj.dividendDonationsGrid.getPostData());
        _obj.dividendDonationsGrid.trigger('reloadGrid');
    },
    init: function () {
        highlightMenu('payment');
        this.dividendDonationsGrid = $("#DividendDonationsGrid");
        this.dividendDonationsGrid.bind("grid_gridComplete", { obj: this }, function (events, gridContext) {
            var _obj = events.data.obj;
            $(this).find(".dividendDonationAction").bind("click", { obj: _obj }, _obj.onActionClick);
        });

        this.holdingFilter = $("form.holdingFilterForm");
        this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterGrid);

        this.dividendDonationsGrid.unbind(OC.MVC.grid.events.loadComplete);
        this.dividendDonationsGrid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onLoadComplete);
    },
    onLoadComplete: function (events, gridObj, data) {
        var self = events.data.obj;
        self.processEPlanBalanceValues();
    },

    processEPlanBalanceValues: function () {
        //ReinvestmentPlansGrid_IsEmployeePlan
        $('#DividendDonationsGrid tr').each(function () {
            var isEmployeePlan = $(this).getColumnValue('IsEmployeePlan');
            if (isEmployeePlan != undefined && isEmployeePlan.toLowerCase() == "true") {
                var balanceTd = $(this).getColumnCell('Balance');
                balanceTd.text('');
            }
        });
    }
};IC.Public.dividendDonationsFormat = {
    Action: function (cellValue, options, rowObject) {
        var key = '';
        var index = $(this).getColIndex("ID");
        if (index >= 0) {
            key = rowObject[index];
        }
        var isEnabled = false;
        index = $(this).getColIndex("IsEnabled");
        if (index >= 0) {
            isEnabled = (rowObject[index].toLowerCase() == "true");
        }
        
        index = $(this).getColIndex("IsThirdPartyImpersonateUser");
        if (index >= 0 && isEnabled) {
            isEnabled = !(rowObject[index].toLowerCase() == "true");
        }

        var planStatusCode = "";
        index = $(this).getColIndex("ReinvestmentPlan.PlanStatus.Code");
        if (index >= 0) {
            planStatusCode = rowObject[index];
        }

        if (planStatusCode == "ACT") {
            if (isEnabled) {
                var a = $('<a>');
                a.attr("href", "javascript:void(0);");
                a.addClass("dividendDonationAction");

                if (!cellValue || cellValue.toLowerCase() === "none") {
                    a.html("New Donation");
                }
                else {
                    a.html("Edit Donation");
                }

                a.attr("key", key);

                return a.outerHTML();
            }
            else {
                //Get the disabledMessage
                var disabledMessage = IC.Public.reinvestmentPlansFormat.getDisabledMessage($(this), rowObject);
                
                //Show the above message as Tooltip
                var span = $("<span>");
                span.addClass("tooltip");
                span.attr("title", disabledMessage);
                span.html("Not Available");
                return span.outerHTML();
            }
        }
        else {
            return "Cancelled Donation";
        }
    },

    formDownload: function (cellValue, options, rowObject) {
        if (!cellValue || cellValue == "null") {
            return "";
        }
        else {
            var SecurityCode = "";
            var index = $(this).getColIndex("IssuerCode");
            if (index >= 0) {
                SecurityCode = rowObject[index];
            }

            var a = $("<a>");
            a.attr("target", "_blank");
            a.attr("href", "/ReinvestmentPlans/DownloadForm?issuerCode=" + SecurityCode + "&fileName=" + cellValue);
            a.html("download");
            return a.outerHTML();
        }
    }
};IC.Public.dividendDonationInstruction = function (options) {
    if (options) {
    }
    this.init();
}

IC.Public.dividendDonationInstruction.prototype = {

    init: function () {
        highlightMenu('payment');

        this.donationPercentage = $('#DonationPercentage');
        this.donationPercentage.addClass("numeric");
        this.donationPercentage.numeric({ format: "#,0.######", thousandsChar: ',', minValue: '1', maxValue: '100' });
       // this.donationPercentage.setNumVal(this.donationPercentage.val());
        this.donationPercentage.setNumVal("");

        var donationOption = $('input[name=EmployeePlanSecurityCode]');
        donationOption.bind('click', { obj: this }, this.onDonateOptionClick);

        var btnContinue = $("#btnContinue");
        btnContinue.bind("click", { obj: this }, this.onContinueClick);

        var termsAndConditionsAccepted = $("[name=TermsAndConditionsAccepted][type=checkbox]");
        var self = this;
        var hasUnregisteredHrnsValue = $('#HasUnregisteredHrns').val();
        this.hasRegisteredHrns = hasUnregisteredHrnsValue != undefined && hasUnregisteredHrnsValue.toLowerCase() == 'false';
        termsAndConditionsAccepted.bind("change", function () {
            var enableContinueButton = $('input:checkbox[name="TermsAndConditionsAccepted"]').is(':checked') && self.hasRegisteredHrns;
            if (enableContinueButton) {
                btnContinue.parent("span").removeClass("disabled");
            }
            else {
                btnContinue.parent("span").addClass("disabled");
            }
        });
        termsAndConditionsAccepted.change();

        var btnCancel = $("#btnCancel");
        btnCancel.bind("click", { obj: this }, this.onCancelClick);

        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnContinue').click();
                }
            }
        });
        OC.MVC.validate.validationMsgProcess();
    },

    onDonateOptionClick: function (events) {

        var donationOption=$('input[name=EmployeePlanSecurityCode]:checked').val();
        var url = OC.MVC.util.getLink("DividendDonations", "GetDonationPercentage");
        var data = { planSecurityCode: donationOption, cache: $("#CacheKey").val() };

        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: "POST",
            data: data,
            success: function (result) {
                $('#DonationPercentage')[0].value = result;
               IC.Public.hideLoading();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading("");
            }
        });
      
        
    },

    onContinueClick: function (events) {
        if ($(this).parent().hasClass("disabled")) return;
        var form = $("form#DividendDonationInstruction");
        var url = OC.MVC.util.getLink("DividendDonations", "Confirm");
        var data = form.serializeObject();
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: "POST",
            data: data,
            success: function (result) {
                OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading("");
            }
        });
    },
    
    onCancelClick: function (events) {
        var url = OC.MVC.util.getLink("DividendDonations", "Index");
        OC.MVC.util.loadMainContainerView(url);
    }
};IC.Public.dividendDonationConfirm = function (options) {
    if (options) {
        this.numErrors = options.numErrors || 0;
        if (this.numErrors == '') {
            this.numErrors = 0;
        }
        else {
            this.numErrors = parseInt(this.numErrors);
        }
        this.validateTransactionPassword = options.validateTransactionPassword;
    }
    this.init();
}

IC.Public.dividendDonationConfirm.prototype = {
    init: function () {
        var btnContinue = $("#btnContinue");
        btnContinue.bind("click", { obj: this }, this.onContinueClick);
        if (this.numErrors >= 1) {
            btnContinue.html("Re-submit");
        }
        if (this.numErrors > 3) {
            btnContinue.parent("span").addClass("disabled");
            btnContinue.unbind("click");
        }

        var btnBack = $("#btnBack");
        btnBack.bind("click", { obj: this }, this.onBackClick);

        var btnCancel = $("#btnCancel");
        btnCancel.bind("click", { obj: this }, this.onCancelClick);

        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnContinue').click();
                }
            }
        });
    },

    onContinueClick: function (events) {
        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handledividendDonationContinue();
            return false;
        }
        
        if ($(this).parent().hasClass("disabled")) return false;
        
        var isMultiMobileExist = $("#HrnsRequiringPinValidation_IsMultiMobileExist").val();
        if (isMultiMobileExist == "True") {
            self.gotoValidateSmsPin();
        } else {
            if ($('form#DividendDonationInstruction').children().length <= 4) {
                self.handledividendDonationContinue();
                return;
            }
            var formData = $('form#DividendDonationInstruction').serializeObject();
            IC.Public.security.validateTransactionPin(formData, function () { self.handledividendDonationContinue(); }, function () { $("#btnContinue").parent().addClass('disabled'); });
        }
    },

    handledividendDonationContinue: function () {
        IC.Public.showLoading("");
        var url = OC.MVC.util.getLink("DividendDonations", "Complete");
        var data = { cache: $("#CacheKey").val() };
        $.ajax({
            url: url,
            data: data,
            type: "POST",
            success: function (result) {
                OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                IC.Public.hideLoading();
            }
        });
    },
    gotoValidateSmsPin: function () {
        var url = OC.MVC.util.getLink("DividendDonations", "ConfirmValidatePin");
        var form = $("form#DividendDonationInstruction");
        var data = form.serialize();
        //data += '&cacheKey=' + $("#CacheKey").val();
        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err);
                IC.Public.hideLoading();
            }
        });
    },
    onBackClick: function () {
        var url = OC.MVC.util.getLink("DividendDonations", "Donate");
        var data = { key: $("input[name=Key]").val(), cache: $("#CacheKey").val(), isEmployee:  $('#IsEmployeePlan').val() };
        OC.MVC.util.loadMainContainerView(url, data);
    },

    onCancelClick: function () {
        var url = OC.MVC.util.getLink("DividendDonations", "Index");
        OC.MVC.util.loadMainContainerView(url);
    }
}
;IC.Public.dividendDonationConfirmSmsVal = function (options) {
    if (options) {
        this.numErrors = options.numErrors || 0;
        if (this.numErrors == '') {
            this.numErrors = 0;
        }
        else {
            this.numErrors = parseInt(this.numErrors);
        }
        this.validateTransactionPassword = options.validateTransactionPassword;
    }
    this.init();
}

IC.Public.dividendDonationConfirmSmsVal.prototype = {
    init: function () {
        var btnContinue = $("#btnContinue");
        btnContinue.bind("click", { obj: this }, this.onContinueClick);
        if (this.numErrors >= 1) {
            btnContinue.html("Re-submit");
        }
        if (this.numErrors > 3) {
            btnContinue.parent("span").addClass("disabled");
            btnContinue.unbind("click");
        }

        var btnBack = $("#btnBack");
        btnBack.bind("click", { obj: this }, this.onBackClick);

        var btnCancel = $("#btnCancel");
        btnCancel.bind("click", { obj: this }, this.onCancelClick);

        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnContinue').click();
                }
            }
        });
    },

    onContinueClick: function (events) {
        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handledividendDonationContinue();
            return false;
        }
        
        if ($(this).parent().hasClass("disabled")) return false;
        
        var formData = $('form#DividendDonationInstruction').serializeObject();
        IC.Public.security.validateTransactionPin(formData, function () { self.handledividendDonationContinue(); }, function () { $("#btnContinue").parent().addClass('disabled'); });
    },

    handledividendDonationContinue: function () {
        IC.Public.showLoading("");
        var url = OC.MVC.util.getLink("DividendDonations", "Complete");
        var data = { cache: $("#CacheKey").val() };
        $.ajax({
            url: url,
            data: data,
            type: "POST",
            success: function (result) {
                OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                IC.Public.hideLoading();
            }
        });
    },

    onBackClick: function () {
        var url = OC.MVC.util.getLink("DividendDonations", "Donate");
        var data = { key: $("input[name=Key]").val(), cache: $("#CacheKey").val(), isEmployee:  $('#IsEmployeePlan').val() };
        OC.MVC.util.loadMainContainerView(url, data);
    },

    onCancelClick: function () {
        var url = OC.MVC.util.getLink("DividendDonations", "Index");
        OC.MVC.util.loadMainContainerView(url);
    }
}
;IC.Public.dividendDonationComplete = function (options) {
    if (options) {
    }
    this.init();
}

IC.Public.dividendDonationComplete.prototype = {
    init: function () {
        var btnDone = $("#btnDone");
        btnDone.bind("click", { obj: this }, this.onDoneClick);

        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    btnDone.click();
                }   
            }
        });
    },

    onDoneClick: function () {
        var url = OC.MVC.util.getLink("DividendDonations", "Index");
        OC.MVC.util.loadMainContainerView(url);
    }
};IC.Public.dividendReinvestments = function (options) {
    if (options) {
    }
    this.init();
};

IC.Public.dividendReinvestments.prototype = {
    init: function() {
        highlightMenu('payment');
        this.dividendReinvestmentsGrid = $("#DividendReinvestmentsGrid");
        this.dividendReinvestmentsGrid.bind("grid_gridComplete", { obj: this }, function (events, gridContext) {
            var _obj = events.data.obj;
            $(this).find(".reinvestmentAction").bind("click", { obj: _obj }, _obj.onActionClick);
        });

        this.holdingFilter = $("form.holdingFilterForm");
        this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterGrid);
    },
    
    onActionClick: function () {
        var url = OC.MVC.util.getLink("DividendReinvestments", "Instruction");

        var data = { key: $(this).attr("key") || "", isEmployee: $(this).parent().parent().getColumnValue('IsEmployeePlan') };

        IC.Public.showLoading("");
        OC.MVC.util.loadMainContainerView(url, data);
        IC.Public.hideLoading();
    },
    
    onFilterGrid: function (events) {
        var _obj = events.data.obj;
        var filterData = _obj.holdingFilter.getHoldingFilterData();
        _obj.dividendReinvestmentsGrid.appendPostData(filterData);
        IC.Public.registerGA(window.location.hash, _obj.dividendReinvestmentsGrid.getPostData());
        _obj.dividendReinvestmentsGrid.trigger('reloadGrid');
    }
};;IC.Public.dividendReinvestmentInstruction = function(options) {
    if (options) {
    }
    this.init();
};

IC.Public.dividendReinvestmentInstruction.prototype = {
    PartialOption: "Partial",
    
    init: function () {
        highlightMenu('payment');

        this.registerEvents();
        
        this.partialPaticipation = $("#partialPaticipation");
        this.partialPaticipation.html($("#partialPaticipationTemplate").html());
        $("#partialPaticipationTemplate").remove();
        
        var checked = $("input[name=PlanOption]:checked");
        if (checked.length > 0) {
            $(checked).click();
        }

        this.NumberToParticipate = $("#NumberToParticipate");
        this.NumberToParticipate.addClass("numeric");
        this.assignFormat();
        this.NumberToParticipate.setNumVal(this.NumberToParticipate.val());
        
        OC.MVC.validate.validationMsgProcess();
    },
    
    registerEvents: function() {
        this.btnCancel = $("#btnCancel");
        this.btnContinue = $("#btnContinue");

        this.btnCancel.bind("click", { obj: this }, this.onCancelClick);
        this.btnContinue.bind("click", { obj: this }, this.onContinueClick);
        
        this.termsAndConditionsAccepted = $("[name=TermsAndConditionsAccepted][type=checkbox]");
        this.termsAndConditionsAccepted.bind("change", { obj: this }, this.onTermsAndConditionsAcceptedChange);
        this.termsAndConditionsAccepted.change();

        this.planOption = $("input[name=PlanOption]");
        this.planOption.bind("click", { obj: this }, this.onRadioBtnClick);
        
        this.ParticipationType = $("input[name=ParticipationType]");
        if (this.ParticipationType.length == 0) {
            this.ParticipationType = $("select[name=ParticipationType]");
            this.ParticipationType.bind("change", { obj: this }, this.onParticipationTypeSelect);
        }
        
        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnContinue').click();
                }
            }
        });
    },
    
    onRadioBtnClick: function (events) {
        var _obj = events.data.obj;

        if ($(this).val() === _obj.PartialOption) {
            _obj.partialPaticipation.show();
        }
        else {
            _obj.partialPaticipation.hide();
        }
    },
    
    onParticipationTypeSelect: function (events) {
        var _obj = events.data.obj;
        _obj.assignFormat();
        _obj.NumberToParticipate.setNumVal(0);
    },
    
    assignFormat: function () {
        if (this.ParticipationType.val() == "PER") {
            this.NumberToParticipate.numeric({ format: "#,0.######", thousandsChar: ',', minValue: '0', maxValue: '100' });
        }
        else {
            this.NumberToParticipate.numeric({ format: "#,0", thousandsChar: ',', minValue: '0' });
        }
    },
    
    onContinueClick: function (events) {
        if ($(this).parent().hasClass("disabled")) return;
        var form = $("form#DividendReinvestmentInstruction");
        var url = OC.MVC.util.getLink("DividendReinvestments", "Confirm");
        var data = form.serializeObject();
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: "POST",
            data: data,
            success: function (result) {
                OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading("");
            }
        });
    },

    onCancelClick: function (events) {
        var url = OC.MVC.util.getLink("DividendReinvestments", "Index");
        OC.MVC.util.loadMainContainerView(url);
    },
    
    onTermsAndConditionsAcceptedChange: function (events) {
        var self = events.data.obj;
        
        if ($(this).attr("checked")) {
            self.btnContinue.parent("span").removeClass("disabled");
        }
        else {
            self.btnContinue.parent("span").addClass("disabled");
        }
    }
};

;IC.Public.dividendReinvestmentPlanConfirm = function (options) {
    if (options) {
        this.numErrors = options.numErrors || 0;
        if (this.numErrors == '') {
            this.numErrors = 0;
        }
        else {
            this.numErrors = parseInt(this.numErrors);
        }
        this.validateTransactionPassword = options.validateTransactionPassword;
    }

    
    this.cacheKey = $("#CacheKey");
    this.btnContinue = $("#btnContinue");
    this.isEmployeePlan = $("#IsEmployeePlan");
    this.btnBack = $("#btnBack");
    this.btnCancel = $("#btnCancel");

    this.init();
}


IC.Public.dividendReinvestmentPlanConfirm.prototype = {

    
    init: function () {

        this.btnContinue.bind("click", {obj: this}, this.onContinueClick);
        if (this.numErrors >= 1) {
            this.btnContinue.html("Re-submit");
        }

        if (this.numErrors > 3) {
            this.btnContinue.parent("span").addClass("disabled");
            this.btnContinue.unbind("click");
        }

        this.btnBack.bind("click", { obj: this }, this.onBackClick);       
        this.btnCancel.bind("click", { obj: this }, this.onCancelClick);

        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    e.btnContinue.trigger('click');
                }
            }
        });
    },

    onContinueClick: function (events) {
        var self = events.data.obj; 
        if (!self.validateTransactionPassword) {
            self.handleDividendReinvestmentPlanConfirm(events);
            return;
        }

        if ($(this).parent().hasClass("disabled")) return;

        //var formData = $('form#DividendReinvestmentInstruction').serializeObject();
        //IC.Public.security.validateTransactionPassword(formData, function () { self.handleDividendReinvestmentPlanConfirm(events); }, function () { self.btnContinue.parent().addClass('disabled'); });
        self.gotoValidateSmsPin();
    },

    handleDividendReinvestmentPlanConfirm: function (events) {
        var obj = events.data.obj;
        IC.Public.showLoading("");
        var url = OC.MVC.util.getLink("DividendReinvestments", "Complete");
        var data = { cache: obj.cacheKey.val() };
        $.ajax({
            url: url,
            data: data,
            type: "POST",
            success: function (result) {
                OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                IC.Public.hideLoading();
            }
        });
    },
    gotoValidateSmsPin: function () {
        var url = OC.MVC.util.getLink("DividendReinvestments", "ConfirmValidatePin");
        var form = $("form#DividendReinvestmentInstruction");
        var data = form.serialize();
        //data += '&cacheKey=' + $("#CacheKey").val();
        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err);
                IC.Public.hideLoading();
            }
        });
    },
    onBackClick: function (events) {
        var obj = events.data.obj;
        var url = OC.MVC.util.getLink("DividendReinvestments", "Instruction");
        var data = { key: $("input[name=Key]").val(), cacheKey: obj.cacheKey.val(), isEmployee: obj.isEmployeePlan.val().toLowerCase() == "true" };
        
        OC.MVC.util.loadMainContainerView(url, data);
    },

    onCancelClick: function () {
        var url = OC.MVC.util.getLink("DividendReinvestments", "Index");
        OC.MVC.util.loadMainContainerView(url);
    }

};//*******************************************************************************************************
//  
//  File:        ic.public.dividendReinvestmentPlanComplete.js
//  Author:      Sriram Venkatesh
//  Description: JS for DividendReinvestmentPlanComplete.cshtml page
//  Review:      
//
//*******************************************************************************************************

IC.Public.dividendReinvestmentPlanComplete = function (options) {
    if (options) {
        this.numErrors = options.numErrors || 0;
        if (this.numErrors == '') {
            this.numErrors = 0;
        }
        else {
            this.numErrors = parseInt(this.numErrors);
        }
        
        this.btnDone = $("#btnDone");
        this.html = $("html");
    }
    this.init();
}


IC.Public.dividendReinvestmentPlanComplete.prototype = {

    init: function () {
        this.btnDone.bind("click", function () {
            var url = OC.MVC.util.getLink("ReinvestmentPlans", "Index");
            OC.MVC.util.loadMainContainerView(url);
        });
        this.html.unbind('keypress');
        this.html.bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    this.btnDone.click();
                }
            }
        });
    }
};IC.Public.ddpApplyAllList = function (options) {
    if (options) {
    }
    this.init();
};

IC.Public.ddpApplyAllList.prototype = {
    onActionClick: function () {
        var url = OC.MVC.util.getLink("DdpApplyAll", "Donate");
        var data = { key: $(this).attr("key") || "", isEmployee: $(this).parent().parent().getColumnValue('IsEmployeePlan') };

        IC.Public.showLoading("");
        OC.MVC.util.loadMainContainerView(url, data);
        IC.Public.hideLoading();
    },
    onFilterGrid: function (events) {
        var _obj = events.data.obj;
        var filterData = _obj.holdingFilter.getHoldingFilterData();
        _obj.dividendDonationsGrid.appendPostData(filterData);
        IC.Public.registerGA(window.location.hash, _obj.dividendDonationsGrid.getPostData());
        _obj.dividendDonationsGrid.trigger('reloadGrid');
    },
    init: function () {
        highlightMenu('payment');
        this.dividendDonationsGrid = $("#DividendDonationsGrid");
        this.dividendDonationsGrid.bind("grid_gridComplete", { obj: this }, function (events, gridContext) {
            var _obj = events.data.obj;
            $(this).find(".dividendDonationAction").bind("click", { obj: _obj }, _obj.onActionClick);
        });

        this.holdingFilter = $("form.holdingFilterForm");
        this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterGrid);
    }
};IC.Public.ddpApplyAllInstruction = function (options) {
    if (options) {
    }
    this.init();
}

IC.Public.ddpApplyAllInstruction.prototype = {

    init: function () {
        highlightMenu('payment');

        this.donationPercentage = $('#DonationPercentage');
        this.donationPercentage.addClass("numeric");
        this.donationPercentage.numeric({ format: "#,0.######", thousandsChar: ',', minValue: '1', maxValue: '100' });
        this.donationPercentage.setNumVal(this.donationPercentage.val());

        var btnContinue = $("#btnContinue");
        btnContinue.bind("click", { obj: this }, this.onContinueClick);

        var termsAndConditionsAccepted = $("[name=TermsAndConditionsAccepted][type=checkbox]");
        termsAndConditionsAccepted.bind("change", function () {
            if ($(this).attr("checked")) {
                btnContinue.parent("span").removeClass("disabled");
            }
            else {
                btnContinue.parent("span").addClass("disabled");
            }
        });
        termsAndConditionsAccepted.change();

        var btnCancel = $("#btnCancel");
        btnCancel.bind("click", { obj: this }, this.onCancelClick);

        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnContinue').click();
                }
            }
        });
        OC.MVC.validate.validationMsgProcess();
    },

    onContinueClick: function (events) {
        if ($(this).parent().hasClass("disabled")) return;
        var form = $("form#DividendDonationInstruction");
        var url = OC.MVC.util.getLink("DdpApplyAll", "Confirm");
        var data = form.serializeObject();
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: "POST",
            data: data,
            success: function (result) {
                OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading("");
            }
        });
    },

    onCancelClick: function (events) {
        var url = OC.MVC.util.getLink("DdpApplyAll", "Index");
        OC.MVC.util.loadMainContainerView(url);
    }
};IC.Public.ddpApplyAllConfirm = function (options) {
    if (options) {
        this.numErrors = options.numErrors || 0;
        if (this.numErrors == '') {
            this.numErrors = 0;
        }
        else {
            this.numErrors = parseInt(this.numErrors);
        }
        this.validateTransactionPassword = options.validateTransactionPassword;
    }
    this.init();
}

IC.Public.ddpApplyAllConfirm.prototype = {
    init: function () {
        var btnContinue = $("#btnContinue");
        btnContinue.bind("click", { obj: this }, this.onContinueClick);
        if (this.numErrors >= 1) {
            btnContinue.html("Re-submit");
        }
        if (this.numErrors > 3) {
            btnContinue.parent("span").addClass("disabled");
            btnContinue.unbind("click");
        }

        var btnBack = $("#btnBack");
        btnBack.bind("click", { obj: this }, this.onBackClick);

        var btnCancel = $("#btnCancel");
        btnCancel.bind("click", { obj: this }, this.onCancelClick);

        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnContinue').click();
                }
            }
        });
    },

    onContinueClick: function (events) {
        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handledividendDonationContinue();
            return false;
        }

        if ($(this).parent().hasClass("disabled")) return false;

        var formData = $('form#DividendDonationInstruction').serializeObject();
        IC.Public.security.validateTransactionPassword(formData, function () { self.handledividendDonationContinue(); }, function () { $("#btnContinue").parent().addClass('disabled'); });
    },

    handledividendDonationContinue: function () {
        IC.Public.showLoading("");
        var url = OC.MVC.util.getLink("DdpApplyAll", "Complete");
        var data = { cache: $("#CacheKey").val() };
        $.ajax({
            url: url,
            data: data,
            type: "POST",
            success: function (result) {
                OC.MVC.util.loadHtml(OC.MVC.constants.mainContainer, result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                IC.Public.hideLoading();
            }
        });
    },

    onBackClick: function () {
        var url = OC.MVC.util.getLink("DdpApplyAll", "Donate");
        var data = { key: $("input[name=Key]").val(), cache: $("#CacheKey").val(), isEmployee: $('#IsEmployeePlan').val() };
        OC.MVC.util.loadMainContainerView(url, data);
    },

    onCancelClick: function () {
        var url = OC.MVC.util.getLink("DdpApplyAll", "Index");
        OC.MVC.util.loadMainContainerView(url);
    }
};IC.Public.ddpApplyAllComplete = function (options) {
	if (options) {
	}
	this.init();
}

IC.Public.ddpApplyAllComplete.prototype = {
	init: function () {
		var btnDone = $("#btnDone");
		btnDone.bind("click", { obj: this }, this.onDoneClick);

		$('html').unbind('keypress');
		$('html').bind('keypress', function (e) {
		    if (e.keyCode == 13) {
		        if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
		            //Do Nothing
		        } else {
		            btnDone.click();
		        }
			}
		});
	},

	onDoneClick: function () {
		var url = OC.MVC.util.getLink("DdpApplyAll", "Index");
		OC.MVC.util.loadMainContainerView(url);
	}
};IC.Public.newInvestor = function(options) {
    if (options) {
    }
}

IC.Public.newInvestor.prototype = {
    onCommunicationOptionUpdateClick: function() {
        var url = OC.MVC.util.getLink("CommunicationsPreferences", "Preferences");
        OC.MVC.util.loadView("newInvestorContent", url);
    }, 
    onCommunicationOptionEmailUpdateClick: function() {
        var url = OC.MVC.util.getLink("CommunicationsPreferences", "Preferences");
        OC.MVC.util.loadMainContainerView(url);
    },
    onPaymentInstructionsUpdateClick: function() {
        var url = OC.MVC.util.getLink("UpdatePaymentInstructions", "Index");
        OC.MVC.util.loadMainContainerView(url);
    },
    onTaxDetailsUpdateClick: function() {
        var url = OC.MVC.util.getLink("UpdateTaxDetails", "GetTaxDetails");
        OC.MVC.util.loadMainContainerView(url);
    },
    onSkipClick: function(events) {
        events.data.obj.onContinueNavigation(events);
    },
    onDoneClick: function(events) {
        events.data.obj.onContinueNavigation(events);
    },
    onContinueNavigation: function(events) {
        var url = OC.MVC.util.getLink("NewInvestor", "Skip");
        var data = events.data.obj.form.serialize();
        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: data,
            success: function(result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function(err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    init: function() {
        this.isLoading = true;
        highlightMenu('home');
        this.form = $("form#newInvestor");

        $("#btnCommunicationOptionUpdate").bind("click", { obj: this }, this.onCommunicationOptionUpdateClick);
        $("#btnCommunicationOptionEmailUpdate").bind("click", { obj: this }, this.onCommunicationOptionEmailUpdateClick);
        $("#btnPaymentInstructionsUpdate").bind("click", { obj: this }, this.onPaymentInstructionsUpdateClick);
        $("#btnTaxDetailsUpdate").bind("click", { obj: this }, this.onTaxDetailsUpdateClick);
        $("#btnSkip").bind("click", { obj: this }, this.onSkipClick);
        $("#btnDone").bind("click", { obj: this }, this.onDoneClick);

        this.isLoading = false;
    }
}
;IC.Public.linkAccounts = function(options) {
}

IC.Public.linkAccounts.prototype = {

    onContinueClick: function(events) {
        var _obj = events.data.obj;
        var url = OC.MVC.util.getLink("OpenAccess", "UserRegistrationComplete");
        var data = { cacheKey: $("#CacheKey").val(), isElectronicSelected: $("#IsElectronicSelected").is(':checked') };

        //IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: data,
            success: function(result) {
                //OC.MVC.util.loadView(OC.MVC.constants.mainContainer, OC.MVC.util.getLink("Holdings", "Index"));
                OC.MVC.history.register(OC.MVC.constants.mainContainer, OC.MVC.util.getLink("Holdings", "Index"));
                window.location = '/';
                //IC.Public.hideLoading();
            },
            error: function(err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },

    init: function() {
        highlightMenu('home');

        this.btnContinue = $("#btnContinue");
        this.btnContinue.button();
        this.btnContinue.bind("click", { obj: this }, this.onContinueClick);
        this.btnContinue.focus();
    }
}
;IC.Public.userValidation = function(options) {
}

IC.Public.userValidation.prototype = {

    onContinueClick: function(events) {
        IC.Public.showLoading("");
    },

    init: function() {
        this.btnContinue = $("#btnContinue");
        this.btnContinue.bind("click", { obj: this }, this.onContinueClick);
    }
};IC.Public.singleHoldingReg = function () {
}

IC.Public.singleHoldingReg.prototype = {
    
    onRegisterClick: function (e) {
        var cacheKey = $("#CacheKey").val();
        IC.Public.showLoading("");
        $.ajax({
            url: OC.MVC.util.getLink("OpenAccess", "SingleHoldingUserRegistration"), type: 'POST',
            data: { cacheKey: cacheKey },
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                ("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        IC.Public.hideLoading();
    },
    init: function () {
        IC.Public.HideMenu();
        this.btnRegister = $("#btnRegister");
        this.btnRegister.bind("click", { obj: this }, this.onRegisterClick);
    }
};IC.Public.customLogin = function(options) {
    this.btnLogin = $("#btnLogin");
    this.customLoginForm = $("#customLoginForm");
    this.init();
}

IC.Public.customLogin.prototype = {
    onLoginClick: function (events) {

        var _obj = events.data.obj;
        var form = $("form:first");
        var data = _obj.customLoginForm.serializeObject();
        if (typeof data.Country == "object" && data.Country.length > 0) {
            data.Country = "" + data.Country[0];
        }
        form.submit();
        //        $.ajax({
        //            url: OC.MVC.util.getLink("CustomLogin", "Index"),
        //            data: data,
        //            type: 'POST',
        //            success: function (result) {
        //                var errorContainer = _obj.customLoginForm.find("#mainErrorDiv");
        //                errorContainer.find("ol li").remove();
        //                _obj.customLoginForm.find(".error").removeClass("error");
        //                if (result.Status == "SUCCESS") {
        //                    var url = "/CustomLogin/Success/" + result.Response.actionUrl; //result.Response.pageCode + (OC.MVC.constants.iis6 ? '.aspx' : '');
        //                    window.location = url;
        //                }
        //                else if (result.Status == "INVALID") {
        //                    if (result.Response && result.Response.length == 0) {
        //                        result.Response.push({ Key: "MemberNumber", Value: "Failed to login" })
        //                    }
        //                    IC.Public.validation.showValidationMessages(_obj.customLoginForm, result.Response);
        //                    RegistrationCaptcha.ReloadImage();
        //                }
        //                else {
        //                    IC.Public.validation.showError(_obj.customLoginForm, result.ErrorMessage);
        //                    RegistrationCaptcha.ReloadImage();
        //                }
        //            },
        //            error: function (err) {
        //                alert('error');
        //            }
        //        });
    },
    onMemberNumberBlur: function (e) {
        var memberNumberControl = $(e.target);
        var memberNumberControlValue = memberNumberControl.val();
        var issuerCodeControl = $('#IssuerCode');

        if (issuerCodeControl != null 
                &&  issuerCodeControl.val() != null
                && (issuerCodeControl.val().toUpperCase() == 'ICAA' || issuerCodeControl.val().toUpperCase() == 'HCF' || issuerCodeControl.val().toUpperCase() == 'RACP')
                && memberNumberControlValue != '' 
                && memberNumberControlValue.match('^[0-9]')) {
            memberNumberControl.val('I' + memberNumberControlValue);
        }
    },
    init: function () {
        this.btnLogin.unbind("click");
        this.btnLogin.bind("click", { obj: this }, this.onLoginClick);
        $('#MemberNumber').blur(this.onMemberNumberBlur);
    }
};IC.Public.identifyLogin = function (options) {    
}

IC.Public.identifyLogin.prototype = {
       
    init: function () {
        this.lnkForgottenPIN = $("#lnkLoginForgottenPIN");
        this.lnkForgottenPIN.click({ obj: this }, this.onForgottenPINLinkClick);
        if ($('#SingleHolding_IsCaptchaRequired').val() == 'True') {
            $('#labelSecurityCheckSH').text($('#BUYBACK_LOGIN_SINGLEHOLDINGSECURITYCODE').val());
            $('#labelSecurityCodeSH').text($('#BUYBACK_LOGIN_SINGLEHOLDINGTYPECODE').val());
        }
        if ($('#EmployeeSingleHolding_IsCaptchaRequired').val() == 'True') {
            $('#labelSecurityCheckESH').text($('#BUYBACK_LOGIN_EMPLOYEESINGLEHOLDINGSECURITYCODE').val());
            $('#labelSecurityCodeESH').text($('#BUYBACK_LOGIN_EMPLOYEESINGLEHOLDINGTYPECODE').val());
        }
    },

    onForgottenPINLinkClick: function () {
        var issuerName = $("#EmployeeSingleHolding_IssuerName").val();
        var issuerCode = $("#EmployeeSingleHolding_IssuerCode").val();
        var url = OC.MVC.util.getLink("OpenAccess", "ForgotPinRequest");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url, { companyName: issuerName, companyCode: issuerCode, fromPageName: "IdentifyHolding", fromPageURL: window.location.toString() });
        return false;
    }
};IC.Public.Plans.ExerciseTypes =
{
    CashExercise: "CASH",
    CashExerciseDirectCredit: "CASH_Direct_Credit",
    SameDaySale: "SDS",
    SellToCover: "STC",
    WithholdShares: "WTH",
    StockSwap: "SWAP",
    ExerciseAwards: "EAW"
};

IC.Public.Plans.ShareDeliveryMethods = {
    ManageShares: "MS",
    TransferToSRN: "TSRN"
};

IC.Public.Plans.SaleMethods = {
    SellByMarketOrder: "MKT",
    SellByLimit: "LIM",
    GoodTillCancel: "GTC"
};


IC.Public.Plans.ExerciseCustomFormatters =
{
    formatGrantDate: function (cellValue, options, rowObject) {
        //If grant description exists, display it as tooltip
        
        var uniqueId = IC.Public.gridFormatter.getCellValue($(this), "GrantId", rowObject);
        var grantDescription = IC.Public.gridFormatter.getCellValue($(this), "GrantDescription", rowObject);
        if (grantDescription == undefined || grantDescription.trim().length == 0)
            return cellValue;
        
        var clueToolTip = $('<div>');

        var a = $('<a>');
        a.attr("href", "#");
        a.attr("rel", ".tooltip" + uniqueId);
        a.attr("class", "grantDateTooltip cluetooltip");
        a.html(cellValue);
        a.appendTo(clueToolTip);
        
        var toolTipContent = $('<div>');
        toolTipContent.attr("class", "tooltip" + uniqueId);
        var tooltipTitle = "<p class='grantDetailsTooltipTitle'>Grant Description</p>";
        toolTipContent.html(tooltipTitle);
        var tooltipMessage = "<p>" + grantDescription + "</p>";
        toolTipContent.html(toolTipContent.html() + tooltipMessage);

        toolTipContent.appendTo(clueToolTip);
        return clueToolTip.html();
    },
    
    planNameTooltip: function (cellValue, options, rowObject) {
        var div = $('<div>');
        var span = $('<span>');
        var index = $(this).getColIndex("PlanName");
        if (index < 0)
            index = $(this).getColIndex("Name");

        if (index >= 0) {
            span.attr("title", rowObject[index].replace('&amp;', '&').replace('&apos;', '\'').replace('&gt;', '>').replace('&lt;', '<'));
        }
        span.addClass("tooltip");
        span.html(cellValue);
        span.appendTo(div);
        return div.html();
    }
};IC.Public.Plans.exerciseOption = {    
    awards: 1,
    transactions: 2
};

IC.Public.Plans.exercise = function() {
    this.init();
};

IC.Public.Plans.exercise.prototype = {
    init: function () {
        this.action = $("[name=Action]");
        this.action.bind('click', { obj: this }, this.onActionClick);
        this.viewKey = $("#ViewKey");

        //ViewBar change event
        var self = this;
        var holdingFilter = $("form.holdingFilterForm");
        if (holdingFilter.length) {
            holdingFilter.unbind("applyHoldingFilter");
            holdingFilter.bind("applyHoldingFilter", { obj: self }, self.onFilterClick);
        }

        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.Exercise);
    },

    onFilterClick: function (events) {
        var url = OC.MVC.util.getLink("Exercise", "Exercise");
        OC.MVC.util.loadMainContainerView(url, null);
    },

    onActionClick: function (events) {
        var _obj = events.data.obj;
        var value = $(this).val();
        var url = IC.Public.urls.Exercise.awardsList;
        if (value == IC.Public.Plans.exerciseOption.transactions)
            url = IC.Public.urls.Exercise.transactionsList;
        OC.MVC.util.loadView("grid-container", url, { viewKey: _obj.viewKey.val() });
    }
};IC.Public.Plans.awardsList = function() {
    this.init();
};

IC.Public.Plans.awardsList.prototype = {
    init: function() {
        this.grid = $("#ExerciseAwardsGrid");
        this.grid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onLoadComplete);
        this.btnExercise = $("#btnExercise");
        this.btnExercise.bind('click', { obj: this }, this.onExerciseClick);
    },
    
    onLoadComplete: function (events) {
        var _obj = events.data.obj;
        $('.cluetooltip').bind('click', _obj.onLinkClick);
        $('.select-checkbox').bind('click', { obj: _obj}, _obj.onSelectClick);
    },
    
    onLinkClick: function () {
        var type = $(this).attr("type");
        var code = $(this).attr("code");
        var viewKey = $("#ViewKey").val();
        var transactionType = $(this).attr("transactionType");
        var windowId = $(this).attr("windowId");
        
        var modal = new IC.Public.Plans.message();
        modal.showModal(type, code, viewKey, transactionType, windowId, IC.Public.Plans.Common.MenuSubItemTypes.Exercise);
    },
    
    getSelectedPlans: function() {
        var plans = $('.select-checkbox:checked').map(function () {
            return $(this).attr('code');
        }).get();
        return plans;
    },

    onSelectClick: function (events) {
        var _obj = events.data.obj;
        var plans = _obj.getSelectedPlans();
        
        if (plans.length > 0) {
            _obj.btnExercise.parent().removeClass('disabled');
        } else {
            _obj.btnExercise.parent().addClass('disabled');
        }
        if (IC.Public.IsThirdPartyImpersonateUser())
            _obj.btnExercise.parent().addClass('disabled');
    },
    
    onExerciseClick: function(events) {
        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        var viewKey = $('#ViewKey').val();
        var plans = _obj.getSelectedPlans().join(',');
        var url = IC.Public.urls.Exercise.details;
        OC.MVC.util.loadMainContainerView(url, { viewKey: viewKey, plans: plans });
    }
};
;/// <reference path="../../Holdings/ic.public.transactionGrid.js" />
IC.Public.Plans.confirmExercise = function (validateTransactionPassword) {
    this.init(validateTransactionPassword);
};

IC.Public.Exercise.processRestrictedGrantDetails = function() {
    var restrictedExerciseOptionsTotal = 0;
    $('span.restricted').each(function() {
        var numberToExercise = parseInt($(this).text());
        numberToExercise = isNaN(numberToExercise) ? 0 : numberToExercise;
        restrictedExerciseOptionsTotal += numberToExercise;
    });

    if (restrictedExerciseOptionsTotal != 0) {
        $('#restrictedSharesToDeliverDiv').show();
        $('#restrictedSharesToDeliverSpan').text(restrictedExerciseOptionsTotal);
        $('#restrictedShareDeliveryDiv').show();
    }
};

IC.Public.Plans.confirmExercise.prototype = {
    init: function (validateTransactionPassword) {
        this.validateTransactionPassword = validateTransactionPassword;
        this.confirmExerciseForm = $('form#confirmExerciseForm');

        this.grid = $("#ExerciseDetailsGrid");
        this.grid.unbind(OC.MVC.grid.events.loadComplete);
        this.grid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onLoadComplete);

        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind('click');
        this.btnCancel.bind("click", { obj: this }, this.onCancelButtonClick);

        this.btnBackButton = $('#btnBack');
        this.btnBackButton.unbind('click');
        this.btnBackButton.bind("click", { obj: this }, this.onBackButtonClick);

        this.btnConfirmButton = $('#btnConfirm');
        this.btnConfirmButton.unbind('click');
        this.btnConfirmButton.bind("click", { obj: this }, this.onConfirmButtonClick);

        this.shareDeliveryMethod = $('#ShareDeliveryMethod');
        this.transferTermsAndConditions = $('.transferTermsAndConditions');
        this.displayTermsAndConditions();
        
        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.Exercise);
    },

    onLoadComplete: function (events, gridObj, data) {
        IC.Public.Exercise.processRestrictedGrantDetails();
        
        $("A.cluetooltip").each(function () {
            $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 150 });
        });
    },
    
    onCancelButtonClick: function() {
        var url = OC.MVC.util.getLink("Exercise", "Details");
        OC.MVC.util.loadMainContainerView(url, { viewKey: $('#ViewKey').val(), plans: $('#hdnPlans').val() });
    },

    onBackButtonClick: function (events) {
        var self = events.data.obj;
        var formData = self.confirmExerciseForm.serializeObject();
        var modelData = {
            ViewKey: formData.ViewKey,
            SelectedPlanCodes: formData.hdnPlans,
            SelectedGrants: formData.hdnSelectedGrants,
            ExerciseMethod: formData.ExerciseMethods,
            SalesMethod: formData.SalesMethod,
            LimitPrice: formData.hdnLimitPrice,
            PaymentMethod: formData.PaymentMethod,
            ShareDeliveryMethod: formData.ShareDeliveryMethod,
            Srn: formData.Srn,
            IsExerciseAll: formData.IsExerciseAll,
            IsExerciseNumber: formData.IsExerciseNumber,
            ExerciseNumber: formData.ExerciseNumber
        };

        OC.MVC.util.loadMainContainerView(IC.Public.urls.Exercise.detailsWithValues, modelData);
    },

    onConfirmButtonClick: function (events) {
        if ($('#IsImpersonateUser').val().toLowerCase() == "true") {
            alert(IC.Public.Plans.Common.ImperonateUserNoAccessMessage);
            return;
        }
        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handleExerciseConfirmClick();
            return;
        }

        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        var formData = self.confirmExerciseForm.serializeObject();
        IC.Public.security.validateTransactionPin(formData, function () { self.handleExerciseConfirmClick(); }, function () { self.disableExerciseButtons(); });
    },

    handleExerciseConfirmClick: function () {
        var data = this.confirmExerciseForm.serializeObject();

        IC.Public.showLoading("");
        $.ajax({
            url: IC.Public.urls.Exercise.confirmExercise,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },

    disableExerciseButtons: function () {
        this.btnConfirmButton.parent().addClass('disabled');
    },
    
    displayTermsAndConditions: function () {
        var shareDeliveryMethod = this.shareDeliveryMethod.val();
        if (shareDeliveryMethod == IC.Public.Plans.ShareDeliveryMethods.TransferToSRN) {
            this.transferTermsAndConditions.show();
        } else {
            this.transferTermsAndConditions.hide();
        }
    }
};
;IC.Public.Plans.exerciseDetails = function (validateTransactionPassword) {
    this.init(validateTransactionPassword);
};

IC.Public.Plans.exerciseDetails.prototype = {
    init: function (validateTransactionPassword) {
        var self = this;
        this.blockExerciseMethodProcessing = false;
        this.validateTransactionPassword = validateTransactionPassword;
        this.SalesMethod = $("[name='SalesMethod']");
        this.EstimatedSaleValue = $("#EstimatedSaleValue");

        this.registerEvents(self);
        IC.Public.updateCampaignsPanel('ExerciseDetails');

        this.salesTermsAndConditions = $('.salesTermsAndConditions');
        this.transferTermsAndConditions = $('.transferTermsAndConditions');
        this.generalTermsAndConditions = $('.generalTermsAndConditions');
        this.displayTermsAndConditions();

        this.errorContainer = $('.errorContainer');
        if (this.errorContainer.html() == '')
            this.errorContainer.hide();

        if ($('#exerciseMethodsList').children('li').length == 1) {
            var exerciseMethodRadio = $('#exerciseMethodsList').find("input[type='radio']");
            if (!exerciseMethodRadio.attr('checked')) {
                exerciseMethodRadio.attr('checked', true);
                exerciseMethodRadio.trigger('click');
            }
        }

        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.Exercise);
    },

    registerEvents: function (self) {
        this.registerGridRelatedControlEvents();
        this.registerExerciseMethodEvents(self);
        this.registerButtonEvents();

        this.TermsAndConditionsAccepted = $("#TermsAndConditionsAccepted");
        this.TermsAndConditionsAccepted.change(function () {
            self.validateData();
        });
    },

    registerGridRelatedControlEvents: function () {
        this.exerciseAllCheckBox = $("#IsExerciseAll");
        this.exerciseAllCheckBox.unbind('click');
        this.exerciseAllCheckBox.bind('click', { obj: this }, this.onExerciseAllClick);

        this.exerciseNumberCheckBox = $("#IsExerciseNumber");
        this.exerciseNumberCheckBox.unbind('click');
        this.exerciseNumberCheckBox.bind('click', { obj: this }, this.onExerciseNumberClick);

        this.sellNumberTextBox = $("#ExerciseNumber");
        this.sellNumberTextBox.unbind('blur');
        this.sellNumberTextBox.bind('blur', { obj: this }, this.onExerciseNumberBlur);
        this.sellNumberTextBox.numeric();

        this.TotalAvailable = $("#TotalAvailable");
        this.TotalSelected = $("#TotalSelected");
        this.TotalExercisePrice = $("#TotalExercisePrice");

        this.grid = $("#ExerciseDetailsGrid");
        this.grid.unbind(OC.MVC.grid.events.loadComplete);
        this.grid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onLoadComplete);
    },

    registerExerciseMethodEvents: function (self) {
        this.ExerciseMethods = $("[name='ExerciseMethods']");
        this.ExerciseMethods.unbind('click');
        this.ExerciseMethods.bind("click", { obj: this }, this.onMethodSelect);

        var availableFor = ".AvailableFor_" + this.ExerciseMethods.val();
        $(availableFor).show();

        this.methodDetailItems = $('.method-detail-items').find("input[type='radio']");
        this.methodDetailItems.unbind('click');
        this.methodDetailItems.bind('click', { obj: this }, this.onMethodDetailsClick);

        this.SellByLimitPrice = $("[name='SellByLimitPrice']");
        this.SellByLimitPrice.numeric({ format: "#0.00" });
        this.SellByLimitPrice.unbind('keyup');
        this.SellByLimitPrice.bind('keyup', function () {
            self.updateEstimatedValues();
            self.validateData();
        });

        this.GoodTillCancelPrice = $("[name='GoodTillCancelPrice']");
        this.GoodTillCancelPrice.numeric({ format: "#0.00" });
        this.GoodTillCancelPrice.unbind('keyup');
        this.GoodTillCancelPrice.bind('keyup', function () {
            self.updateEstimatedValues();
            self.validateData();
        });

        this.transferToSrnTextBox = $("[name='UserSRN']");
        this.transferToSrnTextBox.unbind('keyup');
        this.transferToSrnTextBox.bind('keyup', function () {
            self.validateData();
        });

        this.updateBankAccountLink = $('.update-bank-account');
        this.updateBankAccountLink.unbind('click');
        this.updateBankAccountLink.bind("click", { obj: this }, this.onUpdateBankAccountLinkClick);

        this.updateIbwAccountLink = $('.ibw-update-account');
        this.updateIbwAccountLink.unbind('click');
        this.updateIbwAccountLink.bind("click", { obj: this }, this.onUpdateIbwAccountLinkClick);

        this.updateAddressLink = $('.update-address');
        this.updateAddressLink.unbind('click');
        this.updateAddressLink.bind("click", { obj: this }, this.onUpdateAddressLinkClick);

        this.exerciseMessageLink = $('.exerciseMessageLink');
        this.exerciseMessageLink.unbind('click');
        this.exerciseMessageLink.bind('click', this.onExerciseMessageLinkClick);
    },

    registerButtonEvents: function () {
        this.btnContinue = $('#btnContinue');
        this.btnContinue.unbind('click');
        this.btnContinue.bind("click", { obj: this }, this.onContinueButtonClick);

        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind('click');
        this.btnCancel.bind('click', function () {
            var url = OC.MVC.util.getLink("Exercise", "Exercise");
            OC.MVC.util.loadMainContainerView(url);
        });
    },

    onExerciseAllClick: function (events) {
        var _obj = events.data.obj;
        var value = $(this).is(':checked');
        _obj.toggleOptionsToTransactValues(value);
        _obj.toggleSellNumberInputs(!value);

        if (!_obj.blockExerciseMethodProcessing)
            _obj.processExerciseMethods();
    },

    onExerciseNumberClick: function (events) {
        var _obj = events.data.obj;
        var value = $(this).is(':checked');
        _obj.toggleSellAllInputs(_obj, !value);
        _obj.toggleSellNumberInputs(value);
        $("#ExerciseNumber").focus();
    },

    onExerciseNumberBlur: function (events) {
        var sellNumberText = $(this).val().replaceAll(",", "");
        if (sellNumberText == "")
            return;
        var exerciseNumber = parseInt(sellNumberText);
        if (isNaN(exerciseNumber))
            return;

        var _obj = events.data.obj;
        _obj.distributeSellNumber(exerciseNumber);

        if (!_obj.blockExerciseMethodProcessing)
            _obj.processExerciseMethods();
    },

    toggleSellAllInputs: function (obj, value) {
        //$("#IsExerciseAll").attr('checked', value);
        $("#IsExerciseAll").prop('checked', value);
        obj.toggleOptionsToTransactValues(value);
    },

    toggleSellNumberInputs: function (value) {
        //$("#IsExerciseNumber").attr('checked', value);
        //$("#ExerciseNumber").attr('disabled', !value);
        $("#IsExerciseNumber").prop('checked', value);
        $("#ExerciseNumber").prop('disabled', !value);
        if (!value) {
            //$("#ExerciseNumber").val('');
            $("#ExerciseNumber").prop('value','');
        }
    },

    toggleOptionsToTransactValues: function (value) {
        var self = this;
        $.each($('.numberToExercise'), function (i, item) {
            var tr = $(item).parents('tr');
            var exercisable = parseInt(tr.getColumnValue('Exercisable').replaceAll(",", ""));
            var numberToExerciseTextBox = $(item);
            if (value == true) {
                numberToExerciseTextBox.val(exercisable);
            } else {
                numberToExerciseTextBox.val('');
            }
            //numberToExerciseTextBox.trigger('keyup');
            self.handleGridExerciseNumberChange(numberToExerciseTextBox, false);
        });
    },

    distributeSellNumber: function (exerciseNumber) {
        var self = this;
        $.each($('.numberToExercise'), function (i, item) {
            if (exerciseNumber <= 0)
                return;

            var tr = $(item).parents('tr');
            var exercisable = parseInt(tr.getColumnValue('Exercisable').replaceAll(",", ""));
            var numberToExerciseTextBox = $(item);
            if (exerciseNumber <= exercisable) {
                numberToExerciseTextBox.val(exerciseNumber);
            } else {
                numberToExerciseTextBox.val(exercisable);
            }
            //numberToExerciseTextBox.trigger('keyup');
            self.handleGridExerciseNumberChange(numberToExerciseTextBox, false);
            exerciseNumber -= exercisable;
        });
    },

    onLoadComplete: function (events, gridObj, data) {
        var _obj = events.data.obj;
        if (data && data.userdata)
            _obj.TotalAvailable.text($.formatNumber(data.userdata.TotalAvailable, "#,0"));
        var numberToExercise = $('.numberToExercise');
        numberToExercise.numeric();
        numberToExercise.unbind('keyup');
        numberToExercise.bind('keyup', { obj: _obj }, _obj.onNumberChange);
        var warnings = $('div.underwater-warning');
        var warningTextContainer = $('div.underwater-warning-container');
        if (warnings.length > 0)
            warningTextContainer.show();
        else
            warningTextContainer.hide();
        numberToExercise.each(function () {
            var hasValue = ($(this).val() == "");
            _obj.blockExerciseMethodProcessing = hasValue;
            $(this).trigger('keyup');
            _obj.blockExerciseMethodProcessing = false;
        });
        var restrictionsLinks = $('.restrictions-link');
        restrictionsLinks.unbind('click');
        restrictionsLinks.bind('click', { obj: _obj }, _obj.onRestrictionsClick);

        $("A.grantDateTooltip").each(function () {
            $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 150 });
        });
    },

    onRestrictionsClick: function (events) {
        var _obj = events.data.obj;
        var data = { PlanViewKey: $(this).attr('key') };
        OC.MVC.util.showModalByUrl(IC.Public.urls.PlanDetails.vestingGrid, data, { minHeight: 300, minWidth: 700 });
    },

    onNumberChange: function (events) {
        var self = events.data.obj;
        self.handleGridExerciseNumberChange($(this), true);
        if (!self.blockExerciseMethodProcessing)
            self.processExerciseMethods();
    },

    handleGridExerciseNumberChange: function (exerciseNumberControl, unCheckExerciseNumberChecboxes) {
        var self = this;
        var tr = exerciseNumberControl.parents('tr');
        var td = tr.getColumnCell('TotalExercisePrice');
        var value = exerciseNumberControl.val();
        var totalEl = td.find('.total');

        if (value) {
            var exercisePrice = tr.getColumnValue('ExercisePrice');
            var _exercisePrice = parseFloat(exercisePrice);
            var _number = parseInt(exerciseNumberControl.val());

            if (!isNaN(_exercisePrice) && !isNaN(_number)) {
                var exercisable = parseInt(tr.getColumnValue('Exercisable').replaceAll(",", ""));
                if (exercisable < _number) {
                    alert('Number to Exercise must be less or equal to Exercisable options');
                    exerciseNumberControl.val('');
                    self.processExerciseNumberCheckBoxes(unCheckExerciseNumberChecboxes);
                    return;
                }

                var total = _exercisePrice * exerciseNumberControl.val();
                totalEl.show();
                td.find('.total-price').text($.formatNumber(total, "#,0.00"));

                self.processExerciseNumberCheckBoxes(unCheckExerciseNumberChecboxes);
            }
        } else {
            totalEl.hide();
            td.find('.total-price').text('');
        }
        self.updateTotals();
    },

    processExerciseNumberCheckBoxes: function (uncheck) {
        if (uncheck) {
            $("#IsExerciseAll").attr('checked', false);
            $("#IsExerciseNumber").attr('checked', false);
            $("#ExerciseNumber").val('');
        }
    },

    updateTotals: function () {
        var _numberToExercise = $('.numberToExercise').map(function () {
            var _value = parseInt($(this).val());
            return isNaN(_value) ? 0 : _value;
        }).get();

        this.TotalSelected.text($.formatNumber(_numberToExercise.sum(), "#,0"));

        var _totalExercisePrice = $('.total-price').map(function () {
            var _value = parseFloat($(this).text().replaceAll(',', ''));
            return isNaN(_value) ? 0 : _value;
        }).get();
        this.TotalExercisePrice.text($.formatNumber(_totalExercisePrice.sum(), "#,0.00"));
        this.updateEstimatedValues();
        this.validateData();
    },

    processExerciseMethods: function () {
        var totalSelected = $('#TotalSelected').text();
        if (totalSelected == "" || totalSelected == "0") {
            $('#exerciseMethodLi' + IC.Public.Plans.ExerciseTypes.ExerciseAwards).show();
            $('#exerciseMethodLi' + IC.Public.Plans.ExerciseTypes.CashExercise).show();
            $('#exerciseMethodLi' + IC.Public.Plans.ExerciseTypes.CashExerciseDirectCredit).show();
            $('#exerciseMethodLi' + IC.Public.Plans.ExerciseTypes.SellToCover).show();
            $('#exerciseMethodLi' + IC.Public.Plans.ExerciseTypes.SameDaySale).show();
            this.validateData();
            return;
        }

        var _totalExercisePrice = $('.total-price').map(function () {
            var _value = parseFloat($(this).text().replaceAll(',', ''));
            return isNaN(_value) ? 0 : _value;
        }).get();
        if (_totalExercisePrice.sum() == 0) {
            this.displayOrHideExerciseMethod(IC.Public.Plans.ExerciseTypes.ExerciseAwards, true);
            this.displayOrHideExerciseMethod(IC.Public.Plans.ExerciseTypes.CashExercise, false);
            this.displayOrHideExerciseMethod(IC.Public.Plans.ExerciseTypes.CashExerciseDirectCredit, false);
            this.displayOrHideExerciseMethod(IC.Public.Plans.ExerciseTypes.SellToCover, false);
            this.displayOrHideExerciseMethod(IC.Public.Plans.ExerciseTypes.SameDaySale, true);
        } else {
            this.displayOrHideExerciseMethod(IC.Public.Plans.ExerciseTypes.ExerciseAwards, true);
            this.displayOrHideExerciseMethod(IC.Public.Plans.ExerciseTypes.CashExercise, true);
            this.displayOrHideExerciseMethod(IC.Public.Plans.ExerciseTypes.CashExerciseDirectCredit, true);
            this.displayOrHideExerciseMethod(IC.Public.Plans.ExerciseTypes.SellToCover, true);
            this.displayOrHideExerciseMethod(IC.Public.Plans.ExerciseTypes.SameDaySale, true);
        }
        this.validateData();
    },

    displayOrHideExerciseMethod: function (exerciseMethodCode, show) {
        var isMethodSeletced = $('#ExerciseMethods_' + exerciseMethodCode).is(':checked');
        if (show) {
            $('#exerciseMethodLi' + exerciseMethodCode).show();
            if (isMethodSeletced)
                $('#ExerciseMethods_' + exerciseMethodCode + '_container').show();
        } else {
            $('#exerciseMethodLi' + exerciseMethodCode).hide();
            $('#ExerciseMethods_' + exerciseMethodCode + '_container').hide();
        }
    },

    onMethodSelect: function (events) {
        var _obj = events.data.obj;
        var optionId = $(this).val();
        var containers = $('.exercise-method-container');
        containers.hide();
        $('.method-details').hide();
        containers.find("input:radio").removeAttr("checked");

        var id = "#ExerciseMethods_" + optionId + "_container";
        $(id).show();

        $('.sellTotals').hide();
        var availableFor = ".AvailableFor_" + optionId;
        $(availableFor).show();

        _obj.EstimatedSaleValue.text($.formatNumber(0, "#,0.00"));
        _obj.validateData();
        _obj.displayTermsAndConditions();
    },

    getSellPrice: function (element) {
        var sellprice = element.find('.sell-price');
        var inputSellPrice = sellprice.find('input');
        var value;

        if (inputSellPrice.length > 0) {
            value = parseFloat(inputSellPrice.val());
        } else
            value = parseFloat(sellprice.text());

        if (isNaN(value)) return 0;
        return value;
    },

    updateEstimatedValues: function (element, methodCode) {
        if (!element) {
            var salesMethod = $("input[name='SalesMethod']:checked");
            element = salesMethod.parent().parent().find('.method-details');
            methodCode = salesMethod.val();
        }

        if (methodCode == IC.Public.Plans.SaleMethods.SellByLimit || methodCode == IC.Public.Plans.SaleMethods.GoodTillCancel ||
            methodCode == IC.Public.Plans.SaleMethods.SellByMarketOrder) {
            var sellPrice = this.getSellPrice(element);
            var estimateSaleValue = (parseInt(this.TotalSelected.text().replaceAll(",", "")) * sellPrice).toFixed(2);
            this.EstimatedSaleValue.text($.formatNumber(estimateSaleValue, "#,0.00"));
        }

    },

    onMethodDetailsClick: function (events) {
        var _obj = events.data.obj;
        $(this).parents('ul.method-detail-items').find('.method-details').hide();
        var methodDetails = $(this).parents('ul.method-detail-items li').find('.method-details');
        if (methodDetails.length == 0)
            methodDetails = $(this).parents('ul.method-detail-items li').find('.view-details');

        if ($(this).val() == 'FDC' || $(this).val() == 'IBW') {
            $("div .callout").show();
        } else {
            $("div .callout").hide();
        }

        methodDetails.show();
        _obj.updateEstimatedValues(methodDetails, $(this).val());
        _obj.validateData();
        _obj.displayTermsAndConditions();
    },

    displayTermsAndConditions: function () {
        if (this.SalesMethod.is(':checked')) {
            this.salesTermsAndConditions.show();
        } else {
            this.salesTermsAndConditions.hide();
        }

        var shareDeliveryMethod = $("input[name='ShareDeliveryMethod']:checked").val();
        if (shareDeliveryMethod == IC.Public.Plans.ShareDeliveryMethods.TransferToSRN) {
            this.transferTermsAndConditions.show();
        } else {
            this.transferTermsAndConditions.hide();
        }
    },

    getNumberToExercise: function () {
        var result = $('.numberToExercise').map(function (index, item) {
            var grantId = $(item).attr('grantId');
            var planCode = $(item).attr('planCode');
            var numberToExercise = $(item).val();
            if (numberToExercise == '' || numberToExercise == '0') return null;
            return grantId + "|" + numberToExercise + "|" + planCode;
        }).get();
        return result.join(',');
    },

    validateData: function () {
        if (!$("input[name='ExerciseMethods']:checked").is(':visible')) {
            this.btnContinue.parent().addClass('disabled');
            return false;
        }
        var exerciseMethod = $("input[name='ExerciseMethods']:checked").val();
        var isValid = exerciseMethod != null;
        var selectedGrants = this.getNumberToExercise();
        isValid = isValid &&
            (selectedGrants && selectedGrants != '') &&
            this.TermsAndConditionsAccepted.is(':checked') &&
            this.checkMethodDetails(exerciseMethod) &&
            $("#HasUnregisteredHrns").val().toLowerCase() == "false";

        if (isValid)
            this.btnContinue.parent().removeClass('disabled');
        else
            this.btnContinue.parent().addClass('disabled');

        return isValid;
    },

    checkMethodDetails: function (exerciseMethod) {
        var isValid = false;
        var salesMethod, shareDelivery, paymentMethod;

        switch (exerciseMethod) {
            case IC.Public.Plans.ExerciseTypes.CashExercise:
            case IC.Public.Plans.ExerciseTypes.CashExerciseDirectCredit:
            case IC.Public.Plans.ExerciseTypes.ExerciseAwards:
                shareDelivery = $("input[name='ShareDeliveryMethod']:checked").val();
                isValid = (shareDelivery != null);
                break;

            case IC.Public.Plans.ExerciseTypes.SameDaySale:
                salesMethod = $("input[name='SalesMethod']:checked").val();
                paymentMethod = $("input[name='PaymentMethod']:checked").val();
                isValid = (salesMethod != null && paymentMethod != null && this.checkSalesMethodDetails(exerciseMethod, salesMethod));
                break;

            case IC.Public.Plans.ExerciseTypes.SellToCover:
                salesMethod = $("input[name='SalesMethod']:checked").val();
                shareDelivery = $("input[name='ShareDeliveryMethod']:checked").val();
                isValid = (salesMethod != null && shareDelivery != null && this.checkSalesMethodDetails(exerciseMethod, salesMethod));
                break;
        }

        if (isValid && shareDelivery == IC.Public.Plans.ShareDeliveryMethods.TransferToSRN) {
            isValid = this.checkTransferToSrnShareDeliveryMethodDetails(exerciseMethod);
        }
        return isValid;
    },

    checkSalesMethodDetails: function (exerciseMethod, salesMethod) {
        var isValid = true;
        var methodDetailsContainer = $('#ExerciseMethods_' + exerciseMethod + '_container');

        if (salesMethod == IC.Public.Plans.SaleMethods.SellByLimit) {
            var sellByLimitPrice = parseInt(methodDetailsContainer.find('#SellByLimitPrice').val());
            isValid = (!isNaN(sellByLimitPrice) && sellByLimitPrice > 0);
        }
        else if (salesMethod == IC.Public.Plans.SaleMethods.GoodTillCancel) {
            var goodTillCancelPrice = parseInt(methodDetailsContainer.find('#GoodTillCancelPrice').val());
            isValid = (!isNaN(goodTillCancelPrice) && goodTillCancelPrice > 0);
        }
        return isValid;
    },

    checkTransferToSrnShareDeliveryMethodDetails: function (exerciseMethod) {
        var isValid = true;
        var methodDetailsContainer = $('#ExerciseMethods_' + exerciseMethod + '_container');
        var transferToSrnTextBox = methodDetailsContainer.find("#UserSRN[type='text']");
        if (transferToSrnTextBox != undefined && transferToSrnTextBox.val() != undefined) {
            isValid = (transferToSrnTextBox.val().trim().length > 0);
        }

        return isValid;
    },

    onContinueButtonClick: function (events) {
        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        var plans = _obj.grid.getPostDataItem('plans');
        var selectedGrants = _obj.getNumberToExercise();

        if (!selectedGrants) return;

        $('#hdnPlans').val(plans);
        $('#hdnSelectedGrants').val(selectedGrants);
        var form = $("form#confirmExerciseForm");
        var data = form.serializeObject();

        var exerciseMethod = $("input[name='ExerciseMethods']:checked").val();
        var methodDetailsContainer = $('#ExerciseMethods_' + exerciseMethod + '_container');
        if (data.ShareDeliveryMethod == IC.Public.Plans.ShareDeliveryMethods.TransferToSRN) {
            var srnControl = methodDetailsContainer.find("[name='UserSRN']");
            var srnValue = srnControl.val();
            if (srnValue == "") srnValue = srnControl.text();
            var noDataAttributeValue = srnControl.attr('data-no-value');
            if (noDataAttributeValue == "true") srnValue = "";
            $('#hdnTransferToSrn').val(srnValue);
        }
        if (data.SalesMethod == IC.Public.Plans.SaleMethods.SellByLimit)
            $('#hdnLimitPrice').val(methodDetailsContainer.find('#SellByLimitPrice').val());
        else if (data.SalesMethod == IC.Public.Plans.SaleMethods.GoodTillCancel)
            $('#hdnLimitPrice').val(methodDetailsContainer.find('#GoodTillCancelPrice').val());

        data = form.serializeObject();
        _obj.validateExercise(data);
    },

    validateExercise: function (data) {
        IC.Public.showLoading("");
        $.ajax({
            url: IC.Public.urls.Exercise.validateExercise,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },

    onUpdateBankAccountLinkClick: function (events) {
        var self = events.data.obj;
        //var proceedSeqNumber = parseInt($(this).attr('proceedSeqNumber'));
        var proceedSeqNumber = $(this).attr('proceedSeqNumber');
        IC.Public.showLoading("");

        var paymentMethodCode = $(this).attr('paymentMethodTypeCode');
        var paymentMethodDesc = $(this).attr('paymentMethodTypeDesc');
        var modal = new IC.Public.Plans.BankAccountDetails();
        modal.showModal(proceedSeqNumber, IC.Public.Plans.Common.MenuSubItemTypes.Exercise, self.validateTransactionPassword, paymentMethodCode, paymentMethodDesc);
    },

    onUpdateIbwAccountLinkClick: function (events) {
        //var proceedSeqNumber = parseInt($(this).attr('proceedSeqNumber'));
        var proceedSeqNumber = $(this).attr('proceedSeqNumber');
        var modal = new IC.Public.Plans.InternationalBankWire();
        IC.Public.showLoading("");
        modal.showModal(proceedSeqNumber, IC.Public.Plans.Common.MenuSubItemTypes.Exercise);
    },

    onUpdateAddressLinkClick: function () {
        var url = OC.MVC.util.getLink("Address", "Edit");
        OC.MVC.util.loadMainContainerView(url);
    },

    onExerciseMessageLinkClick: function () {
        var type = $(this).attr("type");
        var code = $(this).attr("code");
        var viewKey = $("#ViewKey").val();
        var transactionType = $(this).attr("transactionType");
        var windowId = $(this).attr("windowId");

        var modal = new IC.Public.Plans.message();
        modal.showModal(type, code, viewKey, transactionType, windowId, IC.Public.Plans.Common.MenuSubItemTypes.Exercise);
    }
};

;IC.Public.Plans.exerciseReceipt = function (isSaleReceipt) {
    this.init(isSaleReceipt);
};

IC.Public.Plans.exerciseReceipt.prototype = {
    init: function (isSaleReceipt) {
        this.isSaleReceipt = isSaleReceipt;
        this.grid = $("#ExerciseDetailsGrid");
        this.grid.unbind(OC.MVC.grid.events.loadComplete);
        this.grid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onLoadComplete);
        
        this.btnDoneButton = $('#btnDone');
        this.btnDoneButton.unbind('click');
        this.btnDoneButton.bind("click", { obj: this }, this.onDoneButtonClick);
        
        this.aDownloadReceiptLink = $("#aDownloadReceiptLink");
        this.aDownloadReceiptLink.unbind();
        this.aDownloadReceiptLink.bind("click", { obj: this }, this.OnDownloadReceiptLinkClicked);
        
        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.Exercise);
    },

    onLoadComplete: function (events, gridObj, data) {
        IC.Public.Exercise.processRestrictedGrantDetails();
        
        $("A.cluetooltip").each(function () {
            $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 150 });
        });
    },
    
    onDoneButtonClick: function() {
        OC.MVC.util.loadMainContainerView(IC.Public.urls.Exercise.exerciseTransactionList, {viewKey: $('#ViewKey').val()});
    },
    
    OnDownloadReceiptLinkClicked: function (events) {
        var self = events.data.obj;
        var url = OC.MVC.util.getLink("Exercise", "GenerateSaleReceipt");
        if (!self.isSaleReceipt)
            url = OC.MVC.util.getLink("Exercise", "GenerateCashReceipt");
        window.location = url + "?viewKey=" + $("#ViewKey").val() + "&exerciseId=" + $("#ExerciseId").val();
    }
};IC.Public.Plans.exerciseTransactionList = function () {
    this.init();
};

IC.Public.Plans.exerciseTransactionList.prototype = {
    init: function () {
        var gridLinksInitialised = false;
        var rowCount = 0;
        this.exerciseTransactionsGrid = $("#ExerciseTransactionsGrid");
        this.exerciseTransactionsGrid.bind("grid_gridComplete", { obj: this }, function (events, gridContext) {
            rowCount = gridContext;
            var _obj = events.data.obj;
            $(this).find("a.view-confirmation").each(function (index) {
                if ($(this).attr('key') && $(this).attr('key').trim().length > 0) {
                    gridLinksInitialised = true;
                    $(this).bind("click", { obj: _obj }, _obj.viewConfirmationClick);
                }
            });
        });
        
        // Sometimes the grid is loaded before the gridComplete event can be binded, so we need to reload so that View Confirmation click event is binded
        if (rowCount > 0 && !gridLinksInitialised) {
            this.exerciseTransactionsGrid.trigger('reloadGrid');
        }
        
        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.Exercise);
    },

    viewConfirmationClick: function() {
        var exerciseId = $(this).attr('key');
        var url = OC.MVC.util.getLink("Exercise", "ViewConfirmation");
        OC.MVC.util.loadMainContainerView(url, { viewKey: $('#ViewKey').val(), exerciseId: exerciseId });
    }
};;IC.Public.Plans.plansummaryPostAjax = function (container, url, data) {
    $.ajax({
        url: url,
        data: data,
        type: "GET",
        success: function (html) {
            OC.MVC.util.loadHtml(container, html);
        }
    });
};
IC.Public.Plans.plansummary = function (tabNum, hasNewOffers, hideAvailableOffers, hideOutstandingLoanPopup) {
    this.planSummaryContainer = $('#planSummaryContainer');
    this.fullyPaidOrdinarySecurityContainer = $('#fullyPaidOrdinarySecurityContainer');
    this.hasNewOffers = hasNewOffers.toString().toLowerCase() == "true";
    this.hideAvailableOffers = hideAvailableOffers;
    this.hideOutstandingLoanPopup = hideOutstandingLoanPopup;
    this.init(tabNum);
};

IC.Public.Plans.plansummary.prototype = {
    init: function (tabNum) {
        this.registerEvents();
        var isInitialScreenPlanSummary = ($('#IsInitialScreenPlanSummary').val() != undefined && $('#IsInitialScreenPlanSummary').val().toLowerCase() == "true");
        var isImpersonateUser = ($('#IsImpersonateUser').val() != undefined && $('#IsImpersonateUser').val().toLowerCase() == "true");
        /*Fix for INC0049108*/
        //if (isInitialScreenPlanSummary && !isImpersonateUser)
        //{
        //    IC.Public.showLastLoginInfo();
        //}
        $("#lnkPwdClose").bind('click', { obj: this }, this.onPwdBannerHideClick);
        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.PlanSummary);
        $('#downloadLink').attr('href', 'javascript:onDownloadClick("CSV");');

        $('#planDropDownDiv').hide();
        $('#vestingCalculatorDiv').hide();
        if (tabNum != null && tabNum > -1) {
            //this.planSummaryContainer.find('.displayLinks a').eq(tabNum).trigger('click'); // trigger required tab
            
            $('#planSummaryContainer').find(".displayLinks a[summarygridtype='" + tabNum + "']").trigger('click');

        }

        //Offer Modal related
        if (sessionStorage.showOffersModal != null) {
            if (sessionStorage.showOffersModal.toString().toLowerCase() == "true") {
                this.checkAndDisplayOutstandingLoan();
                if (this.hasNewOffers) {
                    //TODO: Uncomment following method call for Offers Modal
                    this.checkAndDisplayOpenOffers();
                }
                sessionStorage.showOffersModal = false;
                
            }
        }

        //This code would initialize the cluetooltip - this has to be after Grid is loaded
        //this.holdingsListGrid = $("#MyHoldingsGrid");
        //this.holdingsListGrid.bind("grid_gridComplete", { obj: this }, function (events, gridContext) {
        //    var _obj = events.data.obj;
        //    var isCurrencyConversionApplied = IC.Public.Currency.isCurrencyConversionApplied();
        //    var conversionCurrency = _obj.conversionCurrency.val();
        //    _obj.setGridColumnCurrencyCodes(conversionCurrency, isCurrencyConversionApplied);
        //    $("A.cluetooltip").each(function () {
        //        $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 310 });
        //    });
        //});

        $("#lnkClose").bind('click', { obj: this }, this.onBannerHideClick);
    },

    onBannerHideClick: function (events) {
        $("#divBannerDetails").hide();
        document.cookie = "BannerCookie=IsHidden; path=/";
    },
    
    onPwdBannerHideClick: function (events) {
        $("#divPwdExpiryDetails").hide();
    },

    
    registerEvents: function () {
        var self = this;
        this.planSummaryContainer.find('.displayLinks a').each(function () {
            $(this).bind('click', { obj: self, url: $(this).attr('actionurl') }, self.displayLinkOnClick);
        });

        this.planSummaryContainer.find('.overviewLinks a').each(function () {
            $(this).bind('click', { obj: self, url: $(this).attr('actionurl') }, self.onActionClick);
        });

        this.fullyPaidOrdinarySecurityContainer.find('.overviewLinks a').each(function () {
            $(this).bind('click', { obj: self, url: $(this).attr('actionurl') }, self.onActionClick);
        });

        var holdingFilter = $("form.holdingFilterForm");
        if (holdingFilter.length) {
            holdingFilter.unbind("applyHoldingFilter");
            holdingFilter.bind("applyHoldingFilter", { obj: self }, self.onFilterClick);
        }

        this.conversionCurrency = $('#conversionCurrency');
        this.conversionCurrency.unbind('applyCurrencyChange');
        this.conversionCurrency.bind('applyCurrencyChange', { obj: this }, this.onApplyCurrencyChange);
    },
    displayLinkOnClick: function (events) {
        // if not already selected
        if (!$(this).hasClass('selected')) {
            $('#planDropDownDiv').hide();
            IC.Public.showLoading("");
            var _obj = events.data.obj;
            _obj.planSummaryContainer.find('.displayLinks a').each(function () {
                $(this).removeClass('selected');
            });
            $(this).addClass('selected');
            var url = events.data.url;
            //IC.Public.Plans.plansummaryPostAjax("PlanSummaryGridContainer", url, null);
            IC.Public.Plans.plansummaryPostAjax("planSummaryTabsGridContainer", url, null);
        }
    },
    onActionClick: function (events) {
        var url = events.data.url;
        OC.MVC.util.loadMainContainerView(url, null);
    },
    onFilterClick: function (events) {
        var url = OC.MVC.util.getLink("Summary", "Summary");
        OC.MVC.util.loadMainContainerView(url, null);
    },
    onApplyCurrencyChange: function (eventArgs, currencyCode) {
        var _obj = eventArgs.data.obj;
        var data = { tabNum: -1 };
        var link = _obj.planSummaryContainer.find('.displayLinks a.selected');

        if (link) {
            switch (link.attr('summaryGridType')) {
                case IC.Public.Plans.Common.SummaryGridType.Overview:
                    data.tabNum = 0;
                    break;
                case IC.Public.Plans.Common.SummaryGridType.TargetReward:
                    data.tabNum = 1;
                    break;
                case IC.Public.Plans.Common.SummaryGridType.RewardReceived:
                    data.tabNum = 2;
                    break;
                case IC.Public.Plans.Common.SummaryGridType.Plans:
                    data.tabNum = 3;
                    break;
                case IC.Public.Plans.Common.SummaryGridType.PlanDocuments:
                    data.tabNum = 4;
                    break;
                case IC.Public.Plans.Common.SummaryGridType.VestingCalculator:
                    data.tabNum = 5;
                    break;
                case IC.Public.Plans.Common.SummaryGridType.PerformanceTracker:
                    data.tabNum = 6;
                    break;
            }
        }
        var url = OC.MVC.util.getLink("Summary", "SummaryTab");
        OC.MVC.util.loadMainContainerView(url, data);
    },

    checkAndDisplayOpenOffers: function () {

        var IshideAvailableOffer = this.hideAvailableOffers;

        if (IshideAvailableOffer != null) {
            if (IshideAvailableOffer.toLowerCase() == "false") {
                var url = OC.MVC.util.getLink("Offers", "OpenOffers");
                OC.MVC.util.showModalByUrl(url, { status: "OPEN", isCaptionRequired: true }, { maxHeight: 360, maxWidth: 500 }, function () {
                    $("#simplemodal-container").addClass("offermodalwrap"); //Fritz required this additional class just for offers.
                    var parentDiv = $('#simplemodal-data').parent();
                    parentDiv.css("overflow", "hidden");
                });
            }
        }

    },

    checkAndDisplayOutstandingLoan: function () {

        var hideOutstandingLoanPopup = this.hideOutstandingLoanPopup;
        if (hideOutstandingLoanPopup.toLowerCase() == "false") {

            var url = OC.MVC.util.getLink("Offers", "OutstandingLoanPlan");
            OC.MVC.util.showModalByUrl(url, { maxHeight: 360, maxWidth: 500 }, function () {
                $("#simplemodal-container").addClass("offermodalwrap"); //Fritz required this additional class just for offers.
                var parentDiv = $('#simplemodal-data').parent();
                parentDiv.css("overflow", "hidden");
            });
        }
    }
};
$.ajaxSetup({
    // Disable caching of AJAX responses
    cache: false
});;IC.Public.Plans.plandetailPostAjax = function (container, gridType, url, data) {
    IC.Public.showLoading("");
    $.ajax({
        url: url,
        data: data,
        type: "POST",
        success: function (html) {
            IC.Public.hideLoading();
            var matches = html.match(/hdnGridType="([^"]*)/);
            if (matches && gridType == matches[1]) {
                OC.MVC.util.loadHtml(container, html);
            }
        },
        error: function (err) {
            IC.Public.hideLoading();
        }
    });
};

IC.Public.Plans.plandetail = function () {
    this.planSummaryContainer = $('#planSummaryContainer');
    this.planDetailGrid = $('#planDetailGridDiv');
    this.init();
};

IC.Public.Plans.plandetail.prototype = {
    init: function () {
        this.registerEvents();
        this.planSummaryContainer.find('.displayLinks a').eq(0).trigger('click'); // trigger first one
        $('#downloadLink').attr('href', 'javascript:onDownloadClick("CSV");');
        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.PlanSummary);
    },
    registerEvents: function () {
        var self = this;
        this.planSummaryContainer.find('.displayLinks a').each(function () {
            var viewKey = $('#PlanViewKey');
            $(this).bind('click', { obj: self, gridType: $(this).attr('gridtype'), url: $(this).attr('actionurl'), planViewKey: viewKey.val() }, self.displayLinkOnClick);
        });

        this.planSummaryContainer.find('.overviewCol1 a').each(function () {
            $(this).bind('click', { obj: self, url: $(this).attr('actionurl') }, self.onActionClick);
        });

        this.planDetailGrid.bind("grid_beforeRequest", { obj: this }, function (events, gridContext) {
            var planDetailGrantsGrid = $('#PlanDetailsGrid');
            var viewKey = $('#PlanViewKey');
            var gridType = $('#GridType');
            planDetailGrantsGrid.setGridParam({ postData: { planViewKey: viewKey.val(), gridType: gridType.val()} });
        });
    },
    displayLinkOnClick: function (events) {
        // if not already selected
        if (!$(this).hasClass('selected')) {
            var _obj = events.data.obj;
            _obj.planSummaryContainer.find('.displayLinks a').each(function() {
                $(this).removeClass('selected');
            });
            $(this).addClass('selected');
            var containerName = "planDetailGridDiv";
            $('#' + containerName).html('');
            var url = events.data.url;
            var planViewKey = events.data.planViewKey;
            var gridType = events.data.gridType;
            IC.Public.Plans.plandetailPostAjax("planDetailGridDiv", gridType, url, { planViewKey: planViewKey });
        }
    },
    onActionClick: function (events) {
        var url = events.data.url;
        OC.MVC.util.loadMainContainerView(url, null);
    }
};;IC.Public.offers = function (options) {

};

IC.Public.offers.prototype = {

    init: function () {

        //Track selected offer
        $("input[name='SelectedOfferType']").bind("click", { obj: this }, this.onOfferTypeChanged);

        $("input[name='SelectedOfferType']:first").trigger("click"); //Default the offer type to "Open Offers"

        var holdingFilter = $("form.holdingFilterForm");
        if (holdingFilter.length) {
            holdingFilter.unbind("applyHoldingFilter");
            holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterClick);
        }
        
        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.Offers);
    },
    onOfferTypeChanged: function (events) {
        var self = events.data.obj;

        //Fetch the grid markup based on selected offer and inject into DOM
        $.get("/Offers/FetchGridMarkup", { offerType: this.value }, function (data) {

            $("#divOfferGrid").html(data);

            $("#divOfferGrid").unbind().bind("grid_gridComplete", { obj: this }, function () {
                
                var url = OC.MVC.util.getLink("Offers", "TakeAction");
                
                /*
                Handle the click events of the action links. The reason we need to have this logic here is because, 
                only at this point it is guaranteed that the action links would have been populated.
                */
                $("#divOfferGrid").find("a[data-linktype='view']").bind("click", function () {
                    self.loadOfferView(OC.MVC.constants.mainContainer, url, self.getParameters(this, "view"));
                });
                $("#divOfferGrid").find("a[data-linktype='edit']").bind("click", function () {
                    self.loadOfferView(OC.MVC.constants.mainContainer, url, self.getParameters(this, "edit"));
                });
                $("#divOfferGrid").find("a[data-linktype='confirmation']").bind("click", function () {
                    var confirmationURL = OC.MVC.util.getLink("Offers", "ConfirmationReceipt");
                    self.loadOfferView(OC.MVC.constants.mainContainer, confirmationURL, self.getParameters(this, "confirmation"));
                });

                $("#divOfferGrid").find("a[data-linktype='documents']").bind("click", function () {
                    var offerId = $(this).attr('data-offerid');
                    var offerGroupCode = $(this).attr('data-offerGroupCode');
                    var offerStatus = $(this).attr('data-offerStatus');
                    var isCaptionRequired = $(this).attr('data-isCaptionRequired');
                    
                    var modal = new IC.Public.OfferDocuments();
                    modal.showModal(offerId, offerGroupCode, offerStatus, isCaptionRequired);
                });

            });
        });
    },
    getParameters:function(thisObj,selectedAction)
    {
        /*
        Builds the controller action parameters.
        */
        var params = {
            offerId: $(thisObj).parents("tr").find("[aria-describedby='OffersGrid_ID']").html(),
            offerCode: $(thisObj).parents("tr").find("[aria-describedby='OffersGrid_OfferCode']").html().replace("&nbsp;", ""),
            employee: $("#hidEmployee").val(),
            employeeID: $("#hidEmployeeID").val(),
            planName: $(thisObj).parents("tr").find("[aria-describedby*='Name']").html().replace(/<br>/g, "").replace(/<BR>/g, ""), //TODO: Need to check if the column name can be without spaces.If so, we can use = operator instead of *=.
            status: $(thisObj).parents("tr").find("[aria-describedby='OffersGrid_Status'] span").html(),
            selectedAction: selectedAction,
            isCaptionRequired: true
        };
        return params;
    },
    onFilterClick: function (events) {
        var url = OC.MVC.util.getLink("Offers", "Index");
        OC.MVC.util.loadMainContainerView(url, { viewKey: $("#ViewKey").val() });
    },
    loadOfferView:function(containerName, viewName, data){
        IC.Public.showLoading("");
        $.ajax({
            url: viewName,
            data: data,
            success: function(result) {
                OC.MVC.util.loadHtml(containerName, result);
                IC.Public.registerGA(viewName, { viewKey: $(result).find("[name=ViewKey]").val() });
            },
            error: function(err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            },
            cache:false
        });
    }

};
;IC.Public.offerActionsParseFloat = function (value) {
    if (value != null && value.length) {
        value = value.replaceAll(',', '');
        return parseFloat(value);
    }
    return value;
};

IC.Public.OfferActions = function (options) {

};
IC.Public.OfferActions.prototype = {
    init: function () {
        
        var thisObj = this;
        var btnContinue = $("#btnContinue");
        $(btnContinue).parent().addClass("disabled");
        $(btnContinue).attr("disabled", "disabled");

        var btnCancel = $("#btnCancel");
        btnCancel.bind("click", { obj: this }, this.onCancelClick);

        btnContinue.bind("click", { obj: this }, this.onContinueClick);

        $(".rdoAccept").each(function (i, obj) {
            $(this).bind("click", { obj: thisObj }, thisObj.onRadioButtonClick);
            var ofId = $(this).attr("data-theid");
            $(this).attr("id", "rdoAccept" + ofId);
        });
        $(".rdoReject").each(function (i, obj) {
            $(this).bind("click", { obj: thisObj }, thisObj.onRadioButtonClick);
            var ofId = $(this).attr("data-theid");
            $(this).attr("id", "rdoReject" + ofId);
        });

        var chkAcceptTermsAndConditions = $("#chkAcceptTermsAndConditions");

        chkAcceptTermsAndConditions.change(function () {
            if ($(this).is(':checked')==true) {
                $(btnContinue).parent().removeClass("disabled");
                $(btnContinue).removeAttr("disabled");
            } else {
                $(btnContinue).parent().addClass("disabled");
                $(btnContinue).attr("disabled", "disabled");
            }

        });

        //To keep track of the selected action on page load.
        $(".rdoReject").each(function (i, obj) {
            var ofId = $(this).attr("data-theid");
            if ($(this).is(':checked') == true) {
                $(this).trigger('click');
            }
        });
        $(".rdoAccept").each(function (i, obj) {
            var ofId = $(this).attr("data-theid");
            if ($(this).is(':checked') == true) {
                $(this).trigger('click');
            }
        });
        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.Offers);

        $(".rejectWarning").each(function (i, obj) {
            $(this).hide();
        });
        
        if ($("#LoanAmountType").val() === "CLA") {
            var currencyCode = $(this).find('#offerModel_CurrencyCode').val();
            var acceptedAmountDisplayValue = $("#AcceptedAmountDisplayValue").val();
            if (currencyCode == undefined || currencyCode == "")
                currencyCode = "AUD";
            var acceptedAmountValue;
            if (typeof acceptedAmountDisplayValue != "undefined") {
                acceptedAmountValue = acceptedAmountDisplayValue.toString().replaceAll(currencyCode, "");
                acceptedAmountValue = acceptedAmountValue.toString().replaceAll(",", "");
                $("#AcceptedAmountValue").val(acceptedAmountValue);
                $("#loanAmountValue").html(currencyCode + ' ' + $.formatNumber(acceptedAmountValue * $("#LoanAmountPerShare").val(), "#,0.00"));
                $("#LoanAmount").val($.formatNumber(acceptedAmountValue * $("#LoanAmountPerShare").val(), "#,0.00"));
            }
        }
        $("#AcceptedAmountValue").keyup(function (e) {

            if (event.which != 8 && isNaN(String.fromCharCode(event.which))) {
                event.preventDefault(); //stop character from entering input
            }

            if ($("#LoanAmountType").val() === "CLA") {
                if (currencyCode == undefined || currencyCode == "")
                    currencyCode = "AUD";
                var acceptedAmountValue = $(this).val();

                if (acceptedAmountValue != undefined && acceptedAmountValue.trim() != '') {
                    acceptedAmountValue = acceptedAmountValue.toString().replaceAll(currencyCode, "");
                    acceptedAmountValue = acceptedAmountValue.toString().replaceAll(",", "");
                }
                $("#loanAmountValue").html(currencyCode + ' ' + $.formatNumber(acceptedAmountValue * $("#LoanAmountPerShare").val(), "#,0.00"));
                $("#LoanAmount").val($.formatNumber(acceptedAmountValue * $("#LoanAmountPerShare").val(), "#,0.00"));
            }

        }
        );
        thisObj.divEntitlementError = $('#divEntitlementError');
        thisObj.divEntitlementError.hide();
    },

    onRadioButtonClick: function (events) {
        
        var self = events.data.obj;
        //Each radio button on the offer actions page would have a data-id attribute to denote the type of action it is intended to perform.
        var selectedAction = $(this).attr("data-id");
        var index = $(this).parent().parent().find("#currentIndexId").val();
        var currentOfferDiv = $(this).parents().find("#divOffer" + index);
        self.onOfferActionClick(currentOfferDiv, $(this));

        currentOfferDiv.find("#hidSelectedAction").val(selectedAction);
        currentOfferDiv.find(".OfferError").empty();
        currentOfferDiv.find(".input-validation-error error").parent().removeAttr("class");

        var currencyCode = $(this).find('#offerModel_CurrencyCode').val();
    },
    onCancelClick: function () {
        var url = OC.MVC.util.getLink("Offers", "Index");
        OC.MVC.util.loadMainContainerView(url);
    },
    onOfferActionClick: function (currentOfferDiv, el) {
        //Toggle content based on selected radio option
        if (el.hasClass("rdoAccept")) {
            currentOfferDiv.find("#divAccept").css("display", "block");
            currentOfferDiv.find("#divReject").css("display", "none");
        } else {
            currentOfferDiv.find("#divAccept").css("display", "none");
            currentOfferDiv.find("#divReject").css("display", "block");
        }
        if (currentOfferDiv.find("#hidOptOut").val()) {
            currentOfferDiv.find("#divAccept").css("display", "block");
            currentOfferDiv.find("#divReject").css("display", "block");
            if (el.hasClass("rdoAccept")) {
                currentOfferDiv.find(".rejectWarning").hide();
            }
        }
    },
    onContinueClick: function (events) {
        var self = events.data.obj;
        var url = OC.MVC.util.getLink("Offers", "Confirmation");
        var isValid;
        
        isValid = self.validate();

        var resultArray = [];
        if (isValid) {
            IC.Public.showLoading("");
            var form = $("#frmOfferActions");
            form.find('.SingleOffer').each(function (i, obj) {
                var offerDetails = $(this).find(':input').serializeObject();
                var item = {};
                for (var key in offerDetails) {
                    if (offerDetails.hasOwnProperty(key)) {
                        var modifiedKey = key.replaceAll("offerModel.", "");
                        item[modifiedKey] = offerDetails[key];
                    }
                }
                resultArray.push(item);
            });
            var result = { 'OfferActions': resultArray, 'Count': resultArray.length };

            $.post(url, result, function (data) {
                $("#" + OC.MVC.constants.mainContainer).html(data);
                IC.Public.hideLoading();
            });
        }
    },
    validate: function (events) {
        var isValid = true;
        var isOfferValid = true;
        var thisObj = this;

        thisObj.divEntitlementError.empty();
        $(".OfferError").each(function (i, obj) {
            $(this).empty(); //clear previous errors.
        });

        var singleOffersCount = 0;
        var acceptedOffersCount = 0;
        var rejectedOffersCount = 0;
        var noOfferActionCount = 0;
        var offerAcceptanceType = $("#hidOfferAcceptanceType").val()
        $('.SingleOffer').each(function (i, obj) {
            singleOffersCount++;
            var selectedAction = $(this).find("#hidSelectedAction").val().toLowerCase();
            if (!(selectedAction == 'accept' || selectedAction == 'reject')) {
                noOfferActionCount++;
                var errorMessage = thisObj.getRadioButtonErrorMesssage($(this));                
                if (offerAcceptanceType.toLowerCase() != "s" || acceptedOffersCount + rejectedOffersCount == 0){
                    thisObj.BuildError("Invalid Selection", errorMessage, $(this));
                    isOfferValid = false;
                }
            } else {                
                if (selectedAction == 'accept') {
                    acceptedOffersCount++;
                    var offerValueType = $(this).find("#hidOfferValueType").val().toLowerCase();
                    var hasDualAmountValueTypes = $(this).find("#hidHasDualAmountValueTypes").val().toLowerCase() == "true";
                    var offerAmountType = $(this).find("#hidOfferAmountType").val().toLowerCase();

                    if (hasDualAmountValueTypes) {
                        isOfferValid= thisObj.validateAmount(offerAmountType, $(this)) & thisObj.validatePercentage(offerAmountType, $(this));

                    } else if (offerValueType == "amount") {
                        isOfferValid = thisObj.validateAmount(offerAmountType, $(this));
                    } else if (offerValueType == "percentage") {
                        isOfferValid = thisObj.validatePercentage(offerAmountType, $(this));
                    }
                }
                else if (selectedAction == 'reject'){
                    rejectedOffersCount++;
                }
            }
            if (!isOfferValid) {
                isValid = false;
            }
        });

        if (offerAcceptanceType.toLowerCase() == "s"){
            if (singleOffersCount > 1 && acceptedOffersCount > 1) {
                isValid = false;
                var errorMsg = "You may participate in one offer only. Please amend your selection by rejecting one of the offers";
                $('.SingleOffer').each(function (i, obj) {

                    thisObj.BuildError("Invalid Selection", errorMsg, $(this));
                });
            }
            else if (acceptedOffersCount + rejectedOffersCount > 0) {
                isValid = true;
            }
        }
        if (isValid) {
            isValid = thisObj.ValidateEntitlementLimit();
        }

        return isValid;
    },

    getRadioButtonErrorMesssage: function (parentDiv) {
        var errorMessage = "Please Accept or Reject Offer before proceeding.";
        if (parentDiv == undefined) {
            return errorMessage;
        }

        if (parentDiv.find('.rdoAccept').length == 0) {
            errorMessage = "Please Reject Offer before proceeding.";
        }
        else if (parentDiv.find('.rdoReject').length == 0) {
            errorMessage = "Please Accept Offer before proceeding.";
        }

        return errorMessage;
    },

    validateAmount: function (offerAmountType, currentOfferElement) {
        var isValid = false;
        var acceptedAmountValue = IC.Public.offerActionsParseFloat(currentOfferElement.find("#AcceptedAmountValue").val());
        var minimumEntitlementValue = IC.Public.offerActionsParseFloat(currentOfferElement.find("#minimumEntitlementValue").html());
        if (offerAmountType != "fixed") {
            var blockSize = IC.Public.offerActionsParseFloat(currentOfferElement.find("#AmountBlockSize").val());
            if (acceptedAmountValue >= minimumEntitlementValue) { //For both range and minimum, the entered value has to be greater than minimumEntitlementValue
                if (offerAmountType == "range") { //additional conditions apply
                    var maximumEntitlementValue = IC.Public.offerActionsParseFloat(currentOfferElement.find("#maximumEntitlementValue").html());
                    if (acceptedAmountValue <= maximumEntitlementValue) {
                        if (blockSize > 0 && (acceptedAmountValue % blockSize > 0)) {
                            this.BuildError("AcceptedAmountValue", "Please enter an amount in multiples of " + blockSize + ".", currentOfferElement);
                            isValid = false;
                        } else {
                            currentOfferElement.find("#AcceptedAmountValue").removeAttr("class");
                            isValid = true;
                        }
                    } else {
                        this.BuildError("AcceptedAmountValue", "Please enter an amount within the range.", currentOfferElement);
                        isValid = false;
                    }
                } else if (offerAmountType == "minimum") {
                    if (blockSize > 0 && (acceptedAmountValue % blockSize > 0)) {
                        this.BuildError("AcceptedAmountValue", "Please enter an amount in multiples of " + blockSize + ".", currentOfferElement);
                        isValid = false;
                    } else {
                        currentOfferElement.find("#AcceptedAmountValue").removeAttr("class");
                        isValid = true;
                    }
                }
            } else {
                if (offerAmountType == "range") {
                    this.BuildError("AcceptedAmountValue", "Please enter an amount within the range.", currentOfferElement);
                } else if (offerAmountType == "minimum") {
                    this.BuildError("AcceptedAmountValue", "Please enter an amount greater than the minimum offer entitlement.", currentOfferElement);
                }
                isValid = false;
            }
        } else
            isValid = true; //There is no validation for fixed amount type.

        return isValid;
    },
    validatePercentage: function (offerAmountType, currentOfferElement) {
        var isValid = false;
        var acceptedPercentValue = currentOfferElement.find("#AcceptedPercentValue").val();
        var minimumEntitlementPercentage = IC.Public.offerActionsParseFloat(currentOfferElement.find("#minimumEntitlementPercentage").html());

        if (offerAmountType != "fixed") {
            var blockSize = IC.Public.offerActionsParseFloat(currentOfferElement.find("#PercentageBlockSize").val());
            if (acceptedPercentValue >= minimumEntitlementPercentage) { //For both range and minimum, the entered value has to be greater than minimumEntitlementPercentage
                if (offerAmountType == "range") { //additional conditions apply
                    var maximumEntitlementPercentage = IC.Public.offerActionsParseFloat(currentOfferElement.find("#maximumEntitlementPercentage").html());
                    if (acceptedPercentValue <= maximumEntitlementPercentage) {
                        if (blockSize > 0 && (acceptedPercentValue % blockSize > 0)) {
                            this.BuildError("AcceptedPercentValue", "Please enter a value in multiples of " + blockSize + ".", currentOfferElement);
                            isValid = false;
                        } else {
                            currentOfferElement.find("#AcceptedPercentValue").removeAttr("class");
                            isValid = true;
                        }
                    } else {
                        this.BuildError("AcceptedPercentValue", "Please enter a value within the range.", currentOfferElement);
                        isValid = false;
                    }
                } else if (offerAmountType == "minimum") {
                    if (blockSize > 0 && (acceptedPercentValue % blockSize > 0)) {
                        this.BuildError("AcceptedPercentValue", "Please enter a value in multiples of " + blockSize + ".", currentOfferElement);
                        isValid = false;
                    } else {
                        currentOfferElement.find("#AcceptedPercentValue").removeAttr("class");
                        isValid = true;
                    }
                }
            } else {
                if (offerAmountType == "range") {
                    this.BuildError("AcceptedPercentValue", "Please enter a value within the range.", currentOfferElement);
                } else if (offerAmountType == "minimum") {
                    this.BuildError("AcceptedPercentValue", "Please enter a value greater than the minimum offer entitlement.", currentOfferElement);
                }
                isValid = false;
            }
        } else
            isValid = true; //There is no validation for fixed amount type.

        return isValid;
    },
    BuildError: function (targetElementId, errorMessage, currentOfferElement) {
        var errorContent = currentOfferElement.find(".OfferError");
        if (errorContent.children().length > 0) {
            errorContent = currentOfferElement.find(".OfferError ol");
        }
        var li = $("<li>");
        var label = $("<label>");
        label.attr("for", targetElementId);
        label.attr("data-parent-div-id", currentOfferElement.attr('id'));
        label.attr("class", "error");
        label.html(errorMessage);
        label.bind('click', { obj: this }, this.handleErrorLabelClick);
        li.append(label);
        
        var targetControl = currentOfferElement.find("#" + targetElementId);
        if (targetControl != undefined) {
            targetControl.attr("class", "input-validation-error error");
        }

        if (errorContent.children().length == 0) {
            var ol = $("<ol>");
            ol.append(li);
            $(errorContent).append(ol);
        } else {
            $(errorContent).append(li);
        }
    },
    
    handleErrorLabelClick: function (eventArgs) {
        var forId = $(this).attr('for');
        var parentId = $(this).attr('data-parent-div-id');
        var targetControl = $('#' + parentId).find('#' + forId);
        if (targetControl == undefined) return true;

        targetControl.focus();
        return false;
    },

    ValidateEntitlementLimit: function () {
        var isValid = true;
        var hidMaxEntitlementLimit = -1;
        var sumOfAllAcceptedAmounts = 0;
        var divEntitlementError;
        var self = this;
        $('.SingleOffer').each(function (i, obj) {
            if (hidMaxEntitlementLimit == -1) {
                hidMaxEntitlementLimit = $(this).parent().find("#hidMaxEntitlementLimit").val();
                divEntitlementError = $(this).parent().find("#divEntitlementError");
            }

            var offerMinAmountLimit = parseFloat($(this).find("#OfferMinAmountLimit").val());
            var offerMaxAmountLimit = parseFloat($(this).find("#OfferMaxAmountLimit").val());
            var offerAmount = 0;
            var selectedAction = $(this).find("#hidSelectedAction").val().toLowerCase();
            if (selectedAction == 'accept') {
                var currencyCode = $(this).find('#offerModel_CurrencyCode').val();
                if (currencyCode == undefined || currencyCode == "")
                    currencyCode = "AUD";

                var acceptedAmountValue = self.getAcceptedAmountValue($(this));
                if (acceptedAmountValue != undefined && acceptedAmountValue.trim() != '') {
                    acceptedAmountValue = acceptedAmountValue.toString().replaceAll(currencyCode, "");
                    acceptedAmountValue = acceptedAmountValue.toString().replaceAll(",", "");
                    sumOfAllAcceptedAmounts = parseFloat(sumOfAllAcceptedAmounts) + parseFloat(acceptedAmountValue);
                    offerAmount = parseFloat(acceptedAmountValue);
                }

                // Handle Additional Amount
                var acceptedAdditionalAmountValue = self.getAcceptedAdditionalAmountValue($(this));
                if (acceptedAdditionalAmountValue != undefined && acceptedAdditionalAmountValue.trim() != '') {
                    acceptedAdditionalAmountValue = acceptedAdditionalAmountValue.toString().replaceAll(currencyCode, "");
                    acceptedAdditionalAmountValue = acceptedAdditionalAmountValue.toString().replaceAll(",", "");
                    sumOfAllAcceptedAmounts = parseFloat(sumOfAllAcceptedAmounts) + parseFloat(acceptedAdditionalAmountValue);
                    offerAmount += parseFloat(acceptedAdditionalAmountValue);
                }

                if (offerMaxAmountLimit > 0 && offerAmount > offerMaxAmountLimit) {
                    isValid = false;
                    self.BuildError("AcceptedAmountValue", "The maximum offer amount limit of " + offerMaxAmountLimit + " has been exceeded, please re-enter amounts", $(this));
                } else if (offerMinAmountLimit > 0 && offerAmount < offerMinAmountLimit) {
                    isValid = false;
                    self.BuildError("AcceptedAmountValue", "The minimum offer amount limit of " + offerMinAmountLimit + " has not been met, please re-enter amounts", $(this));
                }

            }
        });
        if ((parseFloat(sumOfAllAcceptedAmounts) > parseFloat(hidMaxEntitlementLimit) && (parseFloat(hidMaxEntitlementLimit) > 0))) {
            isValid = false;
            this.BuildMaxEntitlementError(hidMaxEntitlementLimit, divEntitlementError);
        }
        return isValid;
    },

    getAcceptedAmountValue: function (offerDiv) {
        var acceptedAmountValue = "0";
        if (offerDiv.find("#AcceptedAmountValue").length > 0) {
            acceptedAmountValue = offerDiv.find("#AcceptedAmountValue").val();
        } else if (offerDiv.find("#AcceptedAmountDisplayValue").length > 0) {
            acceptedAmountValue = offerDiv.find("#AcceptedAmountDisplayValue").val();
        }

        return acceptedAmountValue;
    },

    getAcceptedAdditionalAmountValue: function (offerDiv) {
        var acceptedAdditionalAmountValue = "0";
        var additionalAmountControl = offerDiv.find("#AcceptedPercentValue[isadditionalamount='1']");
        var additionalAmountDisplayControl = offerDiv.find("#AcceptedPercentDisplayValue[isadditionalamount='1']");
        if (additionalAmountControl.length > 0) {
            acceptedAdditionalAmountValue = additionalAmountControl.val();
        } else if (additionalAmountDisplayControl.length > 0) {
            acceptedAdditionalAmountValue = additionalAmountDisplayControl.val();
        }

        return acceptedAdditionalAmountValue;
    },

    BuildMaxEntitlementError: function (hidMaxEntitlementLimit, divEntitlementError) {
        divEntitlementError.html("<span class='field-validation-error'>" + $('#hdnMaxEntitlementLimitMessage').val() + "</span>");
        divEntitlementError.show();
        divEntitlementError.focus();
        $("html, body").animate({ scrollTop: 0 }, "slow");
    }
};

$.postify = function (value) {
    var result = {};
    
    var buildResult = function (object, prefix) {
        for (var key in object) {

            var postKey = isFinite(key)
                ? (prefix != "" ? prefix : "") + "[" + key + "]"
                : (prefix != "" ? prefix + "." : "") + key;

            switch (typeof (object[key])) {
                case "number": case "string": case "boolean":
                    result[postKey] = object[key];
                    break;

                case "object":
                    if (object[key].toUTCString)
                        result[postKey] = object[key].toUTCString().replace("UTC", "GMT");
                    else {
                        buildResult(object[key], postKey != "" ? postKey : key);
                    }
            }
        }
    };

    buildResult(value, "");

    return result;
};;IC.Public.OffersGridFormatter = {
    formatActionsColumn: function (cellValue, options, rowObject) {
        {
            /*Builds the action link depending upon the selected offer and status of each offer*/

            var status = rowObject[6];
            var uniqueId = rowObject[0];
            var selectedOfferType = $("input[name='SelectedOfferType']:checked").val();
            var div = $("<div>");

            var isThirdPartyImpersonateUser = rowObject[13];

            if (isThirdPartyImpersonateUser.toLowerCase() === "false") {
                if (selectedOfferType.toLowerCase() == 'open') {
                    if (status.toString().toLowerCase() == 'election not made' || status.toString().toLowerCase() == 'open') {

                        /* 
                we are trying to create a markup similar to this:
                <a href="javascript:void(0);">Accept</a>
                <span>/</span>
                <a href="javascript:void(0);">Reject</a>
                */
                        var viewOfferHyperlink = $('<a>');
                        viewOfferHyperlink.attr("href", "javascript:void(0);");
                        viewOfferHyperlink.html("View Offer");
                        viewOfferHyperlink.attr("data-linktype", "view");
                        viewOfferHyperlink.appendTo(div);


                    } else if (status.toString().toLowerCase() == 'accepted' || status.toString().toLowerCase() == 'rejected') {
                        /*
                    We are trying to create a markup similar to this:
                    <a href="javascript:void(0);">Edit Instruction</a>
                    <span><br></span>
                    <a href="javascript:void(0);">View Confirmation</a>
                    */

                        var editInstructionHyperlink = $('<a>');
                        editInstructionHyperlink.attr("href", "javascript:void(0);");
                        editInstructionHyperlink.attr("data-linktype", "edit");
                        editInstructionHyperlink.html("Edit Instruction");
                        editInstructionHyperlink.appendTo(div);


                        var seperator = $('<span>');
                        seperator.html("<br/>");
                        seperator.appendTo(div);

                        var viewConfirmationHyperlink = $('<a>');
                        viewConfirmationHyperlink.attr("href", "javascript:void(0);");
                        viewConfirmationHyperlink.attr("data-linktype", "confirmation");
                        viewConfirmationHyperlink.html("View Confirmation");
                        viewConfirmationHyperlink.appendTo(div);
                    }

                    var documentCountColIndex = $(this).getColIndex('DocumentsCount');
                    if (documentCountColIndex >= 0) {
                        var documentsCount = rowObject[documentCountColIndex];
                        if (parseInt(documentsCount) > 0) {
                            seperator = $('<br/>');
                            seperator.appendTo(div);

                            var documentsLink = $('<a>');
                            documentsLink.attr("href", "javascript:void(0);");
                            documentsLink.attr("data-linktype", "documents");
                            documentsLink.attr("data-offerid", IC.Public.OffersGridFormatter.getColumnValue('ID', $(this), rowObject));
                            documentsLink.attr("data-offerGroupCode", IC.Public.OffersGridFormatter.getColumnValue('OfferCode', $(this), rowObject));
                            documentsLink.attr("data-offerStatus", IC.Public.OffersGridFormatter.getColumnValue('Status', $(this), rowObject));
                            documentsLink.attr("data-isCaptionRequired", true);

                            documentsLink.html("View Documents");
                            documentsLink.appendTo(div);
                        }
                    }

                    return div.html();
                } else if (selectedOfferType.toLowerCase() == 'historical') {
                    /*
                We are trying to create a markup similar to this:
                <a href="javascript:void(0);">View Confirmation</a>
                */
                    var viewConfirmationHyperlink = $('<a>');
                    viewConfirmationHyperlink.attr("href", "javascript:void(0);");
                    viewConfirmationHyperlink.attr("data-linktype", "confirmation");
                    viewConfirmationHyperlink.html("View Confirmation");
                    viewConfirmationHyperlink.appendTo(div);
                    return div.html();

                }
            }
            else {
                var span = $("<span>");
                span.addClass("tooltip");
                span.attr("title", "You are viewing a third party's portfolio");
                span.html("Not Available");
                span.appendTo(div);
                return div.html();
            }
        }
    },

    getColumnValue: function (columnName, grid, rowObject) {
        var index = grid.getColIndex(columnName);
        if (index >= 0)
            return rowObject[index];
        return null;
    },

    overflowTooltip: function (cellValue, options, rowObject) {
        /*Displays a tooltip to show cell contents that were cut off because of the column width */

        var div = $('<div>');
        var span = $('<span>');

        span.attr("title", cellValue.replace('&amp;', '&').replace('&apos;', '\'').replace('&gt;', '>').replace('&lt;', '<'));
        span.addClass("tooltip");

        span.html(cellValue);

        span.appendTo(div);
        return div.html();
    },
    wrapWords: function (cellValue, options, rowObject) {
        /*wraps the cell contents if it exceeds the column width*/

        var div = $('<div>');
        var span = $('<span>');

        span.css("word-wrap", "break-word");

        span.html(cellValue);

        span.appendTo(div);
        return div.html();
    },

    formatPlanName: function (cellValue, options, rowObject) {
        var formattedValue = cellValue.replace(/\r\n/g, "<br/>");
        return formattedValue;
    }
}
;IC.Public.OfferConfirmation = function (options) {

};

IC.Public.OfferConfirmation.prototype = {

    init: function () {
        var btnCancel = $("#btnCancel");
        btnCancel.bind("click", { obj: this }, this.onCancelClick);

        var btnBack = $("#btnBack");
        btnBack.bind("click", { obj: this }, this.onBackClick);

        var btnContinue = $("#btnContinue");
        btnContinue.bind("click", { obj: this }, this.onContinueClick);
        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.Offers);
    },
    onBackClick:function()
    {
        var url = OC.MVC.util.getLink("Offers", "TakeAction");
        OC.MVC.util.loadMainContainerView(url, { offerCode: $("#OfferCode").val(), cacheKey: $("#hidCacheKey").val(), isCaptionRequired: true }); //Cache key is used to restore the values on the Offer actions page to exactly how it was earlier in this transaction.
    },
    onCancelClick:function()
    {
        var url = OC.MVC.util.getLink("Offers", "Index");
        OC.MVC.util.loadMainContainerView(url);
    },
    onContinueClick:function(events)
    {
        var resultArray = [];
        ////To Do
        //if ($('#IsImpersonateUser').val().toLowerCase() == "true") {
        //    alert(IC.Public.Plans.Common.ImperonateUserNoAccessMessage);
        //    return;
        //}
        var self = events.data.obj;
        var url = OC.MVC.util.getLink("Offers", "ConfirmationReceipt");
        IC.Public.showLoading("");

        var form = $("#frmOfferConfirmation");
        
        form.find('.SingleOffer').each(function (i, obj) {
            var offerDetails = $(this).find(':input').serializeObject();
            var item = {};
            for (var key in offerDetails) {
                if (offerDetails.hasOwnProperty(key)) {
                    var modifiedKey = key.replaceAll("confirmation.", "");
                    item[modifiedKey] = offerDetails[key];
                }
            }
            resultArray.push(item);
        });

        var result = { 'OfferActions': resultArray, 'Count': resultArray.length };

        $.post(url, result, function (data) {
            $("#" + OC.MVC.constants.mainContainer).html(data);
            IC.Public.hideLoading();
        });
    }
};IC.Public.OfferConfirmationReceipt = function (options) {

};

IC.Public.OfferConfirmationReceipt.prototype = {

    init: function () {
        var btnContinue = $("#btnContinue");
        btnContinue.bind("click", this.onContinueClick);
        var baseObj = this;

        $(".DownloadOfferReceiptLink").each(function(i, obj) {
            if ($(this).length) {
                $(this).bind("click", baseObj.OnDownloadReceiptLinkClicked);
            }
        });
        
        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.Offers);
    },
    onContinueClick: function () {
        var url = OC.MVC.util.getLink("Offers", "Index");
        OC.MVC.util.loadView(OC.MVC.constants.mainContainer, url);
    },
    OnDownloadReceiptLinkClicked: function (events) {
        var viewKey = (($(this).attr("viewkey") != undefined) ? $(this).attr("viewkey") : "");
        var transactionId = (($(this).attr("transactionid") != undefined) ? $(this).attr("transactionid") : "");
        var offerId = (($(this).attr("offerId") != undefined) ? $(this).attr("offerId") : "");
        var offerStatus = (($(this).attr("offerDisplayStatus") != undefined) ? $(this).attr("offerDisplayStatus") : "");
        var url = OC.MVC.util.getLink("Offers", "GenerateOfferConfirmationReceipt");
        window.location = url + "?viewKey=" + viewKey + "&transactionId=" + transactionId + "&offerId=" + offerId + "&offerDisplayStatus=" + offerStatus;
    }
};IC.Public.OfferDocuments = function(options) {
    if (options) {
        $.extend(this, options);
    }
};

IC.Public.OfferDocuments.prototype = {
    showModal: function (offerId, offerGroupCode, offerStatus, isCaptionRequired) {
        IC.Public.showLoading("");
        OC.MVC.util.showModal('Offers', 'DocumentList', { offerId: offerId, offerGroupCode: offerGroupCode, offerStatus: offerStatus, isCaptionRequired: isCaptionRequired }, { minWidth: 400, width: 400 }, function () {
            IC.Public.hideLoading();
            OC.MVC.util.adjustModalSizeAndPosition();
        });
    }
};

;IC.Public.NewOffers = function (options) {

};

IC.Public.NewOffers.prototype = {

    init: function () {
        var self = this;
        $("#errorContainer").hide();
        $("#btnContinueToHoldings").bind("click", function () {
            self.updateDismissedOffers("btnContinueToHoldings");
        });
        $("#btnGoToOffers").bind("click", function () {
            self.updateDismissedOffers("btnGoToOffers");
        });
    },
    updateDismissedOffers: function (callerID) {
        var self = this;
        var defaultOfferViewKey=null;
        $("input[data-name='DoNotShowMessageCheckBox']").each(function () {
            var checkedState = $(this).attr("checked");
            var targetElementID = $(this).attr("data-targetHiddenElement");
            $("input[name='" + targetElementID + "']").val(checkedState);
            if (checkedState == false && defaultOfferViewKey==null)
            {
                var targetViewKeyElement = $(this).attr("data-targetViewKeyElement");
                defaultOfferViewKey = $("input[name='" + targetViewKeyElement + "']").val();
                $("#Model_ViewKey").val(defaultOfferViewKey);
            }
        });

        var url = OC.MVC.util.getLink("Offers", "UpdateUserDismissedOffers");
        var formData = $("#OpenOffersForm").serializeObject();

        $.post(url, formData, function (result) {
            if (result == false) {
                $("#errorContainer").show();
                $("#errorMessage").html("Sorry, something went wrong while updating your choices. Please try again later.");
            }
            else {
                if (callerID == 'btnContinueToHoldings')
                    $.modal.close();
                else if (callerID == 'btnGoToOffers')
                    self.goToOffers();

                $("#errorContainer").hide();
                $("#errorMessage").html("");
            }
            return result;
        });
    },
    goToOffers: function () {
        var form = $('form#OpenOffersForm');
        var formData = form.serializeObject();
        var url = OC.MVC.util.getLink("Offers", "OpenOffers");
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: formData,
            success: function (result) {
                $.modal.close();
                IC.Public.hideLoading();
                $("#" + OC.MVC.constants.mainContainer).html(result);
            },
            error: function (error) {
                $.modal.close();
            }
        });
    }
};;IC.Public.OutstandingLoan = function (options) {

};

IC.Public.OutstandingLoan.prototype = {

    init: function () {
        var self = this;
        $("#errorContainer").hide();
        $("#btnContinueToHoldings").bind("click", function () {
            self.updateDismissedLoanPlan("btnContinueToHoldings");
        });
        $("#btnGoToLoanRepayment").bind("click", function () {
            self.updateDismissedLoanPlan("btnGoToLoanRepayment");
        });
    },
    updateDismissedLoanPlan: function (callerID) {
        var self = this;
        var defaultOfferViewKey=null;
        $("input[data-name='DoNotShowMessageCheckBox']").each(function () {
            var checkedState = $(this).attr("checked");
            var targetElementID = $(this).attr("data-targetHiddenElement");
            $("input[name='" + targetElementID + "']").val(checkedState);
            if (checkedState == false && defaultOfferViewKey==null)
            {
                var targetViewKeyElement = $(this).attr("data-targetViewKeyElement");
                defaultOfferViewKey = $("input[name='" + targetViewKeyElement + "']").val();
                $("#Model_ViewKey").val(defaultOfferViewKey);
            }
        });

        var url = OC.MVC.util.getLink("Offers", "UpdateUserDismissedLoanPlan");
        var formData = $("#OutstandingLoanPlanForm").serializeObject();

        $.post(url, formData, function (result) {
            if (result == false) {
                $("#errorContainer").show();
                $("#errorMessage").html("Sorry, something went wrong while updating your choices. Please try again later.");
            }
            else {
                if (callerID == 'btnContinueToHoldings')
                    $.modal.close();
                else if (callerID == 'btnGoToLoanRepayment')
                    self.goToLoanRepayment();

                $("#errorContainer").hide();
                $("#errorMessage").html("");
            }
            return result;
        });
    },
    goToLoanRepayment: function () {
        var form = $('form#OutstandingLoanPlanForm');
        var formData = form.serializeObject();
        var url = OC.MVC.util.getLink("Offers", "OpenLoanRepayment");
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: formData,
            success: function (result) {
                $.modal.close();
                IC.Public.hideLoading();
                $("#" + OC.MVC.constants.mainContainer).html(result);
            },
            error: function (error) {
                $.modal.close();
            }
        });
    }
};;String.prototype.tooltipReplace = function(currency, lastPrice, type, value, number) {
    return this.replace(/\[\[TYPE\]\]/g, type).replace(/\[\[CURRENCY\]\]/g, currency).replace(/\[\[VALUE\]\]/g, value).replace(/\[\[NUMBER\]\]/g, number).replace(/\[\[PRICE\]\]/g, lastPrice);
};

String.prototype.tooltipReplaceWithConversion = function (origcurrency, origlastPrice, origvalue, rate, ratedate) {
    return this.replace(/\[\[ORIGCURRENCY\]\]/g, origcurrency).replace(/\[\[ORIGVALUE\]\]/g, origvalue).replace(/\[\[RATE\]\]/g, rate).replace(/\[\[RATEDATE\]\]/g, ratedate).replace(/\[\[ORIGPRICE\]\]/g, origlastPrice);
};

IC.Public.Plans.plansummarygrid = function (hidePagingDiv, isSplitGrid, gridContainerId) {
    this.gridId = "PlanSummaryGrid"; // default
    if (gridContainerId != null && gridContainerId.length) {
        this.gridId = gridContainerId;
    }
    this.planSummaryGrid = $('#' + this.gridId);
    this.gview_planSummaryGrid = $('#gview_' + this.gridId);
    this.planSummaryGridTotals = $('#' + this.gridId + 'Totals');
    this.hidePagingDiv = hidePagingDiv;
    this.isSplitGrid = isSplitGrid;
    this.init();
};
IC.Public.Plans.LoanHistoryGrid = {
    RepaymentAmountFormat: function (cellValue, options, rowObject) {

        var span = $('<span>');
        var index = $(this).getColIndex("RepaymentReasonCode");
        span.html(cellValue);
        if (index >= 0) {
            if (["CHQ", "SHS", "DIV"].indexOf(rowObject[index]) != -1)
                span.html("(" + cellValue + ")");

        }
            
        return span.html();
    },
    LoanBalanceFormat: function (cellValue, options, rowObject) {

        var span = $('<span>');
        var balanceValue = parseInt(cellValue);
        span.html(cellValue);
        if (balanceValue < 0)
            span.html("(" + cellValue + ")");
        return span.html();
    },
};
IC.Public.Plans.plansummarygrid.prototype = {
    init: function () {
        this.gridCompleteEvent();
        
        $('#vestingCalculatorDiv').hide();
        
        this.downloadStatement = $('#btnDownloadStatement');
        this.downloadStatement.unbind('click');
        this.downloadStatement.bind('click', { obj: this }, this.onDownStatementClick);
    },
    getTitle: function (index) {
        var planSummaryGridDiv = $('#' + this.gridId + 'Div');
        var planSummarySection = $('.planSummarySection');

        var title = planSummaryGridDiv.find('table').find('th').eq(index).find('div');
        if (title == null || title.length == 0) {
            // try the other container
            title = planSummarySection.find('table').find('th').eq(index).find('div');
        }
        return title;
    },
    setGridColumnCurrencyCodes: function (currencyCode, isCurrencyConversionApplied) {
        
        currencyCode = (!isCurrencyConversionApplied) ? $('#hidCurrency').val() : currencyCode;
        if (currencyCode != "-1") {
            $('#lastClosePriceColumnCurrency').text("(" + currencyCode + ")");
            $('#valueColumnCurrency').text("(" + currencyCode + ")");
        }
    },
    gridCompleteEvent: function () {
        
        var self = this;
        
        //for HoldingGrid
        this.holdingsListGrid = $("#MyHoldingsGrid");
        this.holdingsListGrid.bind("grid_gridComplete", { obj: this }, function (events, gridContext) {
            
            var _obj = events.data.obj;
            var isCurrencyConversionApplied = IC.Public.Currency.isCurrencyConversionApplied();
            var conversionCurrency = $('#conversionCurrency').val();
            _obj.setGridColumnCurrencyCodes(conversionCurrency, isCurrencyConversionApplied);
            $("A.cluetooltip").each(function () {
                $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 310 });
            });
        });

        //////////////////////////////////////////////////////


        var planSummaryGridTotals = this.planSummaryGridTotals;
        this.planSummaryGrid.bind("grid_gridComplete", { obj: this }, function (events, gridContext) {
            //$('#PlanSummaryGrid_pager_right').hide(); // hide the pager

            // get the values from returning json model
            var isConverted = $(this).getUserDataItem('IsConverted') === 'True';
            var currency = $(this).getUserDataItem('Currency');
            var lastPrice = $(this).getUserDataItem('LastPrice');
            var origCurrency = $(this).getUserDataItem('OriginalCurrency');
            var origLastPrice = $(this).getUserDataItem('OriginalPrice');
            var fxrate = $(this).getUserDataItem('FxRate');
            var ratedate = $(this).getUserDataItem('RateDate');

            var totalVested = $(this).getUserDataItem('TotalVested');
            var totalUnVested = $(this).getUserDataItem('TotalUnVested');
            var totalRestricted = $(this).getUserDataItem('TotalRestricted');
            var totalReleased = $(this).getUserDataItem('TotalReleased');
            var totalTotalInPlan = $(this).getUserDataItem('TotalTotalInPlan');
            var totalSellTransfer = $(this).getUserDataItem('TotalSellTransfer');
            var totalVestedValue = $(this).getUserDataItem('TotalVestedValue');
            var totalUnVestedValue = $(this).getUserDataItem('TotalUnVestedValue');
            var totalRestrictedValue = $(this).getUserDataItem('TotalRestrictedValue');
            var totalReleasedValue = $(this).getUserDataItem('TotalReleasedValue');
            var totalTotalInPlanValue = $(this).getUserDataItem('TotalTotalInPlanValue');
            var totalSellTransferValue = $(this).getUserDataItem('TotalSellTransferValue');
            var origTotalVestedValue = $(this).getUserDataItem('OrigTotalVestedValue');
            var origTotalUnVestedValue = $(this).getUserDataItem('OrigTotalUnVestedValue');
            var origTotalRestrictedValue = $(this).getUserDataItem('OrigTotalRestrictedValue');
            var origTotalReleasedValue = $(this).getUserDataItem('OrigTotalReleasedValue');
            var origTotalTotalInPlanValue = $(this).getUserDataItem('OrigTotalTotalInPlanValue');
            var origTotalSellTransferValue = $(this).getUserDataItem('OrigTotalSellTransferValue');
            var currencyConversionFailed = $(this).getUserDataItem('CurrencyConversionFailed');
            var rightsPlanNameLiteral = $(this).getUserDataItem('RightsPlanNameLiteral');
            var grantViewEnabled = $(this).getUserDataItem('GrantViewEnabled');
            
            if (currencyConversionFailed) {
                IC.Public.Currency.displayErrorMessage($('#hidCurrency').val(), $('#hidCurrencyConversionErrorRateNotFound').val(), $('#conversionCurrency'));
            }

            var planNameColumnIndex = 0;
            var grantDescriptionColumnIndex = 1;
            var unvestedColumnIndex = 2;
            var vestedColumnIndex = 3;
            var restrictedColumnIndex = 4;
            var releasedColumnIndex = 5;
            var totalInPlanColumnIndex = 6;
            var sellOrTrtansferColumnIndex = 7;

            var noSellTransfer = $(self.gview_planSummaryGrid.find('th').eq(sellOrTrtansferColumnIndex)).css('display') == 'none';
            var isRestrictedColumnHidden = $(self.gview_planSummaryGrid.find('th').eq(restrictedColumnIndex)).css('display') == 'none';
            var isReleasedColumnHidden = $(self.gview_planSummaryGrid.find('th').eq(releasedColumnIndex)).css('display') == 'none';

            // set up the totals table
            var totalRow = planSummaryGridTotals.find('tr').eq(0);
            
            if (totalRow.length) {
                var firstColumnWidth = (grantViewEnabled) ? $(self.planSummaryGrid.find('td').eq(grantDescriptionColumnIndex)).width() : $(self.planSummaryGrid.find('td').eq(planNameColumnIndex)).width();
                totalRow.find('td').eq(0).attr("style", "width:" + firstColumnWidth + "px");

                totalRow.find('td').eq(1).text(totalUnVested);
                totalRow.find('td').eq(1).attr("style", "width:" + $(self.planSummaryGrid.find('td').eq(unvestedColumnIndex)).width() + "px");

                totalRow.find('td').eq(2).text(totalVested);
                totalRow.find('td').eq(2).attr("style", "width:" + $(self.planSummaryGrid.find('td').eq(vestedColumnIndex)).width() + "px");

                if (!isRestrictedColumnHidden){
                    totalRow.find('td').eq(3).text(totalRestricted);
                    totalRow.find('td').eq(3).attr("style", "width:" + $(self.planSummaryGrid.find('td').eq(restrictedColumnIndex)).width() + "px");
                } else {
                    if (self.isSplitGrid) {
                        // Need to check whether the main grid's column is not hidden
                        if ($('#PlanSummaryGridTotals').find('tr').eq(0).find('td').eq(3).is(':visible')) {
                            totalRow.find('td').eq(3).attr("style", "width:" + $(self.planSummaryGrid.find('td').eq(restrictedColumnIndex)).width() + "px");
                            totalRow.find('td').eq(3).text(totalRestricted);
                        } else{
                            totalRow.find('td').eq(3).hide();
                        }
                            
                    } else {
                        // Need to check whether the split grid's column is not hidden
                        if ($('#PlanSummarySplitGridTotals').find('tr').eq(0).find('td').eq(3).is(':visible')) {
                            totalRow.find('td').eq(3).attr("style", "width:" + $(self.planSummaryGrid.find('td').eq(restrictedColumnIndex)).width() + "px");
                            totalRow.find('td').eq(3).text(totalRestricted);
                        } else {
                            totalRow.find('td').eq(3).hide();
                        }
                    }
                }

                if (isReleasedColumnHidden) {
                    totalRow.find('td').eq(4).hide();
                } else {
                    totalRow.find('td').eq(4).text(totalReleased);
                    totalRow.find('td').eq(4).attr("style", "width:" + $(self.planSummaryGrid.find('td').eq(releasedColumnIndex)).width() + "px");
                }
                
                totalRow.find('td').eq(5).text(totalTotalInPlan);
                totalRow.find('td').eq(5).attr("style", "width:" + $(self.planSummaryGrid.find('td').eq(totalInPlanColumnIndex)).width() + "px");

                if (noSellTransfer) {
                    totalRow.find('td').eq(6).hide();
                } else {
                    totalRow.find('td').eq(6).text(totalSellTransfer);
                    totalRow.find('td').eq(6).attr("style", "width:" + $(self.gview_planSummaryGrid.find('th').eq(sellOrTrtansferColumnIndex)).width() + "px");
                }
            }
            var totalValueRow = planSummaryGridTotals.find('tr').eq(1);
            if (totalValueRow.length) {
                // build up the value total based upon table columns data
                var col = 1;
                var title = self.getTitle(col+1);
                totalValueRow.find('td').eq(col).find('a').text(totalUnVestedValue);
                var tooltip = totalValueRow.find('td').eq(col).find('div');
                tooltip.html(tooltip.html().tooltipReplace(currency, lastPrice, title.text(), totalUnVestedValue, totalUnVested));
                if (isConverted) {
                    tooltip.html(tooltip.html().tooltipReplaceWithConversion(origCurrency, origLastPrice, origTotalUnVestedValue, fxrate, ratedate));
                }
                col = 2;
                title = self.getTitle(col + 1);
                totalValueRow.find('td').eq(col).find('a').text(totalVestedValue);
                tooltip = totalValueRow.find('td').eq(col).find('div');
                tooltip.html(tooltip.html().tooltipReplace(currency, lastPrice, title.text(), totalVestedValue, totalVested));
                if (isConverted) {
                    tooltip.html(tooltip.html().tooltipReplaceWithConversion(origCurrency, origLastPrice, origTotalVestedValue, fxrate, ratedate));
                }
                col = 3;
                title = self.getTitle(col + 1);
                if (!isRestrictedColumnHidden) {
                    totalValueRow.find('td').eq(col).find('a').text(totalRestrictedValue);
                    tooltip = totalValueRow.find('td').eq(col).find('div');
                    tooltip.html(tooltip.html().tooltipReplace(currency, lastPrice, title.text(), totalRestrictedValue, totalRestricted));
                    if (isConverted) {
                        tooltip.html(tooltip.html().tooltipReplaceWithConversion(origCurrency, origLastPrice, origTotalRestrictedValue, fxrate, ratedate));
                    }
                } else {
                    totalValueRow.find('td').eq(col).hide();
                }

                col = 4;
                title = self.getTitle(col + 1);
                if (isReleasedColumnHidden) {
                    totalValueRow.find('td').eq(col).hide();
                } else {
                    totalValueRow.find('td').eq(col).find('a').text(totalReleasedValue);
                    tooltip = totalValueRow.find('td').eq(col).find('div');
                    tooltip.html(tooltip.html().tooltipReplace(currency, lastPrice, title.text(), totalReleasedValue, totalReleased));
                    if (isConverted) {
                        tooltip.html(tooltip.html().tooltipReplaceWithConversion(origCurrency, origLastPrice, origTotalReleasedValue, fxrate, ratedate));
                    }
                }
                col = 5;
                title = self.getTitle(col + 1);
                totalValueRow.find('td').eq(col).find('a').text(totalTotalInPlanValue);
                tooltip = totalValueRow.find('td').eq(col).find('div');
                tooltip.html(tooltip.html().tooltipReplace(currency, lastPrice, title.text(), totalTotalInPlanValue, totalTotalInPlan));
                if (isConverted) {
                    tooltip.html(tooltip.html().tooltipReplaceWithConversion(origCurrency, origLastPrice, origTotalTotalInPlanValue, fxrate, ratedate));
                }

                col = 6;
                title = self.getTitle(col + 1);
                if (noSellTransfer) {
                    totalValueRow.find('td').eq(col).hide();
                } else {
                    totalValueRow.find('td').eq(col).find('a').text(totalSellTransferValue);
                    tooltip = totalValueRow.find('td').eq(col).find('div');
                    tooltip.html(tooltip.html().tooltipReplace(currency, lastPrice, title.text(), totalSellTransferValue, totalSellTransfer));
                    if (isConverted) {
                        tooltip.html(tooltip.html().tooltipReplaceWithConversion(origCurrency, origLastPrice, origTotalSellTransferValue, fxrate, ratedate));
                    }
                }

                // cluetip initialisation
                var positionBy = isConverted ? 'bottomTop' : 'mouse';
                totalValueRow.find('a').each(function () {
                    $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 280, positionBy: positionBy });
                });
            }

            self.planSummaryGrid.find('.actionLink').each(function () {
                
                $(this).unbind('click');
                $(this).bind('click', { viewKey: $(this).parent().parent('tr').getColumnValue('PlanViewKey') }, self.onActionClick);
              
            });

            
            self.planSummaryGrid.find('#PlanNameId').each(function () {
               
                $(this).unbind('click');
                $(this).bind('click', { viewKey: $(this).attr('viewKey') }, self.onActionLinkPlanClick);
                var planNames = $(this).attr('viewKey');
               
                planNames=planNames.split(",").pop(-1)
                var array = planNames.split('--')
                planNames = array[0];
                $(this)[0].innerHTML = planNames;
                $("#PlanSummaryGrid tr").addClass("ui-widget-content jqgrow ui-row-ltr");
                $("#PlanSummarySplitGrid tr").addClass("ui-widget-content jqgrow ui-row-ltr");
                

            });

            if (gridContext > 0)
                planSummaryGridTotals.show();

            if (self.isSplitGrid && gridContext == 0)
                $('#PlanSummarySplitGridDiv').hide();
            if (self.isSplitGrid) {
                $('#PlanSummarySplitGridDiv').find('#planNameSpan').text(rightsPlanNameLiteral);
            }
            
            if (self.hidePagingDiv)
                $('#' + self.gridId + '_pager').hide();
        });
    },
    onActionClick: function (events) {
       
        var viewKey = events.data.viewKey;
        var url = OC.MVC.util.getLink("Summary", "PlanDetail");
        var data = { viewKey: viewKey };
        OC.MVC.util.loadMainContainerView(url, data);
    },

    onActionLinkPlanClick: function (events) {
        var val = events.data.viewKey;
        val = val.split("viewKey").pop(-1).split(",").pop(-1);
        var array = val.split('--');
        val = array[0];

        var viewKey = events.data.viewKey;
        viewKey = viewKey.replace(val + '--', '');
        var url = OC.MVC.util.getLink("Summary", "PlanDetail");
        var data = { viewKey: viewKey };
        OC.MVC.util.loadMainContainerView(url, data);
    },
    
    onDownStatementClick: function (eventArgs) {
        IC.Public.showLoading("");
        var modal = new IC.Public.Plans.equityStatementModal();
        modal.showModal();
    }
};;IC.Public.Plans.plandetailgrid = function () {
    this.planDetailGrid = $('#planDetailGridDiv');
    this.DateFrom = $('#DateFrom');
    this.DateTo = $('#DateTo');
    this.init();
};

IC.Public.Plans.plandetailgrid.prototype = {
    init: function () {
        var self = this;
        var selectList = self.planDetailGrid.find('select#SelectedPlanYear');
        this.DateFrom.datepicker('option', { dateFormat: 'dd/mm/yy' });
        this.DateTo.datepicker('option', { dateFormat: 'dd/mm/yy' });
        if (selectList.length) {
            self.setOnSelect(self, false);
        }
        
        this.registerEvents();
        this.isConverted = $("#IsConverted").val();
        this.convCurrency = $('#conversionCurrency').val();

        self.setGridColumnCurrencyCodes(self.convCurrency, self.isConverted);

        this.currencyDiv = $('#currencyDiv');
        this.currencyDiv.hide();
        
        this.gridCompleteEvent();
        this.performanceForm = $('#performanceGridForm');
        this.reportErrorMessage = "The report you have requested cannot be located at this time.";
    },
    
    registerEvents: function () {
        var self = this;
        var btnGo = this.planDetailGrid.find('#btnGo');
        if (btnGo.length) {
            btnGo.bind('click', { obj: self }, self.onGoClick);
        }

        var selectList = this.planDetailGrid.find('select#SelectedPlanYear');
        if (selectList.length) {
            selectList.bind('change', { obj: self }, self.onSelectChanged);
        }
        this.btnHideZeroBalances = $("#btnHideZeroBalances");
        this.btnHideZeroBalances.bind("click", { obj: this }, this.onHideZeroBalancesClick);
        this.btnHideZeroBalances.hide();

        this.btnShowZeroBalances = $("#btnShowZeroBalances");
        this.btnShowZeroBalances.bind("click", { obj: this }, this.onShowZeroBalancesClick);
        this.btnShowZeroBalances.hide();

        var gridType = $('#planSummaryContainer').find('.displayLinks a.selected').attr('gridtype');
        if (gridType == 4) {
            //Restricted Securities tab
            var hasZeroBalanceHoldings = $('#HasZeroBalanceHoldings').val().toString().toLowerCase() == "true";
            if (hasZeroBalanceHoldings)
                self.btnHideZeroBalances.show();
        }
        
    },
    gridCompleteEvent: function () {
        var self = this;
        this.planDetailGrid.bind("grid_gridComplete", { obj: this }, function (events, gridContext) {
            
            var viewKey = $('#PlanViewKey');
            self.planDetailGrid.find('.actionLink').each(function () {
                $(this).unbind('click');
                $(this).bind('click', { url: $(this).attr('actionurl'), viewKey: $(this).attr('viewKey'), ruleId: $(this).attr('ruleId') }, self.onActionClick);
            });

            self.planDetailGrid.find('.scheduleLink').each(function () {
                $(this).unbind('click');
                $(this).bind('click', { url: $(this).attr('actionurl'), viewKey: $(this).attr('viewKey'), isModal: true }, self.onActionClick);
            });

            self.planDetailGrid.find('.cluetooltip').each(function () {
                var width = $(this).hasClass('wide') ? 280 : 210;
                $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: width, positionBy: 'mouse' });
            });

            $(this).find("a.performance-chart-link").unbind("click");
            $(this).find("a.performance-chart-link").bind("click", { obj: self }, self.onPerforamnceChartClick);

            $(this).find("a.performance-report-link").unbind("click");
            $(this).find("a.performance-report-link").bind("click", { obj: self }, self.onDownloadReportLinkClick);
            
            // START - PERFORMANCE TRACKER
            var performanceGrid = $('#gview_PerformanceGrid');
            if (performanceGrid.length > 0) {
                var performanceGridControl = $(this).find('#PerformanceGrid');
                var isConverted = performanceGridControl.getUserDataItem('IsConverted') === 'True';
                var currency = performanceGridControl.getUserDataItem('Currency');
                var lastPrice = performanceGridControl.getUserDataItem('LastPrice');
                var origCurrency = performanceGridControl.getUserDataItem('OriginalCurrency');
                var origLastPrice = performanceGridControl.getUserDataItem('OriginalPrice');
                var fxrate = performanceGridControl.getUserDataItem('FxRate');
                var ratedate = performanceGridControl.getUserDataItem('RateDate');

                var currencyConversionFailed = performanceGridControl.getUserDataItem('CurrencyConversionFailed');
                if (currencyConversionFailed) {
                    IC.Public.Currency.displayErrorMessage($('#hdnCurrency').val(), $('#hidCurrencyConversionErrorRateNotFound').val(), $('#conversionCurrency'));
                }

                var totalIndicativeVestingAmount = performanceGridControl.getUserDataItem('TotalIndicativeVestingAmount');
                var totalIndicativeVestingValue = performanceGridControl.getUserDataItem('TotalIndicativeVestingValue');
                var originalTotalIndicativeVestingValue = performanceGridControl.getUserDataItem('OriginalTotalIndicativeVestingValue');

                var isPlanNameHidden = $(performanceGrid.find('th').eq(0)).css('display') == 'none';
                var isDescriptionHidden = $(performanceGrid.find('th').eq(1)).css('display') == 'none';
                var isGrantDateHidden = $(performanceGrid.find('th').eq(2)).css('display') == 'none';
                var isPercentageVestedHidden = $(performanceGrid.find('th').eq(10)).css('display') == 'none';
                var isCommencementDateHidden = $(performanceGrid.find('th').eq(3)).css('display') == 'none';

                var performanceGridTotals = $('#performanceGridTotals');
                var totalRow = performanceGridTotals.find('tr').eq(0);
                var dummyRow = performanceGridTotals.find('tr').eq(2);

                if (dummyRow.length) {
                    dummyRow.find('td').eq(0).attr("style", "width:" + $(performanceGrid.find('th').eq(0)).width() + "px");
                    dummyRow.find('td').eq(1).attr("style", "width:" + $(performanceGrid.find('th').eq(1)).width() + "px");
                    dummyRow.find('td').eq(2).attr("style", "width:" + $(performanceGrid.find('th').eq(2)).width() + "px");
                    dummyRow.find('td').eq(3).attr("style", "width:" + $(performanceGrid.find('th').eq(3)).width() + "px");
                    dummyRow.find('td').eq(4).attr("style", "width:" + $(performanceGrid.find('th').eq(4)).width() + "px");
                    dummyRow.find('td').eq(5).attr("style", "width:" + $(performanceGrid.find('th').eq(5)).width() + "px");
                    dummyRow.find('td').eq(6).attr("style", "width:" + $(performanceGrid.find('th').eq(6)).width() + "px");
                    dummyRow.find('td').eq(7).attr("style", "width:" + $(performanceGrid.find('th').eq(7)).width() + "px");
                    dummyRow.find('td').eq(8).attr("style", "width:" + $(performanceGrid.find('th').eq(8)).width() + "px");
                    dummyRow.find('td').eq(9).attr("style", "width:" + $(performanceGrid.find('th').eq(9)).width() + "px");
                    dummyRow.find('td').eq(10).attr("style", "width:" + $(performanceGrid.find('th').eq(10)).width() + "px");
                    dummyRow.find('td').eq(11).attr("style", "width:" + $(performanceGrid.find('th').eq(11)).width() + "px");
                    dummyRow.find('td').eq(12).attr("style", "width:" + $(performanceGrid.find('th').eq(12)).width() + "px");

                    if (isPlanNameHidden)
                        dummyRow.find('td').eq(0).hide();
                    if (isDescriptionHidden)
                        dummyRow.find('td').eq(1).hide();
                    if (isCommencementDateHidden)
                        dummyRow.find('td').eq(3).hide();
                    if (isGrantDateHidden)
                        dummyRow.find('td').eq(2).hide();
                    if (isPercentageVestedHidden)
                        dummyRow.find('td').eq(10).hide();
                }

                if (totalRow.length) {
                    totalRow.find('td').eq(7).text(totalIndicativeVestingAmount);

                    if (isDescriptionHidden)
                        totalRow.find('td').eq(1).hide();

                    if (isPlanNameHidden)
                        totalRow.find('td').eq(2).hide();

                    if (isCommencementDateHidden || isGrantDateHidden)
                        totalRow.find('td').eq(3).hide();
                }

                var totalValueRow = performanceGridTotals.find('tr').eq(1);
                if (totalValueRow.length) {
                    totalValueRow.find('td').eq(7).find('a').text(totalIndicativeVestingValue);
                    var tooltip = totalValueRow.find('td').eq(7).find('div');
                    tooltip.html(tooltip.html().tooltipReplace(currency, lastPrice, '', totalIndicativeVestingValue, totalIndicativeVestingAmount));
                    if (isConverted) {
                        tooltip.html(tooltip.html().tooltipReplaceWithConversion(origCurrency, origLastPrice, originalTotalIndicativeVestingValue, fxrate, ratedate));
                    }

                    // cluetip initialisation
                    totalValueRow.find('a').each(function() {
                        $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 280, positionBy: 'fixed', topOffset: -60, leftOffset: 70 });
                    });

                    if (isDescriptionHidden)
                        totalValueRow.find('td').eq(1).hide();

                    if (isPlanNameHidden)
                        totalValueRow.find('td').eq(2).hide();

                    if (isCommencementDateHidden || isGrantDateHidden)
                        totalValueRow.find('td').eq(3).hide();
                }

                performanceGridTotals.show();
                $("A.cluetooltip").each(function() {
                    if ($(this).hasClass('latestTestResult'))
                        $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 175 });
                });
            }
            // END - PERFORMANCE TRACKER
        });
    },
    onActionClick: function (events) {
        var viewKey = events.data.viewKey;
        var url = events.data.url;
        var ruleId = events.data.ruleId;
        var isModal = events.data.isModal;
        var data = { viewKey: viewKey };
        if (url.length) {
            if (ruleId && ruleId.length) {
                data = { viewKey: viewKey, ruleId: ruleId };
                OC.MVC.util.showModalByUrl(url, data, { minHeight: 150, maxHeight: 400, maxWidth: 500, minWidth: 300 });
            } else if (isModal) {
                data = { PlanViewKey: viewKey, IsModal: true };
                OC.MVC.util.showModalByUrl(url, data, { minHeight: 300, minWidth: 700 }, function () {
                    $("#simplemodal-container").addClass("vestingschedulemodalwrap");
                });
            } else {
                OC.MVC.util.loadMainContainerView(url, data);
            }
        }
        return false;
    },
    onPerforamnceChartClick: function (events) {
        var self = events.data.obj;
        IC.Public.removeErrorFor(undefined, self.performanceForm, self.reportErrorMessage);

        var grantId = $(this).attr('data-grant-id');
        var modal = new IC.Public.Plans.performanceChartModal();
        modal.showModal(grantId);
    },
    onDownloadReportLinkClick: function (events) {
        IC.Public.showLoading();
        var grantId = $(this).attr('data-grant-id');

        var self = events.data.obj;
        IC.Public.removeErrorFor(undefined, self.performanceForm, self.reportErrorMessage);
        
        $.ajax({
            url: OC.MVC.util.getLink("Performance", "IsTsrReportExists"),
            type: 'GET',
            data: { grantId: grantId },
            success: function (result) {
                IC.Public.hideLoading();
                if (result && result.Exists) {
                    var url = OC.MVC.util.getLink("Performance", "DownloadTsrReport");
                    window.location = url + "?grantId=" + grantId;
                } else {
                    IC.Public.displayErrorFor(undefined, self.performanceForm, self.reportErrorMessage);
                }
            },
            error: function (err) {
                IC.Public.hideLoading();
                IC.Public.displayErrorFor(undefined, self.performanceForm, self.reportErrorMessage);
            }
        });
    },
    resetPlanYear: function (self) {
        var selectList = self.planDetailGrid.find('select#SelectedPlanYear');
        if (selectList.length) {
            selectList.val('-1');
        }
    },
    onSelectChanged: function (events) {
        var _obj = events.data.obj;
        _obj.planDetailGrid.find('input.hasDatepicker').each(function (index) {
            if ($(this).val().length > 0) {
                // prevent the select plan year event from firing by unbinding the onselect event
                _obj.setOnSelect(_obj, true);
                $(this).val('');
                $.datepicker._clearDate($(this));
                // rebind on select event
                _obj.setOnSelect(_obj, false);
            }
            $(this).removeClass('input-validation-error');
        });
    },
    onGoClick: function (events) {
        
        var _obj = events.data.obj;
        var form = $("form#planDetailsForm");
        if (_obj.DateFrom.val().length) {
            _obj.DateFrom.dateValidate({ mask: "dd/mm/yy", name: "From", allowFuture: true,allowPast:true });
           // if (_obj.DateFrom.hasClass('error')) { return false; }
        }
        if (_obj.DateTo.val().length) {
            _obj.DateTo.dateValidate({ mask: "dd/mm/yy", name: "To", allowFuture: true, allowPast: true });
           // if (_obj.DateTo.hasClass('error')) { return false; }
        }

        if (_obj.DateTo.val().length && _obj.DateFrom.val().length) {
            var dateTo = $.datepicker.parseDate("dd/mm/yy", _obj.DateTo.val());
            var dateFrom = $.datepicker.parseDate("dd/mm/yy", _obj.DateFrom.val());
            var dateError = '"To" Date is not allowed to be earlier than "From" Date.';
            IC.Public.removeErrorFor(_obj.DateTo, form, dateError);

            if (dateTo < dateFrom) {
                IC.Public.displayErrorFor(_obj.DateTo, form, dateError);
                return false;
            }
            var currentDate = Date.now();
            var dateError = 'To date must be equal or less than today’s date.';
            if (dateTo > currentDate) {
                IC.Public.displayErrorFor(_obj.DateTo, form, dateError);
                return false;
            }
        }
        var url = $('#planSummaryContainer').find('.displayLinks a.selected').attr('actionurl');
        var gridType = $('#planSummaryContainer').find('.displayLinks a.selected').attr('gridtype');

        var data = form.serialize();
        form.validate();
        if (form.valid()) {
            IC.Public.Plans.plandetailPostAjax("planDetailGridDiv", gridType, url, data);
        }
        return false;
    },
    setOnSelect: function (self, reset) {
        if (reset) {
            self.DateFrom.datepicker('option', { onSelect: null });
            self.DateTo.datepicker('option', { onSelect: null });
        } else {
            self.DateFrom.datepicker('option', { onSelect: function () { self.resetPlanYear(self); } });
            self.DateTo.datepicker('option', { onSelect: function () { self.resetPlanYear(self); } });
        }
    },
    onHideZeroBalancesClick: function (events) {
        var _obj = events.data.obj;
        document.cookie = "hideZeroBalances=true;";
        var gridType = $('#planSummaryContainer').find('.displayLinks a.selected').attr('gridtype');
        $('#PlanDetailsGrid').setPostData({ isHideZeroBalances: true, GridType: gridType });
        $('#PlanDetailsGrid').trigger("reloadGrid", [{ page: 1 }]);
        _obj.btnShowZeroBalances.show();
        $(this).hide();
    },
    onShowZeroBalancesClick: function (events) {
        var _obj = events.data.obj;
        document.cookie = "hideZeroBalances=false;";
        var gridType = $('#planSummaryContainer').find('.displayLinks a.selected').attr('gridtype');
        $('#PlanDetailsGrid').setPostData({ isHideZeroBalances: false, GridType: gridType });
        $('#PlanDetailsGrid').trigger("reloadGrid", [{ page: 1 }]);
        _obj.btnHideZeroBalances.show();
        $(this).hide();
    },
    setGridColumnCurrencyCodes: function (currencyCode, isCurrencyConversionApplied) {
        currencyCode = (!isCurrencyConversionApplied) ? $('#hdnCurrency').val() : currencyCode;
        if (currencyCode != "-1") {
            $('#planSummaryContainer').find('#valueColumnCurrency').text("(" + currencyCode + ")");
        }
    },
    TaxingEventTooltip: function (cellValue, options, rowObject) {
        var div = $('<div>');
        var span = $('<span>');
        span.attr("title", rowObject[3]);
        span.addClass("tooltip");
        span.html(rowObject[3]);
        span.appendTo(div);
        return div.html();
    }
};
;IC.Public.Plans.planoverview = function (overviewType, exercisesCount, isConfigEnabledForExercises, offersCount, isConfigEnabledForOffers, isShowFPOTable) {
    this.OVERVIEWTYPE_PLANSUMMARY = 0; // PlanOverviewType.PlanSummary
    this.overviewType = overviewType;
    this.exercisesCount = exercisesCount;
    this.isConfigEnabledForExercises = isConfigEnabledForExercises;
    this.offersCount = offersCount;
    this.isConfigEnabledForOffers = isConfigEnabledForOffers;
    this.isShowFPOTable = isShowFPOTable;
    this.init();
};

IC.Public.Plans.planoverview.prototype = {
    init: function () {
        var self = this;
        

        if (self.overviewType == "PlanSummary") {
            // If some exercises present or some offers present, display the Overview section
            $("#planSummaryOverviewContainer").hide();
            if (self.isConfigEnabledForExercises && self.exercisesCount > 0)
            {
                $("#planSummaryOverviewContainer").show();
            }
            if (self.isConfigEnabledForOffers && self.offersCount > 0)
            {
                $("#planSummaryOverviewContainer").show();
            }
            if (self.isShowFPOTable == "False")
            {
                $("#planSummaryOverviewContainer").show();
            }
            
        }
        else {
            $("#planSummaryOverviewContainer").show();
        }

    }
};;IC.Public.Plans.plandocumentsgrid = function() {
    this.init();
};

IC.Public.Plans.plandocumentsgrid.prototype = {    
    init: function() {
        $('#planDropDownDiv').show();
        $('#vestingCalculatorDiv').hide();
        
        this.plansDropDown = $('#Plan');
        this.plansDropDown.val("-1");
        this.plansDropDown.unbind('change');
        this.plansDropDown.bind('change', { obj: this }, this.onPlansDropDownChange);
        this.documentsGrid = $('#PlanDocumentsGrid');
    },
    
    onPlansDropDownChange: function (events) {
        var self = events.data.obj;
        
        self.documentsGrid.appendPostData({ planCode: $(this).val() });
        self.documentsGrid.setGridParam({ page: 1 });
        self.documentsGrid.trigger('reloadGrid');
    }
};;IC.Public.Plans.vestingcalculatorgrid = function () {
    this.init();
};

IC.Public.Plans.vestingcalculatorgrid.prototype = {
    init: function () {
        this.securityPrice = $('#SecurityPrice');
        this.vestingPercentage = $('#VestingPercentage');
        this.applyLatestPerformanceResult = $('#ApplyLatestPerformanceResult');
        this.applyLatestPerformanceResult.attr('checked', false);
        this.securityPriceCurrency = $('#securityPriceCurrency');
        this.registerEvents();

        $('#vestingCalculatorDiv').show();
        $('#planDropDownDiv').hide();
        $('input[name=DateFilter][value=1]').attr('checked', 'checked');
        $('#VestingType').val("-1");
        $('#VestingPercentage').val("100");
        // reset when coming back to this tab
        this.onDateFilterClick();
       
        var conversionCurrency = $('#conversionCurrency').val();
       
        if (conversionCurrency != "-1") {
            this.securityPriceCurrency.text(conversionCurrency);
            $('#estimatedValueCurrency').text('(' + conversionCurrency + ')');
        }
        
        this.securityPrice.val($('#LatestSecurityPrice').val());
    },
    
    registerEvents: function (resetDates) {
        this.vestingCalculatorGrid = $('#VestingCalculatorGrid');
        this.gview_VestingCalculatorGrid = $('#gview_VestingCalculatorGrid');
        
        this.dateFrom = $('#DateFrom');
        this.dateTo = $('#DateTo');
        this.dateFrom.datepicker('option', { dateFormat: 'dd/mm/yy', minDate: new Date(), yearRange: 'c:c+20' });
        this.dateTo.datepicker('option', { dateFormat: 'dd/mm/yy', minDate: new Date(), yearRange: 'c:c+20' });
        this.dateFrom.val('');
        this.dateTo.val('');

        this.dateFilterTypeRadio = $("[name=DateFilter]");
        this.dateFilterTypeRadio.unbind('click');
        this.dateFilterTypeRadio.bind("click", { obj: this }, this.onDateFilterClick);

        // Commented as keypress event is no more required as per TRO- 895 changes.
        //this.securityPrice.unbind('keyup');
        //this.securityPrice.bind('keyup', { obj: this }, this.onSecurityPriceChange);
        //this.securityPrice.numeric();
        

        this.securityPrice.unbind('keypress');
        this.securityPrice.bind('keypress', { obj: this }, this.onSecurityPriceChange1);
        
        //this.vestingPercentage.unbind('keyup');
        //this.vestingPercentage.bind('keyup', { obj: this }, this.onVestingPercentageChange);

        this.applyLatestPerformanceResult.unbind('change');
        this.applyLatestPerformanceResult.bind('change', { obj: this }, this.onApplyLatestPerformanceResultChange);
        
        this.goButton = $('#btnGo');
        this.goButton.unbind('click');
        this.goButton.bind('click', { obj: this }, this.onGoButtonClick);

        this.resetButton = $('#btnReset');
        this.resetButton.unbind('click');
        this.resetButton.bind('click', { obj: this }, this.onResetButtonClick);

        this.btnStatement = $("#btnStatement");
        this.btnStatement.unbind('click');
        this.btnStatement.bind('click', { obj: this }, this.onDownloadClick);
        
        this.handleGridCompleteEvent();
    },
    
    onDownloadClick: function (events) {
        var _obj = events.data.obj;
        var url = IC.Public.urls.Summary.downloadPlanStatement;
        
        var data = _obj.vestingCalculatorGrid.getPostData();
        data.conversionCurrency = $("#conversionCurrency").val();
        window.open(url + '?' + $.param(data), '_blank');
        //window.location = "/Summary/DownloadPlanStatement";
    },

    getFilterData: function () {
        var self = this;
        var form = $('form#vestingCalculatorForm');
        var dateFilter = parseInt($('input[name=DateFilter]:checked').val());
        var result = { IsValid : false };

        switch (dateFilter) {
            case IC.Public.Plans.Common.DateFilterTypes.Range:
                if (self.dateFrom.val().length) {
                    self.dateFrom.dateValidate({ mask: "dd/mm/yy", name: "From", allowFuture: true, allowPast: false });
                    if (self.dateFrom.hasClass('error')) {
                        return result; 
                    }
                }

                if (self.dateTo.val().length) {
                    self.dateTo.dateValidate({ mask: "dd/mm/yy", name: "To", allowFuture: true, allowPast: false });
                    if (self.dateTo.hasClass('error')) {
                        return result;
                    }
                }

                if (self.dateTo.val().length && self.dateFrom.val().length) {
                    var dateTo = $.datepicker.parseDate("dd/mm/yy", self.dateTo.val());
                    var dateFrom = $.datepicker.parseDate("dd/mm/yy", self.dateFrom.val());
                    var dateError = '"To" Date is not allowed to be earlier than "From" Date.';
                    IC.Public.removeErrorFor(self.dateTo, form, dateError);

                    if (dateTo < dateFrom) {
                        IC.Public.displayErrorFor(self.dateTo, form, dateError);
                        return result;
                    }
                }
                result.DateFilter = dateFilter;
                result.DateFrom = this.dateFrom.val();
                result.DateTo = this.dateTo.val();
                result.SecurityPrice = this.securityPrice.val();
                result.VestingPercentage = this.vestingPercentage.val();
                break;

            case IC.Public.Plans.Common.DateFilterTypes.AsAt:
                if (self.dateFrom.val().length) {
                    self.dateFrom.dateValidate({ mask: "dd/mm/yy", name: "From", allowFuture: true });
                    if (self.dateFrom.hasClass('error')) {
                        return result; 
                    }
                }

                if (self.dateFrom.val().length) {
                    result.DateFilter = dateFilter;
                    result.DateFrom = this.dateFrom.val();
                    result.SecurityPrice = this.securityPrice.val();
                    result.VestingPercentage = this.vestingPercentage.val();
                }
        }
        result.IsValid = true;
        result.ViewKey = $("#ViewKey").val();
        result.conversionCurrency = $("#conversionCurrency").val();
        result.VestingType = $("#VestingType").val();
        return result;
    },

    onGoButtonClick: function(eventArgs) {
        var self = eventArgs.data.obj;
        var data = self.getFilterData();
        if (data.IsValid) {
            self.vestingCalculatorGrid.appendPostData(data);
            self.vestingCalculatorGrid.trigger('reloadGrid', [{ page: 1 }]);
        }

        return false;
    },

    onResetButtonClick: function (eventArgs) {
        $('input[name=DateFilter][value=1]').prop('checked', true);
        $('#dateToDiv').show();
        $('#SecurityPrice').val($('#LatestSecurityPrice').val());
        $('#DateFrom').val('');
        $('#DateTo').val('');
        $('#VestingType').val("-1");
        $('#VestingPercentage').val("100");

        var self = eventArgs.data.obj;
        var data = self.getFilterData();
        if (data.IsValid) {
            self.vestingCalculatorGrid.appendPostData(data);
            self.vestingCalculatorGrid.trigger('reloadGrid', [{ page: 1 }]);
        }

        return false;
    },

    handleGridCompleteEvent: function () {
        var self = this;
        var vestingCalculatorGridTotals = $('#vestingCalculatorGridTotals');
        this.vestingCalculatorGrid.bind("grid_gridComplete", { obj: this }, function (events, gridContext) {
            var totalVested = $(this).getUserDataItem('TotalVested');
            var totalEstimatedVestingValue = $(this).getUserDataItem('TotalEstimatedVestingValue');

            var noDescription = $(self.gview_VestingCalculatorGrid.find('th').eq(3)).css('display') == 'none';

            // set up the totals table
            var totalRow = vestingCalculatorGridTotals.find('tr').eq(0);
            if (totalRow.length) {
                totalRow.find('td').eq(0).attr("style", "width:" + $(self.gview_VestingCalculatorGrid.find('th').eq(0)).width() + "px");
                totalRow.find('td').eq(1).attr("style", "width:" + $(self.gview_VestingCalculatorGrid.find('th').eq(1)).width() + "px");
                totalRow.find('td').eq(2).attr("style", "width:" + $(self.gview_VestingCalculatorGrid.find('th').eq(2)).width() + "px");
                if (noDescription) {
                    totalRow.find('td').eq(3).attr("style", "width: 1px;");
                } else {
                    totalRow.find('td').eq(3).attr("style", "width:" + $(self.gview_VestingCalculatorGrid.find('th').eq(3)).width() + "px");
                }
                totalRow.find('td').eq(4).text($.formatNumber(totalVested, "#,0"));
                totalRow.find('td').eq(4).attr("style", "width:" + $(self.gview_VestingCalculatorGrid.find('th').eq(4)).width() + "px");

                var formattedValue = $.formatNumber(totalEstimatedVestingValue, "#,0.00");
                totalRow.find('td').eq(5).find('a').text(formattedValue);
                totalRow.find('td').eq(5).attr("style", "width:" + $(self.gview_VestingCalculatorGrid.find('th').eq(5)).width() + "px");
                var tooltip = totalRow.find('td').eq(5).find('div');
                tooltip.html(tooltip.html().tooltipReplace(self.securityPriceCurrency.text(), '', '', '', ''));
                $('span#estimatedVestingValueToolTip').text(formattedValue);

                // cluetip initialisation
                var positionBy = 'mouse';
                totalRow.find('a').each(function () {
                    $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 280, positionBy: positionBy });
                });
            }

            vestingCalculatorGridTotals.show();
            self.vestingCalculatorGrid.setPostDataItem("VestingPercentage", self.vestingPercentage.val());
        });
    },

    onDateFilterClick: function (eventArgs) {
        
        var dateFilter = $('input[name=DateFilter]:checked').val();
        if (dateFilter == IC.Public.Plans.Common.DateFilterTypes.Range) {
            $('#dateToDiv').show();
        } else {
            $('#dateToDiv').hide();
        }
    },
    
    //onSecurityPriceChange: function (eventArgs) {
        
    //    var securityPriceText = $(this).val().replace(/,/g, "");
    //    if (securityPriceText == "")
    //        return;
    //    var securityPriceNumber = parseFloat(securityPriceText);
    //    if (isNaN(securityPriceNumber))
    //        return;
        
    //    var self = eventArgs.data.obj;
    //    self.vestingCalculatorGrid.setPostDataItem("SecurityPrice", self.securityPrice.val());
    //    self.calculateEstimatedValue(securityPriceNumber);
    //},
    onSecurityPriceChange1: function (eventArgs) {
        if (/^[0-9]\d*(\.\d+)?$/.test(String.fromCharCode(eventArgs.keyCode)) == false) return false;
        
            
    },
    
    //onVestingPercentageChange: function(eventArgs) {
    //    var percentageText = $(this).val().replace(/,/g, "");
    //    if (percentageText == "")
    //        return;
    //    var percentage = parseFloat(percentageText);
    //    if (isNaN(percentage))
    //        return;

    //    if (percentage < 0) {
    //        percentage = 0;
    //        $(this).val(0);
    //    }

    //    if (percentage > 100) {
    //        percentage = 100;
    //        $(this).val(100);
    //    }
        
    //    var self = eventArgs.data.obj;
    //    self.applyPercentage(percentage, false, true);
    //},
    
    applyPercentage: function (percentage, usePerformanceResult, displayErrorMessage) {
        var gridRows = $('#VestingCalculatorGrid tr').map(function () {
            return $(this);
        }).get();

        var totalVested = 0;
        var vestingValueChanged = false;
        $.each(gridRows, function (i, gridRow) {
            var vestingType = gridRow.getColumnValue('VestingType');
            var originalVested = parseInt(gridRow.getColumnValue('OriginalVested').replace(/,/g, ""));
            var percentageToUse;
            var isMiraqlePerformancePlan = gridRow.getColumnValue('IsMiraqlePerformancePlan') == 'true';
            if (usePerformanceResult && isMiraqlePerformancePlan) {
                percentageToUse = parseFloat(gridRow.getColumnValue('TsrVestedValue').replace(/,/g, ""));
            } else {
                percentageToUse = percentage;
            }

            if (vestingType == IC.Public.Plans.Common.VestingType.Performance ||
                vestingType == IC.Public.Plans.Common.VestingType.PerformanceOrTime) {
                var vested = (percentageToUse / 100) * originalVested;
                totalVested += vested;
                var vestedCell = gridRow.getColumnCell('Vested');
                vestedCell.html($.formatNumber(vested, "#,0"));
                vestingValueChanged = true;
            } else {
                totalVested += originalVested;
            }
        });

        if (displayErrorMessage && !vestingValueChanged) {
            alert("There are no performance plans.");
            return false;
        }

        var value = $.formatNumber(totalVested, "#,0");
        $('td.gridCol1').html(value);

        this.securityPrice.trigger('keyup');
        this.vestingCalculatorGrid.setPostDataItem("VestingPercentage", this.vestingPercentage.val());
        this.vestingCalculatorGrid.setPostDataItem("ApplyPerformanceResult", usePerformanceResult);
        return true;
    },

    onApplyLatestPerformanceResultChange: function (eventArgs) {
        var self = eventArgs.data.obj;
        var data = self.getFilterData();
        if (data.IsValid) {
            data.ApplyPerformanceResult = $(this).is(':checked');
            self.vestingCalculatorGrid.appendPostData(data);
            self.vestingCalculatorGrid.trigger('reloadGrid', [{ page: 1 }]);
        }
    },
    
    calculateEstimatedValue: function (securityPriceNumber) {
        var totalEstimatedValue = 0;
        $('span.estimatedVestingValue').each(function () {
            var tr = $(this).parents('tr');
            var vested = parseInt(tr.getColumnValue('Vested').replace(/,/g, ""));
            var estimatedValue = vested * securityPriceNumber;

            // Include the exercise cost if applicable
            var exercisePrice = parseFloat(tr.getColumnValue('OptionPrice').replace(/,/g, ""));
            estimatedValue = estimatedValue - (vested * exercisePrice);
            if (estimatedValue < 0) estimatedValue = 0;
            
            totalEstimatedValue += estimatedValue;
            $(this).text($.formatNumber(estimatedValue, "#,0.00"));
        });
        var value = $.formatNumber(totalEstimatedValue, "#,0.00");
        $('td.gridCol2 a').text(value);
        $('span#estimatedVestingValueToolTip').text(value);
    }
};IC.Public.Plans.equityStatementModal = function() {

};

IC.Public.Plans.equityStatementModal.prototype = {    
    showModal: function () {
        var self = this;
        OC.MVC.util.showModal('Summary', 'EquityPlanStatement', null,
               { minWidth: 500, width: 500, hideCloseButton: false }, function () { self.init(); }
           );
    },
    
    init: function () {
        IC.Public.hideLoading();
        var self = this;

        self.EmptyFromDateMessage = 'Please enter "From" Date.';
        self.EmptyToDateMessage = 'Please enter "To" Date.';
        self.InvalidDateRangeErrorMessage = '"To" Date is not allowed to be earlier than "From" Date.';

        // We need to find the controls in modal since the HTMl is loaded dynamically.
        this.goButton = $('#equityPlanStatementContainer').find('#btnGo');
        this.goButton.unbind('click');
        this.goButton.bind('click', { obj: self }, self.goButtonClick);

        this.goToVestingCalculatorLink = $('#equityPlanStatementContainer').find('#btnGoToVestingCalculator');
        this.goToVestingCalculatorLink.unbind('click');
        this.goToVestingCalculatorLink.bind('click', { obj: self }, self.goToVestingCalculatorLinkClick);
        
        $('input[name=DateFilter][value=1]').attr('checked', 'checked');
        this.dateFrom = $('#equityPlanStatementContainer').find('#FromDate');
        this.dateTo = $('#equityPlanStatementContainer').find('#ToDate');

        var datepickerOptions = {
            showOn: 'button',
            buttonImage: '/images/buttons/calendar.gif',
            buttonImageOnly: 'true',
            changeMonth: 'true',
            changeYear: 'true',
            showWeek: '1',
            buttonText: 'Select date',
            de_onChangeMonthYear: 'IC.Public.onChangeMonthYear'
        };
        this.dateFrom.datepicker(datepickerOptions);
        this.dateTo.datepicker(datepickerOptions);
        
        this.dateFrom.datepicker('option', { dateFormat: 'dd/mm/yy', maxDate: new Date(), yearRange: 'c-12:c' });
        this.dateTo.datepicker('option', { dateFormat: 'dd/mm/yy', maxDate: new Date(), yearRange: 'c-12:c' });
        
        this.dateFrom.val('');
        this.dateTo.val('');
        this.dateToDiv = $('#equityPlanStatementContainer').find('#dateToDiv');
        
        this.dateFilterTypeRadio = $("[name=DateFilter]");
        this.dateFilterTypeRadio.unbind('click');
        this.dateFilterTypeRadio.bind("click", { obj: self }, self.onDateFilterClick);

        this.equityPlanStatementForm = $('#equityPlanStatementContainer').find('#equityPlanStatementForm');
        this.adjustModalSizeAndPosition();
        
        this.planStatementErrorContainer = $('#planStatementError');
        this.planStatementErrorContainer.hide();
    },
    
    onDateFilterClick: function (eventArgs) {
        var self = eventArgs.data.obj;
        
        var dateFilter = $('#equityPlanStatementContainer').find('input[name=DateFilter]:checked').val();
        if (dateFilter == IC.Public.Plans.Common.DateFilterTypes.Range) {
            self.dateToDiv.show();
        } else {
            self.dateToDiv.hide();
        }
    },
    
    adjustModalSizeAndPosition: function () {
        $('#simplemodal-container').css('height', 'auto');
        $.modal.setPosition();
    },
    
    goButtonClick: function (eventArgs) {
        var errorMessage = "Unable to get the security price at this time. Please try again after sometime.";
        var self = eventArgs.data.obj;
        self.planStatementErrorContainer.hide();
        var url = IC.Public.urls.Summary.downloadEquityPlanStatement;

        var result = self.getFilterData();
        if (!result.IsValid)
            return;
        
        $.ajax({
            url: OC.MVC.util.getLink("Summary", "GetSecurityPrice"),
            type: 'GET',
            data: { asAtDate: result.AsAtDate },
            success: function (response) {
                IC.Public.hideLoading();
                if (response && response.Passed) {
                    var data = self.equityPlanStatementForm.serializeObject();
                    data.conversionCurrency = $("#conversionCurrency").val();
                    data.SecurityPrice = response.SecurityPrice;
                    window.location = url + '?' + $.param(data);
                } else {
                    self.planStatementErrorContainer.show();
                    self.planStatementErrorContainer.html("<ol><li>" + response.ErrorMessage + "</li></ol>");
                }
            },
            error: function(err) {
                IC.Public.hideLoading();
                self.planStatementErrorContainer.show();
                self.planStatementErrorContainer.html("<ol><li>" + errorMessage + "</li></ol>");
            }
        });
    },

    getFilterData: function() {
        var self = this;
        var form = self.equityPlanStatementForm;
        var dateFilter = parseInt($('#equityPlanStatementContainer').find('input[name=DateFilter]:checked').val());
        var result = {
            IsValid: false
        };
        IC.Public.removeErrorFor(self.dateFrom, form, self.EmptyFromDateMessage);
        IC.Public.removeErrorFor(self.dateTo, form, self.EmptyToDateMessage);
        IC.Public.removeErrorFor(self.dateTo, form, self.InvalidDateRangeErrorMessage);
        
        switch (dateFilter) {
        case IC.Public.Plans.Common.DateFilterTypes.Range:
            if (self.dateFrom.val().length) {
                self.dateFrom.dateValidate({
                    mask: "dd/mm/yy",
                    name: "From",
                    allowFuture: false
                });
                if (self.dateFrom.hasClass('error')) {
                    return result;
                }
            } else {
                IC.Public.displayErrorFor(self.dateFrom, form, self.EmptyFromDateMessage);
                return result;
            }

            if (self.dateTo.val().length) {
                self.dateTo.dateValidate({
                    mask: "dd/mm/yy",
                    name: "To",
                    allowFuture: false
                });
                if (self.dateTo.hasClass('error')) {
                    return result;
                }
            } else {
                IC.Public.displayErrorFor(self.dateTo, form, self.EmptyToDateMessage);
                return result;
            }

            if (self.dateTo.val().length && self.dateFrom.val().length) {
                var dateTo = $.datepicker.parseDate("dd/mm/yy", self.dateTo.val());
                var dateFrom = $.datepicker.parseDate("dd/mm/yy", self.dateFrom.val());
                var dateError = self.InvalidDateRangeErrorMessage;
                IC.Public.removeErrorFor(self.dateTo, form, dateError);

                if (dateTo < dateFrom) {
                    IC.Public.displayErrorFor(self.dateTo, form, dateError);
                    return result;
                }
            }
            result.DateFilter = dateFilter;
            result.DateFrom = this.dateFrom.val();
            result.DateTo = this.dateTo.val();
            result.AsAtDate = result.DateTo;
            break;
        case IC.Public.Plans.Common.DateFilterTypes.AsAt:
            if (self.dateFrom.val().length) {
                self.dateFrom.dateValidate({ mask: "dd/mm/yy", name: "From", allowFuture: false });
                if (self.dateFrom.hasClass('error')) {
                    return result;
                }
            } else {
                IC.Public.displayErrorFor(self.dateFrom, form, self.EmptyFromDateMessage);
                return result;
            }

            if (self.dateFrom.val().length) {
                result.DateFilter = dateFilter;
                result.DateFrom = this.dateFrom.val();
                result.AsAtDate = result.DateFrom;
            }
            break;
        }
        result.IsValid = true;
        result.ViewKey = $("#ViewKey").val();
        result.conversionCurrency = $("#conversionCurrency").val();
        return result;
    },
    
    goToVestingCalculatorLinkClick: function (eventArgs) {
        $.modal.close();
        var url = OC.MVC.util.getLink("Summary", "VestingCalculatorGrid");
        var vestingCalculatorTabLink = $('#planSummaryContainer').find(".displayLinks a[actionurl='" + url + "']");
        vestingCalculatorTabLink.trigger('click');
    }
};;IC.Public.Plans.actionOptions = {
    sellORTransfer: 1,
    transactions: 2
};

IC.Public.Plans.securities = function (validateTransactionPassword) {
    this.init(validateTransactionPassword);
};

IC.Public.Plans.securities.prototype = {
    init: function (validateTransactionPassword) {
        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.SaleOrTransfer);

        //Memeber Variables
        this.validateTransactionPassword = validateTransactionPassword;
        this.TotalSharesSelected = $("#TotalSharesSelected");
        this.EstimatedValue = $("#EstimatedValue");
        this.SharesToTransactPreviousValue = "";

        //Event bindings
        this.actionOption = $("[name=SecuritiesAction]");
        this.actionOption.unbind('click');
        this.actionOption.bind('click', { obj: this }, this.onActionClick);

        this.sellAllCheckBox = $("#IsSellAll");
        this.sellAllCheckBox.unbind('click');
        this.sellAllCheckBox.bind('click', { obj: this }, this.onSellAllClick);

        this.sellNumberCheckBox = $("#IsSellNumber");
        this.sellNumberCheckBox.unbind('click');
        this.sellNumberCheckBox.bind('click', { obj: this }, this.onSellNumberClick);

        this.sellNumberTextBox = $("#SellNumber");
        this.sellNumberTextBox.unbind('blur');
        this.sellNumberTextBox.bind('blur', { obj: this }, this.onSellNumberBlur);

        this.grid = $("#SellOrTransferSecuritiesGrid");
        this.grid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onGridLoadComplete);

        //Campaign
        this.viewKey = $("#ViewKey");

        this.ExerciseMethods = $("[name=TransactionMethods]");
        this.ExerciseMethods.unbind('click');
        this.ExerciseMethods.bind("click", { obj: this }, this.onMethodSelect);

        this.methodDetailItems = $('.method-detail-items').find('input[type=radio]');
        this.methodDetailItems.unbind('click');
        this.methodDetailItems.bind('click', { obj: this }, this.onMethodDetailsClick);

        this.paymentMethod = $('input[name=PaymentMethod]');
        this.paymentMethod.unbind('click');
        this.paymentMethod.bind('click', { obj: this }, this.onPaymentMethodClick);
        this.paymentDetails = $('.method-details');

        var self = this;
        this.dayLimitMinPrice = $('#dayLimitMinPrice');
        this.dayLimitMinPrice.unbind('keyup');
        this.dayLimitMinPrice.bind('keyup', function () {
            self.processContinueButton();
        });

        this.TermsAndConditionsAccepted = $("#TermsAndConditionsAccepted");
        this.TermsAndConditionsAccepted.change(function () {
            self.processContinueButton();
        });

        this.btnContinue = $('#btnContinue');
        this.btnContinue.unbind('click');
        this.btnContinue.bind("click", { obj: this }, this.onContinueButtonClick);

        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind('click');
        this.btnCancel.bind("click", { obj: this }, this.onCancelClick);

        this.rdoSellByDayLimit = $("#sales_methods_LIM");
        this.rdoSellByDayLimit.unbind('click');
        this.rdoSellByDayLimit.bind('click', this.onSellDayLimitClick);

        //ViewBar change event
        var holdingFilter = $("form.holdingFilterForm");
        if (holdingFilter.length) {
            holdingFilter.unbind("applyHoldingFilter");
            holdingFilter.bind("applyHoldingFilter", { obj: self }, self.onFilterClick);
        }

        this.updateBankAccountLink = $('.update-bank-account');
        this.updateBankAccountLink.unbind('click');
        this.updateBankAccountLink.bind("click", { obj: this }, this.onUpdateBankAccountLinkClick);

        this.iBWUpdateAccountLink = $('.ibw-update-account');
        this.iBWUpdateAccountLink.unbind('click');
        this.iBWUpdateAccountLink.bind("click", { obj: this }, this.onIBWUpdateAccountLinkClick);

        this.updateAddressLink = $('.update-address');
        this.updateAddressLink.unbind('click');
        this.updateAddressLink.bind("click", { obj: this }, this.onUpdateAddressLinkClick);

        //This will only happen when BACK button is clicked in second screen
        if ($("input[name=TransactionMethods]:checked").length > 0) {
            this.showTermsAndConditions($("input[name=TransactionMethods]:checked").val());
        }

        //var url = IC.Public.urls.Securities.transactionsList;
        //OC.MVC.util._loadView("action-container", url, { viewKey: this.viewKey });

        this.exerciseMessageLink = $('.exerciseMessageLink');
        this.exerciseMessageLink.unbind('click');
        this.exerciseMessageLink.bind('click', this.onExerciseMessageLinkClick);

        if (IC.Public.IsThirdPartyImpersonateUser()) $("#btnContinue").attr("disabled", true);
        this.isLoading = false;
    },

    ////**** Event Binder ****\\\\
    onActionClick: function (events) {
        var _obj = events.data.obj;
        var value = $(this).val();
        var url = IC.Public.urls.Securities.sellOrTransferList;
        if (value == IC.Public.Plans.actionOptions.transactions)
            url = IC.Public.urls.Securities.transactionsList;
        //OC.MVC.util._loadView("action-container", url, { viewKey: _obj.viewKey.val() });
        OC.MVC.util.loadMainContainerView(url, { viewKey: _obj.viewKey.val() });
    },

    onSellAllClick: function (events) {
        var _obj = events.data.obj;
        var value = $(this).is(':checked');
        _obj.toggleSharesToTransactValues(value);
        _obj.toggleSellNumberInputs(!value);
        _obj.updateTotals();
        _obj.processContinueButton();
    },

    onSellNumberClick: function (events) {
        var _obj = events.data.obj;
        var value = $(this).is(':checked');
        _obj.toggleSellAllInputs(_obj, !value);
        _obj.toggleSellNumberInputs(value);
        _obj.processContinueButton();
        $("#SellNumber").focus();
        _obj.updateTotals();
    },

    onSellNumberBlur: function (events) {
        var sellNumber = parseInt($(this).val().replaceAll(",", ""));
        var _obj = events.data.obj;
        _obj.distributeSellNumber(sellNumber);
        _obj.updateTotals();
        _obj.processContinueButton();
    },

    onGridLoadComplete: function (events) {
        var _obj = events.data.obj;
        var sharesToTransact = $('.SharesToTransact');
        sharesToTransact.numeric();
        sharesToTransact.bind('keyup', { obj: _obj }, _obj.onSharesToTransactNumberChange);
        sharesToTransact.bind('blur', { obj: _obj }, _obj.onSharesToTransactNumberChange);
        sharesToTransact.bind('focus', { obj: _obj }, _obj.onSharesToTransactFocus);
        _obj.updateTotals();
    },

    onSharesToTransactFocus: function (events) {
        var _obj = events.data.obj;
        _obj.SharesToTransactPreviousValue = $(this).val();

    },

    onSharesToTransactNumberChange: function (events) {
        var _obj = events.data.obj;

        var tr = $(this).parents('tr');

        //It should be sum of all rows
        var availableSharedValue = tr.getColumnValue('AvailableShares'); // $(tr).children().eq(4).text();


        var sharesToTransactValue = $(this).val();
        var sharesToTransactPreviousValue = _obj.SharesToTransactPreviousValue;

        if (sharesToTransactValue) {

            var _availableSharedValue = parseInt(availableSharedValue.replaceAll(",", ""));
            var _sharesToTransactValue = parseInt(sharesToTransactValue.replaceAll(",", ""));
            var _sharesToTransactPreviousValue = parseInt(sharesToTransactPreviousValue.replaceAll(",", ""));

            if (!isNaN(_availableSharedValue) && !isNaN(_sharesToTransactValue)) {
                if (_availableSharedValue < _sharesToTransactValue) {
                    alert('Securities to Transact must be less or equal to Available Securities');
                    $(this).val('');
                    _obj.updateTotals();
                    return;
                }

                //if (_sharesToTransactValue == totalAvailableShares) {
                //    $("#IsSellAll").attr('checked', true);
                //} else {
                //    $("#IsSellAll").attr('checked', false);
                //}

                if (_sharesToTransactValue != _sharesToTransactPreviousValue) {
                    _obj.toggleSellNumberInputs(false);
                }
            }

            _obj.updateTotals();
        }
        _obj.processContinueButton();
    },

    onSellDayLimitClick: function (events) {
        $("#dayLimitMinPrice").focus();
    },

    ////**** Local Methods ****\\\\
    toggleSellAllInputs: function (obj, value) {
        $("#IsSellAll").prop('checked', value);
        obj.toggleSharesToTransactValues(value);
    },

    toggleSellNumberInputs: function (value) {
        $("#IsSellNumber").prop('checked', value);
        $("#SellNumber").attr('disabled', !value);
        if (!value) {
            $("#SellNumber").val('');
        }
    },

    toggleSharesToTransactValues: function (value) {

        var available_shares = $('.available-shares');

        $.each(available_shares, function (i, item) {
            //Read value from AvailableShares column
            var tr = $(item).parents('tr');
            var tdCellAvailableShares = $(item).text();
            var tdCellSharesToTransact = tr.getColumnCell('SharesToTransact');

            if (value == true) {
                //Put AvailableShares into SharesToTransact column
                tdCellSharesToTransact.find('input').val(tdCellAvailableShares);

            } else {
                //Emptying SharesToTransact text box
                tdCellSharesToTransact.find('input').val('');
            }

        });
    },

    distributeSellNumber: function (sellNumber) {

        var _trList = $('#SellOrTransferSecuritiesGrid tr').map(function () {
            return $(this);
        }).get();

        _trList.sort(function (a, b) {
            var date_a = $.datepicker.parseDate('dd/mm/yy', a.getColumnCell('Release Date').text());
            var date_b = $.datepicker.parseDate('dd/mm/yy', b.getColumnCell('Release Date').text());

            return date_a - date_b;
        }
        );

        $.each(_trList, function (i, item) {
            var availableShares = parseInt(item.getColumnValue('AvailableShares').replaceAll(",", ""));

            var tdCellSharesToTransact = item.getColumnCell('SharesToTransact');
            tdCellSharesToTransact.find('input').val('');
            if (sellNumber > 0) {

                if (availableShares <= sellNumber) {
                    tdCellSharesToTransact.find('input').val(availableShares);
                    sellNumber = sellNumber - availableShares;
                } else {
                    tdCellSharesToTransact.find('input').val(sellNumber);
                    sellNumber = 0;
                }
            }
        });

    },

    updateTotals: function () {

        var _SharesToTransact = $('.SharesToTransact').map(function () {
            var _value = parseInt($(this).val().replaceAll(",", ""));
            return isNaN(_value) ? 0 : _value;
        }).get();

        var _TotalSharesSelected = _SharesToTransact.sum();
        this.TotalSharesSelected.text($.formatNumber(_TotalSharesSelected, "#,0"));

        //var _totalExercisePrice = $('.total-price').map(function () {
        //    var _value = parseFloat($(this).text());
        //    return isNaN(_value) ? 0 : _value;
        //}).get();
        var _SharepPrice20MinDelay = parseFloat($("#SharepPrice20MinDelay").val()) * parseInt(_TotalSharesSelected);
        this.EstimatedValue.text($.formatNumber(_SharepPrice20MinDelay, "#,0.00"));

        //If total of inputed value is equal to TotalAvailableShares, then tick the "Sell or Transfer All" checkbox
        var totalAvailableShares = $("#TotalAvailableShares").val();
        if (_TotalSharesSelected == totalAvailableShares) {
            $("#IsSellAll").prop('checked', true);
            $("div.sellAll").fadeOut(100).fadeIn(500);
        } else {
            $("#IsSellAll").prop('checked', false);
        }
    },

    onMethodSelect: function (events) {
        var _obj = events.data.obj;
        var optionId = $(this).val();
        var containers = $('.securities-method-container');
        containers.hide();
        $('.method-details').hide();
        containers.find("input:radio").removeAttr("checked");

        var id = "#TransactionMethods_" + optionId + "_container";
        $(id).show();
        _obj.processContinueButton();
        _obj.showTermsAndConditions(optionId);
    },

    showTermsAndConditions: function (methodType) {
        $('.terms-conditions').show();
        if (methodType == 'S') {
            $('#tncSell').show();
            $('#tncTransfer').hide();
        } else {
            $('#tncSell').hide();
            $('#tncTransfer').show();
        }
    },

    onMethodDetailsClick: function (events) {
        var self = events.data.obj;
        $(this).parents('ul.method-detail-items').find('.method-details').hide();
        $(this).parents('ul.method-detail-items li').find('.method-details').show();
        self.processContinueButton();
    },

    onPaymentMethodClick: function (events) {
        var self = events.data.obj;
        self.paymentDetails.hide();
        var id = "paymentDetails_" + $(this).val() + "_" + $(this).attr('data-payment-proceed-seq-number');
        $('#' + id).show();

        if ($(this).val() == 'FDC' || $(this).val() == 'IBW') {
            $("div .callout").show();
        } else {
            $("div .callout").hide();
        }
        self.processContinueButton();
    },

    processContinueButton: function () {
        var selectedGrants = this.getSharesToTransact();
        var transactionMethod = $('input[name=TransactionMethods]:checked').val();
        var isValid = (selectedGrants != '' && transactionMethod != null);
        if (transactionMethod == 'S') {
            var salesMethod = $("input[name=SalesMethod]:checked").val();
            var paymentMethod = $("input[name=PaymentMethod]:checked").val();
            isValid = (isValid && salesMethod != null && paymentMethod != null);

            if (isValid) {
                if (salesMethod == IC.Public.Plans.SaleMethods.SellByLimit) {
                    var limitPrice = parseFloat($("#dayLimitMinPrice").val().replaceAll(",", ""));
                    isValid = (!isNaN(limitPrice) && limitPrice > 0);
                }
            }

            if (isValid) {
                //if ($("input[name=PaymentMethod]:checked").attr('data-payment-proceed-seq-number').substring(0, 2) == '-1') {
                if ($("input[name=PaymentMethod]:checked").data('payment-proceed-seq-number').toString().substring(0, 2) == '-1') {
                    isValid = false;
                }
            }
        }
        isValid = (isValid && this.TermsAndConditionsAccepted.is(':checked'));
        isValid = isValid && $("#HasUnregisteredHrns").val().toLowerCase() == "false";

        if (isValid)
            this.btnContinue.parent().removeClass('disabled');
        else
            this.btnContinue.parent().addClass('disabled');

        if (IC.Public.IsThirdPartyImpersonateUser())
            this.btnContinue.parent().addClass('disabled');
    },

    getSharesToTransact: function () {
        var selectedGrants = '';
        $('.SharesToTransact').each(function () {
            var numberToTransact = $(this).val().replaceAll(",", "");
            if (numberToTransact == '' || numberToTransact == '0')
                return;
            var manageSeqNumber = $(this).attr('manageSeqNumber');

            if (selectedGrants == '')
                selectedGrants = manageSeqNumber + "|" + numberToTransact;
            else
                selectedGrants += "," + manageSeqNumber + "|" + numberToTransact;
        });

        return selectedGrants;
    },

    onContinueButtonClick: function (events) {
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        var self = events.data.obj;

        //Prepare "ManageSeqNumber|SharesToTransact" array string
        var selectedGrants = self.getSharesToTransact();
        if (selectedGrants == '')
            return;

        var transactionMethod = $('input[name=TransactionMethods]:checked').val();
        if (transactionMethod == null) {
            return;
        }
        //SaleMethod and PaymentMethod validation
        if (transactionMethod == 'S') {
            if ($("input[name=SalesMethod]:checked").length == 0) {
                return;
            }

            if ($("input[name=PaymentMethod]:checked").length == 0) {
                return;
            }
        }

        $('#hdnSelectedGrants').val(selectedGrants);
        $('#hdnPaymentProceedSeqNumber').val($("input[name=PaymentMethod]:checked").data('payment-proceed-seq-number'));
        var totalSharesSelected = $("<input>").attr("type", "hidden").attr("name", "TotalSharesSelected").val($("#TotalSharesSelected").text());

        //SalePrice
        var salePriceInput = 0;
        if ($("#sales_methods_MKT").prop('checked') == true) {
            salePriceInput = $("#marketOrderPrice").text();
        } else {
            salePriceInput = $("#dayLimitMinPrice").val();
        }
        var salePrice = $("<input>").attr("type", "hidden").attr("name", "salePrice").val(salePriceInput);

        ////PaymentMethod
        //var paymentMethodInput = $("input[name=PaymentMethod]:checked").val();
        //var paymentMethod = $("<input>").attr("type", "hidden").attr("name", "paymentMethod").val(paymentMethodInput);

        //ViewKey
        var viewKeyInput = $("#ViewKey").val();
        var viewKey = $("<input>").attr("type", "hidden").attr("name", "viewKey").val(viewKeyInput);

        //transferSRN
        var transferToSRNControl = $("[name='UserSRN']");
        var transferSRNInput = transferToSRNControl.val();
        if (transferSRNInput == "")
            transferSRNInput = transferToSRNControl.text();
        var transferSRN = $("<input>").attr("type", "hidden").attr("name", "transferSRN").val(transferSRNInput);

        //Prepare the Form for submit
        var form = $("form#SellOrTransferForm");
        form.append($(totalSharesSelected)).append($(salePrice)).append($(viewKey)).append($(transferSRN)); //Append misc inputs at the end of the form //append($(paymentMethod)).
        var data = form.serializeObject();

        IC.Public.showLoading("");

        //Disable the COntinue button to prevent more than one click            
        $(this).parent().addClass('disabled');

        //Submit the Form through AJAX
        $.ajax({
            url: IC.Public.urls.Securities.sellOrTransferList,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();

            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
                $(this).parent().removeClass('disabled');
            }
        });
    },

    onFilterClick: function (events) {
        var url = OC.MVC.util.getLink("Securities", "SellOrTransferSecurities", "viewKey=" + $("#ViewKey").val());
        OC.MVC.util.loadMainContainerView(url, null);
    },
    onCancelClick: function (events) {
        //var url = OC.MVC.util.getLink("Securities", "SellOrTransferSecurities", "viewKey=" + $("#ViewKey").val());
        //OC.MVC.util.loadMainContainerView(url, null);
        var url = OC.MVC.util.getLink("Securities", "Cancel");
        OC.MVC.util.loadMainContainerView(url, { viewKey: $("#ViewKey").val() });

    },

    onUpdateBankAccountLinkClick: function (events) {
        var self = events.data.obj;
        var proceedSeqNumber = $(this).attr('proceedSeqNumber');
        IC.Public.showLoading("");

        var paymentMethodCode = $(this).attr('paymentMethodTypeCode');
        var paymentMethodDesc = $(this).attr('paymentMethodTypeDesc');
        var modal = new IC.Public.Plans.BankAccountDetails();
        modal.showModal(proceedSeqNumber, IC.Public.Plans.Common.MenuSubItemTypes.Securities, self.validateTransactionPassword, paymentMethodCode, paymentMethodDesc);
    },

    onIBWUpdateAccountLinkClick: function (events) {
        var proceedSeqNumber = parseInt($(this).attr('proceedSeqNumber'));
        var modal = new IC.Public.Plans.InternationalBankWire();
        IC.Public.showLoading("");
        modal.showModal(proceedSeqNumber, IC.Public.Plans.Common.MenuSubItemTypes.Securities);
    },

    onUpdateAddressLinkClick: function () {
        var url = OC.MVC.util.getLink("Address", "Edit");
        OC.MVC.util.loadMainContainerView(url);
    },

    onExerciseMessageLinkClick: function () {
        var type = $(this).attr("type");
        var code = $(this).attr("code");
        var viewKey = $("#ViewKey").val();
        var transactionType = $(this).attr("transactionType");
        var windowId = $(this).attr("windowId");

        var modal = new IC.Public.Plans.message();
        modal.showModal(type, code, viewKey, transactionType, windowId, IC.Public.Plans.Common.MenuSubItemTypes.Securities);
    }
};
;IC.Public.Plans.securities.confirmSecurities = function (validateTransactionPassword) {
    this.init($("#CurrentTransaction").val(), validateTransactionPassword);
};

IC.Public.Plans.securities.confirmSecurities.prototype = {
    init: function (transactionType, validateTransactionPassword) {
        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.SaleOrTransfer);
        
        this.transactionType = transactionType;
        this.validateTransactionPassword = validateTransactionPassword;

        this.confirmSecuritiesSaleForm = $('form#ConfirmSecuritiesForm');
        this.btnConfirm = $("#btnConfirm");
        this.btnConfirm.bind("click", { obj: this }, this.onConfirmButtonClicked);

        this.btnCancel = $("#btnCancel");
        this.btnCancel.bind("click", this.OnCancelButtonClicked);

        this.btnBack = $("#btnBack");
        this.btnBack.bind("click", this.onBackButtonClicked);

        this.grid = $("#SellOrTransferSecuritiesGrid");
        this.grid.bind(OC.MVC.grid.events.loadComplete, { obj: this });
    },

    onConfirmButtonClicked: function (events) {
        if ($('#IsImpersonateUser').val().toLowerCase() == "true") {
            alert(IC.Public.Plans.Common.ImperonateUserNoAccessMessage);
            return;
        }
        
        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handleSecuritiesConfirmClick();
            return;
        }

        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        var formData = self.confirmSecuritiesSaleForm.serializeObject();
        IC.Public.security.validateTransactionPin(formData, function () { self.handleSecuritiesConfirmClick(); }, function () { self.disableSecuritiesButtons(); });
    },

    handleSecuritiesConfirmClick: function () {
        //Disable the Confirm button to prevent double click
        this.btnConfirm.parent().addClass('disabled');

        IC.Public.showLoading("");
        var url = OC.MVC.util.getLink("Securities", "ConfirmationReceipt");
        $.post(url, { transactionType: this.transactionType, viewKey: $("#ViewKey").val() }, function (data) {
            IC.Public.hideLoading();
            $("#" + OC.MVC.constants.mainContainer).html(data);
        });
    },

    disableSecuritiesButtons: function () {
        this.btnConfirm.parent().addClass('disabled');
    },

    OnCancelButtonClicked: function () {

        //var url = OC.MVC.util.getLink("Securities", "SellOrTransferSecurities");
        //OC.MVC.util.loadMainContainerView(url);
        var url = OC.MVC.util.getLink("Securities", "Cancel");
        OC.MVC.util.loadMainContainerView(url, { viewKey: $("#ViewKey").val() });
    },

    onBackButtonClicked: function () {

        var url = OC.MVC.util.getLink("Securities", "Back");
        OC.MVC.util.loadMainContainerView(url, { viewKey: $("#ViewKey").val() });

    }
}
;IC.Public.Plans.securities.confirmSecuritiesReceipt = function (transactionType) {
   
    this.init(transactionType);
};

IC.Public.Plans.securities.confirmSecuritiesReceipt.prototype = {
    init: function (transactionType) {
       IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.SaleOrTransfer);
        
        this.transactionType = transactionType;
        this.btnDone = $("#btnDone");
        this.btnDone.bind("click", this.onDoneButtonClick);

        this.aDownloadReceiptLink = $("#aDownloadReceiptLink");
        this.aDownloadReceiptLink.bind("click", { obj: this }, this.OnDownloadReceiptLinkClicked);

        var isThirdPartyImpersonateUser = $("#IsThirdPartyImpersonateUser").val();
        if (isThirdPartyImpersonateUser != undefined) {
            if (isThirdPartyImpersonateUser.toLowerCase() == "true") {
                $("#btnDone").attr('disabled', true);
            } else {
                $("#btnDone").removeAttr('disabled');
            }
        }

        IC.Public.updateCampaignsPanel('SecuritiesList');
    },
    onDoneButtonClick: function () {
        //var url = OC.MVC.util.getLink("Securities", "SellOrTransferSecurities");
        //OC.MVC.util.loadMainContainerView(url);
        var url = OC.MVC.util.getLink("Securities", "Done");
        //OC.MVC.util.loadMainContainerView(url, { viewKey: $("#ViewKey").val(), isViewConfirmation: $("#IsViewConfirmation").val() });
      OC.MVC.util._loadView("mainContent", url, { viewKey: $("#ViewKey").val(), isViewConfirmation: $("#IsViewConfirmation").val() });
    },
    OnDownloadReceiptLinkClicked: function (events) {
        
        var self = events.data.obj;
        var url = OC.MVC.util.getLink("Securities", "GenerateSaleReceipt");
        if (self.transactionType.toLowerCase() == "transfer")
            url = OC.MVC.util.getLink("Securities", "GenerateTransferReceipt");
        window.location = url + "?viewKey=" + $("#ViewKey").val() + "&transactionId=" + $("#RecordId").val();
    }

};IC.Public.Plans.securities.gridFormatter = 
{
    formatTransactionsGridActionsColumn: function (cellValue, options, rowObject) {
        {
            /*Builds the hyperlinks inside the actions column*/

            var div = $("<div>");
            var viewConfirmationHyperlink = $('<a>');
            viewConfirmationHyperlink.attr("href", "javascript:void(0);");
            viewConfirmationHyperlink.html("View Confirmation");
            viewConfirmationHyperlink.attr("data-transactionId", rowObject[0]); //Persist transaction id as part of the hyperlink tag just in case if it is required for any other processing
            viewConfirmationHyperlink.attr("data-transactionmethod", rowObject[2]); //Persist transaction method as part of the hyperlink tag just in case if it is required for any other processing            
            viewConfirmationHyperlink.appendTo(div);
            return div.html();
        }
    },
    
    planNameTooltip: function (cellValue, options, rowObject) {
        var div = $('<div>');
        var span = $('<span>');
        var index = $(this).getColIndex("PlanName");
        
        if (index >= 0) {
            span.attr("title", rowObject[index].replace('&amp;', '&').replace('&apos;', '\'').replace('&gt;', '>').replace('&lt;', '<'));
        }
        span.addClass("tooltip");
        span.html(cellValue);
        span.appendTo(div);
        return div.html();
    }
};//IC.Public.Plans.actionOptions = {
//    sellORTransfer: 1,
//    transactions: 2
//};

IC.Public.Plans.securitiesTransactions = function () {
    
};

IC.Public.Plans.securitiesTransactions.prototype = {
    init: function () {
        var gridLinksInitialised = false;
        var rowCount = 0;
        this.securityTransactionsGrid = $("#SecurityTransactionsGridModel");
        this.securityTransactionsGrid.bind("grid_gridComplete", { obj: this }, function (events, gridContext) {
            rowCount = gridContext;
            var _obj = events.data.obj;
            $(this).find("a:contains('View Confirmation')").each(function (index) {
                if ($(this).attr('data-transactionid') && $(this).attr('data-transactionid').length > 0) {
                    gridLinksInitialised = true;
                    $(this).bind("click", { obj: _obj }, _obj.onViewTransactionClick);
                }
            });
        });
        
        // Sometimes the grid is loaded before the gridComplete event can be binded, so we need to reload so that View Confirmation click event is binded
        if (rowCount > 0 && !gridLinksInitialised) {
            this.securityTransactionsGrid.trigger('reloadGrid');
        }
        
        //Event bindings
        var holdingFilter = $("form.holdingFilterForm");
        if (holdingFilter.length) {
            holdingFilter.unbind("applyHoldingFilter");
            holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterClick);
        }

        this.actionOption = $("[name=SecuritiesAction]");
        this.actionOption.unbind('click');
        this.actionOption.bind('click', { obj: this }, this.onActionClick);

        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.SaleOrTransfer);
        this.viewKey = $("#ViewKey");
    },

    ////**** Event Binder ****\\\\
    onFilterClick: function (events) {
        var url = IC.Public.urls.OnlineSale.OnlineSaleSellSecurities;
        OC.MVC.util.loadMainContainerView(url, null);
    },

    onActionClick: function (events) {
        var _obj = events.data.obj;
        var value = $(this).val();
        var url = IC.Public.urls.Securities.sellOrTransferList;
        if (value == IC.Public.Plans.actionOptions.transactions)
            url = IC.Public.urls.Securities.transactionsList;
        //OC.MVC.util.loadView("page-container", url, { viewKey: _obj.viewKey.val() });
        OC.MVC.util.loadMainContainerView(url, { viewKey: _obj.viewKey.val() });
    },

    onViewTransactionClick: function () {
        var url = OC.MVC.util.getLink("Securities", "ViewConfirmationDetails");
        var data = { transactionId: $(this).attr("data-transactionid") || "", viewKey: $("#ViewKey").val(), transactionMethod: $(this).attr("data-transactionmethod") || "" };
        IC.Public.showLoading("");
        OC.MVC.util.loadMainContainerView(url, data);
        IC.Public.hideLoading();
    }
};IC.Public.Plans.BankAccountDetails = function (options) {
    if (options) {
        $.extend(this, options);
    }
};

IC.Public.Plans.BankAccountDetails.prototype = {
    showModal: function (proceedSeqNumber, menuSubItemType, validateTransactionPassword, paymentMethodCode, paymentMethodDesc) {
        var self = this;
        self.proceedSeqNumber = proceedSeqNumber;
        self.validateTransactionPassword = validateTransactionPassword;
        var planCodes = null;
        if (menuSubItemType == IC.Public.Plans.Common.MenuSubItemTypes.Exercise) {
            planCodes = $('#ExerciseDetailsGrid').getPostDataItem('plans');
        }
        if (menuSubItemType == IC.Public.Plans.Common.MenuSubItemTypes.LoanRepayment) {
            if ($("#FormTypeValue").val() == IC.Public.Plans.Common.LoanRepaymentFormType.PartialSales) {
                planCodes = $('#PartialSharesSellingRepaymentDetailsGrid').getPostDataItem('plans');
            }
            if ($("#FormTypeValue").val() == IC.Public.Plans.Common.LoanRepaymentFormType.Sales) {
                planCodes = $('#ShareSellingRepaymentDetailsGrid').getPostDataItem('plans');
            }
        }
        if (proceedSeqNumber.length >= 2 && proceedSeqNumber.substring(0, 2) == '-1') {
            OC.MVC.util.showModal('PaymentMethod', 'NewBankAccountDetails',
                { viewKey: $('#ViewKey').val(), paymentMethodCode: paymentMethodCode, paymentMethodDesc: paymentMethodDesc, menuSubItemType: menuSubItemType },
                { minWidth: 600, width: 600, hideCloseButton: true }, function() { self.init(self.proceedSeqNumber, self.validateTransactionPassword); }
            );
        } else {
            OC.MVC.util.showModal('PaymentMethod', 'BankAccountDetails',
                { proceedSeqNumber: proceedSeqNumber, viewKey: $('#ViewKey').val(), planCodes: planCodes, menuSubItemType: menuSubItemType, paymentMethodCode: paymentMethodCode },
                { minWidth: 600, width: 600, hideCloseButton: true }, function () { self.init(self.proceedSeqNumber, self.validateTransactionPassword); }
            );
        }
    },
    init: function (proceedSeqNumber, validateTransactionPassword) {
        IC.Public.hideLoading();
        this.proceedSeqNumber = proceedSeqNumber;
        this.validateTransactionPassword = validateTransactionPassword;
        var self = this;

        this.cancelButton = $('#btnCancelUpdate');
        this.cancelButton.unbind('click');
        this.cancelButton.bind('click', function () {
            $.modal.close();
        });

        this.updateButton = $('#btnUpdate');
        this.updateButton.unbind('click');
        this.updateButton.bind('click', { obj: self }, self.onUpdateClick);

        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnUpdate').click();
                }
            }
        });

        this.paymentTypesDropDown = $('#PaymentType');
        this.paymentTypesDropDown.unbind('change');
        this.paymentTypesDropDown.bind('change', { obj: self }, self.onPaymentTypeChange);
        this.buildingSocietyRefDiv = $('#buildingSocietyRefDiv');
        this.showOrHideBuildingSocietyRefField(this.paymentTypesDropDown.val());
        this.suffixDiv = $('#suffixDiv');
        this.showOrHideSuffixField(this.paymentTypesDropDown.val());

        this.smsPinRegisterLink = $("#bankAccountDetailsForm").find("#smsPinRegisterLink");
        this.smsPinRegisterLink.unbind('click');
        this.smsPinRegisterLink.bind('click', { obj: this }, this.onSmsPinRegisterLinkClick);

        this.closeButton = $('#btnClose');
        this.closeButton.unbind('click');
        this.closeButton.bind('click', { obj: self }, self.onCloseClick);

        $('#simplemodal-container').css('width', '600px');
        this.adjustModalSizeAndPosition();
    },
    
    onSmsPinRegisterLinkClick: function (eventArgs) {
        $.modal.close();
        var url = IC.Public.urls.Settings.smsPinRegistration;
        OC.MVC.util.loadMainContainerView(url);
    },

    onCloseClick: function (events) {
        var self = events.data.obj;
        //Refresh bank details
        $('#bsbSpan_-1' + $("input[name=PaymentMethod]:checked").val()).text($('#BsbCode').val());
        $('#accountNumberSpan_-1' + $("input[name=PaymentMethod]:checked").val()).text($('#AccountNumber').val());
        $('#updateAccount_-1' + $("input[name=PaymentMethod]:checked").val()).text('Update');
        $('#updateAccount_-1' + $("input[name=PaymentMethod]:checked").val()).attr('proceedseqnumber', $('#PSN').val());
        $("input[name=PaymentMethod]:checked").attr('payment-proceed-seq-number', $('#PSN').val());


        //setting the ul payment details div when new account added ,this div shows on respective payment method radio button click.
        var idVal = "paymentDetails_" + $("input[name=PaymentMethod]:checked").val() + "_" + $('#PSN').val();
        var ulPaymentDetailDiv = $("input[name=PaymentMethod]:checked").parent();
        if (ulPaymentDetailDiv && ulPaymentDetailDiv.siblings("ul.method-details:first")) {
            ulPaymentDetailDiv.siblings("ul.method-details:first").attr('id', idVal);
        }

        //Un-checking the Accept T&C checkbox so that validation for continue button get fired. It is difficult to fire processContinueButton() from here as that method resides in IC.Public.Plans.securities
        $('#TermsAndConditionsAccepted').attr('checked', false);
        var paymentmethod = $('#PaymentMethodCode').val();
        var bsbid = 'bsbSpan_' + $('#ProceedSeqNumber').val();
        var paytype = $('#PaymentType').val();
        if (paytype.toLowerCase() == 'ggy' || paytype.toLowerCase() == 'ism' || paytype.toLowerCase() == 'imn' || paytype.toLowerCase() == 'jey' || paytype.toLowerCase() == 'gbr') {
            $("label[for='" + bsbid + "']").text("Sort Code");
        }
        else if (paytype.toLowerCase() == "nzl") {
            $("label[for='" + bsbid + "']").text("Bank/Branch");
        }

        else {
            $("label[for='" + bsbid + "']").text("BSB");

        }
        $.modal.close();
    },

    onUpdateClick: function (events) {
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        
        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handleUpdateBankAccountDetails(events);
            return;
        }

        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        var formData = $('form#bankAccountDetailsForm').serializeObject();
        IC.Public.security.validateTransactionPin(formData, function () { self.handleUpdateBankAccountDetails(events); }, function () { $("#btnUpdate").parent().addClass('disabled'); });
        self.adjustModalSizeAndPosition();
    },

    handleUpdateBankAccountDetails: function (events) {
        var isElectedDividendAccount = $('#IsElectedDividendAccount').val().toLowerCase();
        if (isElectedDividendAccount == 'true') {
            if (!confirm("Are you sure you want to update your dividend acccount details?")) {
                return false;
            }
        }

        var form = $('form#bankAccountDetailsForm');
        var data = form.serializeObject();
        var self = events.data.obj;
        IC.Public.showLoading("");
        $.ajax({
            url: IC.Public.urls.PaymentMethod.bankAccountDetails,
            type: 'POST',
            data: data,
            success: function (result) {
                IC.Public.hideLoading();
                $("#simplemodal-data").html(result);
                self.init($('#ProceedSeqNumber').val(), self.validateTransactionPassword);
                self.adjustModalSizeAndPosition();
                self.processPaymentMethodCheckBox();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },

    adjustModalSizeAndPosition: function () {
        $('#simplemodal-container').css('height', 'auto');
        $.modal.setPosition();
    },

    processPaymentMethodCheckBox: function () {
        var isSuccess = $('#updatedSucceededMessage').is(':visible');
        if (!isSuccess) {
            return;
        }
        // Enable the payment method checkbox and hide the Specify Account link
        var isNewAccount = $('#IsNewAccount').val().toLowerCase() == "true";
        var proceedSeqNumber = $('#PSN').val();
        var savedProceedSeqNumber = $('#PSN').val();

        // Continue button fix 3758 - after add new bank account update the process sequecnce number to enable continue button
        if ($("#payment_methods_FDC") != undefined) {
            $("#payment_methods_FDC").data("payment-proceed-seq-number", savedProceedSeqNumber);
        }
        if ($("#payment_methods_LDC") != undefined) {
            $("#payment_methods_LDC").data("payment-proceed-seq-number", savedProceedSeqNumber);
        }

        if (isNewAccount) {
            proceedSeqNumber = "-1" + $('#PaymentMethodCode').val();

            var updateAccountLink = $('a[id=updateAccount_' + proceedSeqNumber + ']');
            updateAccountLink.attr('id', 'updateAccount_' + savedProceedSeqNumber);
            updateAccountLink.attr('proceedseqnumber', savedProceedSeqNumber);
            updateAccountLink.text("Update");
        }

        var paymentMethodCheckBox = $('input[data-payment-proceed-seq-number=' + proceedSeqNumber + ']');
        if (paymentMethodCheckBox != undefined) {
            paymentMethodCheckBox.removeAttr('disabled');
            if (isNewAccount) {
                paymentMethodCheckBox.attr('checked', 'checked');
                paymentMethodCheckBox.trigger('click');
            }
        }

        var specifyAccountLink = $('a[id=addAccount_' + proceedSeqNumber + ']');
        if (specifyAccountLink != undefined)
            specifyAccountLink.hide();

        // Update the save values in the payment method details section
        var bsbSpan = $('#bsbSpan_' + proceedSeqNumber);
        if (bsbSpan != undefined) {
            bsbSpan.text($('#BsbCode').val());
            bsbSpan.attr('id', 'bsbSpan_' + savedProceedSeqNumber);
        }

        var accountNumberSpan = $('#accountNumberSpan_' + proceedSeqNumber);
        if (accountNumberSpan != undefined) {
            accountNumberSpan.text($('#MaskedAccountNumber').val());
            accountNumberSpan.attr('id', 'accountNumberSpan_' + savedProceedSeqNumber);
        }
    },

    onPaymentTypeChange: function (events) {
        var viewkey = $('#ViewKey').val();
        var ProceedSeqNumber = $("#ProceedSeqNumber").val();
        var PlanCodes = $('#PlanCodes').val();

        $('.errorContainer').hide();
        
        $('#AccountName').removeClass('input-validation-error');
        $('#BsbCode').removeClass('input-validation-error');
        $('#AccountNumber').removeClass('input-validation-error');
        $('#Suffix').removeClass('input-validation-error');
        var self = events.data.obj;
        var paymentType = $(this).val();
        self.showOrHideBuildingSocietyRefField(paymentType);
        self.showOrHideSuffixField(paymentType);
        var url = OC.MVC.util.getLink("PaymentMethod", "GetBankDetails");

        $.ajax({
            url: url,
            data: { 'proceedSeqNumber': ProceedSeqNumber, 'viewKey': viewkey, 'menuSubItemType': 'Securities', 'planCodes': null, 'Paymentcode': paymentType },
            type: 'GET',

            success: function (data) {
                if (data != "") {
                    var arr = data.split(',');
                    $('#AccountName').val(arr[0]);
                    $('#BsbCode').val(arr[1]);
                    $('#AccountNumber').val(arr[2]);
                    $('#BuildingSocietyRef').val(arr[3]);
                }
                else {
                    $('#AccountName').val("");
                    $('#BsbCode').val("");
                    $('#AccountNumber').val("");
                    $('#BuildingSocietyRef').val("");
                }
            },
            error: function (e)
            { }
        });

        if (paymentType.toLowerCase() == 'ggy' || paymentType.toLowerCase() == 'ism' || paymentType.toLowerCase() == 'imn' || paymentType.toLowerCase() == 'jey' || paymentType.toLowerCase() == 'gbr') {
            $('#lblSortCode').show();
            $('#lblSortCode').html('Sort code');
            $('#lblBsbCode').hide();
        }
        else if (paymentType.toLowerCase() == 'nzl') {
            $('#lblSortCode').show();
            $('#lblSortCode').html('Bank/Branch');
            $('#lblBsbCode').hide();
        }
        else {
            $('#lblSortCode').hide();
            $('#lblBsbCode').show();

        }
        






    },

    showOrHideBuildingSocietyRefField: function (paymentType) {
        if (paymentType == undefined)
            return;

        if (paymentType.toLowerCase() == 'gbr') {
            this.buildingSocietyRefDiv.show();
        } else {
            this.buildingSocietyRefDiv.hide();
        }
        $.modal.setContainerDimensions();
    },

    showOrHideSuffixField: function (paymentType) {
        if (paymentType == undefined)
            return;

        if (paymentType.toLowerCase() == 'nzl') {
            this.suffixDiv.show();
        } else {
            this.suffixDiv.hide();
        }
        $.modal.setContainerDimensions();
    }
};
;IC.Public.Plans.InternationalBankWire = function (options) {
    if (options) {
        $.extend(this, options);
    }
};

IC.Public.Plans.InternationalBankWire.prototype = {
    showModal: function (proceedSeqNumber, menuSubItemType) {
        var self = this;
        self.proceedSeqNumber = proceedSeqNumber;
        
        var planCodes = null;
        if (menuSubItemType == IC.Public.Plans.Common.MenuSubItemTypes.Exercise) {
            planCodes = $('#ExerciseDetailsGrid').getPostDataItem('plans');
        }
        if (menuSubItemType == IC.Public.Plans.Common.MenuSubItemTypes.LoanRepayment) {
            if ($("#FormTypeValue").val() == IC.Public.Plans.Common.LoanRepaymentFormType.PartialSales) {
                planCodes = $('#PartialSharesSellingRepaymentDetailsGrid').getPostDataItem('plans');
            }
            if ($("#FormTypeValue").val() == IC.Public.Plans.Common.LoanRepaymentFormType.Sales) {
                planCodes = $('#ShareSellingRepaymentDetailsGrid').getPostDataItem('plans');
            }
        }
        if (proceedSeqNumber.length >= 2 && proceedSeqNumber.substring(0, 2) == '-1')
            proceedSeqNumber = "-1";
            
        OC.MVC.util.showModal('PaymentMethod', 'InternationalBankWire',
            {
                proceedSeqNumber: proceedSeqNumber,
                viewKey: $('#ViewKey').val(),
                menuSubItemType: menuSubItemType,
                planCodes: planCodes
            },
            {
                minWidth: 367,
                width: 400
            },
            function () { self.init(self.proceedSeqNumber); });
    },

    init: function (proceedSeqNumber) {
        IC.Public.hideLoading();
        this.proceedSeqNumber = proceedSeqNumber;
        var self = this;
        $('#btnUpdate').unbind('click');
        $('#btnUpdate').bind('click', { obj: self }, self.onUpdateClick);

        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnUpdate').click();
                }
            }
        });

        this.cancelButton = $('#btnCancelUpdate');
        this.cancelButton.unbind('click');
        this.cancelButton.bind('click', function () {
            $.modal.close();
        });
        
        this.closeButton = $('#btnClose');
        this.closeButton.unbind('click');
        this.closeButton.bind('click', function() {
            $.modal.close();
        });
        
        var countryCode = $('#BeneficiaryBankDetails_CountryCode');
        countryCode.unbind("change");
        countryCode.bind("change", { obj: this }, this.onCountryCodeChange);
        this.processAddressFields(countryCode);
        
        countryCode = $('#IntermediaryBankDetails_CountryCode');
        countryCode.unbind("change");
        countryCode.bind("change", { obj: this }, this.onCountryCodeChange);
        this.processAddressFields(countryCode);
        this.adjustModalSizeAndPosition();
    },
    
    onCountryCodeChange: function(events) {
        var self = events.data.obj;
        self.processAddressFields($(this));
    },

    processAddressFields: function(countryCodeControl) {
        if (countryCodeControl.val() == IC.Public.Plans.Common.Countries.Australia)
            countryCodeControl.parent().parent().find('.ausAddresFields').show();
        else 
            countryCodeControl.parent().parent().find('.ausAddresFields').hide();
    },

    onUpdateClick: function (events) {
        var form = $('form#internationalBankWireForm');
        var data = form.serializeObject();
        var self = events.data.obj;
        IC.Public.showLoading("");
        $.ajax({
            url: IC.Public.urls.PaymentMethod.internationalBankWire,
            type: 'POST',
            data: data,
            success: function (result) {
                IC.Public.hideLoading();
                if (!OC.MVC.events.beforeLoadHtml(result))
                    return;
                
                $("#simplemodal-data").html(result);
                self.init($('#ProceedSeqNumber').val());
                self.adjustModalSizeAndPosition();
                self.processPaymentMethodCheckBox();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    
    adjustModalSizeAndPosition: function(){
        $('#simplemodal-container').css('height', 'auto');
        $('#simplemodal-container').css('width', '650px');
        $.modal.setPosition();
    },
    
    processPaymentMethodCheckBox: function () {
        var isSuccess = $('#updatedSucceededMessage').is(':visible');
        if (!isSuccess)
            return;

        // Enable the payment method checkbox and hide the Specify Account link
        var isNewAccount = $('#IsNewAccount').val().toLowerCase() == "true";
        var proceedSeqNumber = $('#ProceedSeqNumber').val();
        var savedProceedSeqNumber = $('#SavedProceedSeqNumber').val();

        // Continue button fix 3594 - after add new bank account update the process sequecnce number to enable continue button
        if ($("#payment_methods_IBW") != undefined) {
            $("#payment_methods_IBW").data("payment-proceed-seq-number", savedProceedSeqNumber);
        }

        if (isNewAccount) {
            proceedSeqNumber = "-1IBW";
            
            var updateAccountLink = $('a[id=updateAccount_' + proceedSeqNumber + ']');
            updateAccountLink.attr('id', 'updateAccount_' + savedProceedSeqNumber);
            updateAccountLink.attr('proceedseqnumber', savedProceedSeqNumber);
            updateAccountLink.text("Update");
        }
        var paymentMethodCheckBox = $('input[data-payment-proceed-seq-number=' + proceedSeqNumber + ']');
        if (paymentMethodCheckBox != undefined) {
            paymentMethodCheckBox.removeAttr('disabled');
            if (isNewAccount) {
                paymentMethodCheckBox.attr('checked', 'checked');
                paymentMethodCheckBox.trigger('click');
            }
        }

        var specifyAccountLink = $('a[id=addAccount_' + proceedSeqNumber + ']');
        if (specifyAccountLink != undefined)
            specifyAccountLink.hide();

        //update the proceed seq number if this is created for the first time
        //var updateAccountLink = $('a[id=updateAccount_' + proceedSeqNumber + 'IBW]');
        //if (updateAccountLink != undefined) {
        //    updateAccountLink.attr('proceedseqnumber', proceedSeqNumber);
        //}

        // Update the save values in the payment method details section
        this.updateBeneficiaryBankDetails(proceedSeqNumber, savedProceedSeqNumber);
        this.updateIntermdeiaryBankDetails(proceedSeqNumber, savedProceedSeqNumber);
    },
    
    updateBeneficiaryBankDetails: function (proceedSeqNumber, savedProceedSeqNumber) {
        this.updateSpanValue($('#beneficiaryBankCurrenyCode_' + proceedSeqNumber), $('#BeneficiaryBankDetails_CurrencyCode').val(), 'beneficiaryBankCurrenyCode_' + savedProceedSeqNumber);
        this.updateSpanValue($('#beneficiaryBankSwiftCode_' + proceedSeqNumber), $('#BeneficiaryBankDetails_SwiftCode').val(), 'beneficiaryBankSwiftCode_' + savedProceedSeqNumber);
        this.updateSpanValue($('#beneficiaryBankIban_' + proceedSeqNumber), $('#BeneficiaryBankDetails_IBAN').val(), 'beneficiaryBankIban_' + savedProceedSeqNumber);
        this.updateSpanValue($('#beneficiaryBankSortCode_' + proceedSeqNumber), $('#BeneficiaryBankDetails_SortCode').val(), 'beneficiaryBankSortCode_' + savedProceedSeqNumber);
        this.updateSpanValue($('#beneficiaryBankAccountName_' + proceedSeqNumber), $('#BeneficiaryBankDetails_AccountName').val(), 'beneficiaryBankAccountName_' + savedProceedSeqNumber);
        this.updateSpanValue($('#beneficiaryBankAccountNumber_' + proceedSeqNumber), $('#BeneficiaryBankDetails_MaskedAccountNumber').val(), 'beneficiaryBankAccountNumber_' + savedProceedSeqNumber);

        this.updateSpanValue($('#beneficiaryBankName_' + proceedSeqNumber), $('#BeneficiaryBankDetails_BankName').val(), 'beneficiaryBankName_' + savedProceedSeqNumber);
        this.updateSpanValue($('#beneficiaryAddressLine1_' + proceedSeqNumber), $('#BeneficiaryBankDetails_BankAddress_AddressLine1').val(), 'beneficiaryAddressLine1_' + savedProceedSeqNumber);
        this.updateSpanValue($('#beneficiaryAddressLine2_' + proceedSeqNumber), $('#BeneficiaryBankDetails_BankAddress_AddressLine2').val(), 'beneficiaryAddressLine2_' + savedProceedSeqNumber);
        this.updateSpanValue($('#beneficiaryAddressLine3_' + proceedSeqNumber), $('#BeneficiaryBankDetails_BankAddress_AddressLine3').val(), 'beneficiaryAddressLine3_' + savedProceedSeqNumber);
        this.updateSpanValue($('#beneficiaryAddressLine4_' + proceedSeqNumber), $('#BeneficiaryBankDetails_BankAddress_AddressLine4').val(), 'beneficiaryAddressLine4_' + savedProceedSeqNumber);

        if ($('#BeneficiaryBankDetails_BankAddress_CountryCode').val() == IC.Public.Plans.Common.Countries.Australia) {
            this.updateSpanValue($('#beneficiaryTown_' + proceedSeqNumber), $('#BeneficiaryBankDetails_BankAddress_Town').val(), 'beneficiaryTown_' + savedProceedSeqNumber);
            this.updateSpanValue($('#beneficiaryState_' + proceedSeqNumber), $('#BeneficiaryBankDetails_BankAddress_StateName').val(), 'beneficiaryState_'  + savedProceedSeqNumber);
            this.updateSpanValue($('#beneficiaryPostCode_' + proceedSeqNumber), $('#BeneficiaryBankDetails_BankAddress_PostCode').val(), 'beneficiaryPostCode_' + savedProceedSeqNumber);
        } else {
            $('#beneficiaryTown_' + proceedSeqNumber).parent().hide();
            $('#beneficiaryState_' + proceedSeqNumber).parent().hide();
            $('#beneficiaryPostCode_' + proceedSeqNumber).parent().hide();
        }
        this.updateSpanValue($('#beneficiaryCountry_' + proceedSeqNumber), $('#BeneficiaryBankDetails_BankAddress_CountryName').val(), 'beneficiaryCountry_' + savedProceedSeqNumber);
    },
    
    updateSpanValue: function (spanControl, value, idValue) {
        if (value == undefined) {
            spanControl.parent().hide();
            return;
        }
        
        value = value.trim();
        if (spanControl != undefined) {
            spanControl.text(value);
            if (value != '')
                spanControl.parent().show();
            else
                spanControl.parent().hide();

            spanControl.attr('id', idValue);
        }
    },
        
    updateIntermdeiaryBankDetails: function (proceedSeqNumber, savedProceedSeqNumber) {
        this.updateSpanValue($('#intermediaryBankSwiftCode_' + proceedSeqNumber), $('#IntermediaryBankDetails_SwiftCode').val(), 'intermediaryBankSwiftCode_' +savedProceedSeqNumber);
        this.updateSpanValue($('#intermediaryBankIban_' + proceedSeqNumber), $('#IntermediaryBankDetails_IBAN').val(), 'intermediaryBankIban_' + savedProceedSeqNumber);
        this.updateSpanValue($('#intermediaryBankAba_' + proceedSeqNumber), $('#IntermediaryBankDetails_ABA').val(), 'intermediaryBankAba_' + savedProceedSeqNumber);
        this.updateSpanValue($('#intermediaryBankSortCode_' + proceedSeqNumber), $('#IntermediaryBankDetails_SortCode').val(), 'intermediaryBankSortCode_' + savedProceedSeqNumber);
        this.updateSpanValue($('#intermediaryBankBranch_' + proceedSeqNumber), $('#IntermediaryBankDetails_Branch').val(), 'intermediaryBankBranch_' + savedProceedSeqNumber);
        this.updateSpanValue($('#intermediaryBankAccountNumber_' + proceedSeqNumber), $('#IntermediaryBankDetails_MaskedAccountNumber').val(), 'intermediaryBankAccountNumber_' + savedProceedSeqNumber);
        
        this.updateSpanValue($('#intermediaryBankAccountNumber_' + proceedSeqNumber), $('#IntermediaryBankDetails_BankName').val(), 'intermediaryBankAccountNumber_' + savedProceedSeqNumber);
        this.updateSpanValue($('#intermediaryAddressLine1_' + proceedSeqNumber), $('#IntermediaryBankDetails_BankAddress_AddressLine1').val(), 'intermediaryAddressLine1_' + savedProceedSeqNumber);
        this.updateSpanValue($('#intermediaryAddressLine2_' + proceedSeqNumber), $('#IntermediaryBankDetails_BankAddress_AddressLine2').val(), 'intermediaryAddressLine2_' + savedProceedSeqNumber);
        this.updateSpanValue($('#intermediaryAddressLine3_' + proceedSeqNumber), $('#IntermediaryBankDetails_BankAddress_AddressLine3').val(), 'intermediaryAddressLine3_' + savedProceedSeqNumber);
        this.updateSpanValue($('#intermediaryAddressLine4_' + proceedSeqNumber), $('#IntermediaryBankDetails_BankAddress_AddressLine4').val(), 'intermediaryAddressLine4_' + savedProceedSeqNumber);

        if ($('#IntermediaryBankDetails_BankAddress_CountryCode').val() == IC.Public.Plans.Common.Countries.Australia) {
            this.updateSpanValue($('#intermediaryTown_' + proceedSeqNumber), $('#IntermediaryBankDetails_BankAddress_Town').val(), 'intermediaryTown_' + savedProceedSeqNumber);
            this.updateSpanValue($('#intermediaryState_' + proceedSeqNumber), $('#IntermediaryBankDetails_BankAddress_StateName').val(), 'intermediaryState_' + savedProceedSeqNumber);
            this.updateSpanValue($('#intermediaryPostCode_' + proceedSeqNumber), $('#IntermediaryBankDetails_BankAddress_PostCode').val(), 'intermediaryPostCode_' + savedProceedSeqNumber);
        } else {
            $('#intermediaryTown_' + proceedSeqNumber).parent().hide();
            $('#intermediaryState_' + proceedSeqNumber).parent().hide();
            $('#intermediaryPostCode_' + proceedSeqNumber).parent().hide();
        }
        this.updateSpanValue($('#intermediaryCountry_' + proceedSeqNumber), $('#IntermediaryBankDetails_BankAddress_CountryName').val(), 'intermediaryCountry_' + savedProceedSeqNumber);
    }
};;IC.Public.Plans.BankAddress = function (options) {
    this.init();
};

IC.Public.Plans.BankAddress.prototype = {
    init: function() {
        this.countryCode = $('#CountryCode');
        this.countryCode.unbind('change');
        this.countryCode.bind('change', { obj: this }, this.onCountryCodeChange);
        this.processAddressFields();
    },
    
    onCountryCodeChange: function(events) {
        var self = events.data.obj;
        self.processAddressFields();
    },

    processAddressFields: function() {
        if (this.countryCode.val() == "AUS") 
            $('.ausAddresFields').show();
        else 
            $('.ausAddresFields').hide();
    }
};;IC.Public.Plans.Common.MenuSubItemTypes =
{
    Exercise: 3,
    Securities: 4,
    LoanRepayment:74
};

IC.Public.Plans.Common.LoanRepaymentFormType =
{
    Sales : "Sales",
    PartialSales : "PartialSales",
    Cash: "Cash",
    ConfirmCash: "ConfirmCash"
};

IC.Public.Plans.Common.Countries =
{
    Australia: "AUS",
    NewZealandCountryCode: "NZL"
};

IC.Public.Plans.Common.DateFilterTypes =
{
    Range: 1,
    AsAt: 2
};

IC.Public.Plans.Common.CampaignNames =
{    
    PlanSummary: "PlanSummary",
    Offers: "Offers",
    Exercise: "Exercise",
    SaleOrTransfer: "SaleOrTransfer",
    Performance: "Performance",
    LoanRepaymentCash: "LoanRepaymentDetailsCash"
};

IC.Public.Plans.Common.SummaryGridType =
{    
    Overview :"0",
    TargetReward :"1",
    RewardReceived : "2",
    Plans : "3",
    PlanDocuments : "4",
    VestingCalculator : "5",
    PerformanceTracker : "6"
};

IC.Public.Plans.Common.SourcePage =
{
    PlanSummary: 0,
    Performance: 1
};

IC.Public.Plans.Common.VestingType =
{
    PerformanceOrTime: "PT",
    Performance: "PP"
};

IC.Public.Plans.Common.ImrMaximumYAxisValue = 50;
IC.Public.Plans.Common.GraphLineMinValue = 50;
IC.Public.Plans.Common.GraphLineMaxValue = 75;
IC.Public.Plans.Common.GraphLineMinValueLegendText = '50th percentile';
IC.Public.Plans.Common.GraphLineMaxValueLegendText = '75th percentile';
IC.Public.Plans.Common.GraphLineMinValueColor = 'red';
IC.Public.Plans.Common.GraphLineMaxValueColor = 'green';
IC.Public.Plans.Common.ImperonateUserNoAccessMessage = "Not able to continue. Impersonated user cannot process this request.";

;IC.Public.Plans.GridFormatter = function(options) {
    if (options) {
    }
};

IC.Public.Plans.GridFormatter = {
    restrictedValueTooltip: function (cellValue, options, rowObject) {
        var uniqueId = IC.Public.gridFormatter.getCellValue($(this), "GrantID", rowObject);
        var restrictedBalance=IC.Public.gridFormatter.getCellValue($(this), "RestrictedBalance", rowObject);
        var stockLastPrice=$("#hidStockLastPrice").val();
        var clueToolTip = $('<div>');
        var formattedCellValue = $.fmatter.util.NumberFormat(cellValue, { decimalSeparator: ".", thousandsSeparator: ",", decimalPlaces: 2 });
        var a = $('<a>');
        a.attr("href", "#");
        a.attr("rel", ".tooltip" + uniqueId);
        a.attr("class", "cluetooltip");
        a.html(formattedCellValue);
        a.appendTo(clueToolTip);

        var toolTipContent = $('<div>');
        toolTipContent.attr("class", "tooltip" + uniqueId);

        var toolTipTitle = "Value is an estimate based on:";
        toolTipContent.html("<p class='balanceTooltipTitle'>" + toolTipTitle + "</p>");

        var tooltipMessage = "<p>The restricted balance:" + restrictedBalance + "</p>";
        tooltipMessage += "<p>20-minute-delayed market price: (AUD) " + stockLastPrice + "</p>";
        
        toolTipContent.html(toolTipContent.html() + tooltipMessage);
        toolTipContent.appendTo(clueToolTip);
        return clueToolTip.html();
    },
    
    vestingContiionTooltip: function (cellValue, options, rowObject) {
        var div = $('<div>');
        var span = $('<span>');

        span.attr('title', cellValue);
        span.addClass("tooltip");
        span.html(cellValue);
        span.appendTo(div);
        return div.html();
    },
    
    performancePlanNameTooltip: function (cellValue, options, rowObject) {
        var div = $('<div>');
        var span = $('<span>');

        span.attr('title', cellValue);
        span.addClass("tooltip");
        span.html(cellValue);
        span.appendTo(div);
        return div.html();
    },
    
    performanceLatestTestResultTooltip: function (cellValue, options, rowObject) {
        if (cellValue == '')
            return cellValue;
        
        var reportDate = IC.Public.gridFormatter.getCellValue($(this), "ReportDate", rowObject);
        var uniqueId = IC.Public.gridFormatter.getCellValue($(this), "GrantId", rowObject);
        
        var clueToolTip = $('<div>');
        var a = $('<a>');
        a.attr("href", "#");
        a.attr("rel", ".tooltip" + uniqueId);
        a.attr("class", "cluetooltip latestTestResult");
        a.html(cellValue);
        a.appendTo(clueToolTip);
        
        var toolTipContent = $('<div>');
        toolTipContent.attr("class", "tooltip" + uniqueId);

        var tooltipMessage = "<p>The latest test result was calculated on " + reportDate + "</p>";
        toolTipContent.html(tooltipMessage);

        toolTipContent.appendTo(clueToolTip);
        return clueToolTip.html();
    }
};IC.Public.Plans.message = function() {

};

IC.Public.Plans.message.prototype = {
    showModal: function (type, planCode, viewKey, transactionType, windowId, menuSubItemType) {
        var self = this;
        var url = (type == 'blackout') ? IC.Public.urls.Message.blackoutMessage : IC.Public.urls.Message.preClearanceMessage;

        self.type = type;
        self.planCode = planCode;
        self.viewKey = viewKey;
        self.transactionType = transactionType;
        self.windowId = windowId;

        var data = {
            planCode: planCode,
            viewKey: viewKey,
            transactionType: transactionType,
            windowId: windowId,
            menuSubItemType: menuSubItemType
        };
        OC.MVC.util.showModalByUrl(url, data,
               { maxHeight: 300, maxWidth: 500, hideCloseButton: false }, function () { self.init(); });
    },
    
    init: function () {
        
        var self = this;
        self.registerEvents();
        self.adjustModalSizeAndPosition();
    },
    
    registerEvents: function () {
        
        this.requestPreClearance = $('#btnRequestPreClearance');
        this.requestPreClearance.unbind('click');
        this.requestPreClearance.bind('click', { obj: this }, this.onRequestPreClearanceClick);
    },

    onRequestPreClearanceClick: function (events) {
       
        var self = events.data.obj;
        IC.Public.showLoading("");
        $.ajax({                
            url: OC.MVC.util.getLink("Message", "RequestPreClearance"),
            type: 'POST',
            data: { viewKey: self.viewKey, transactionType: self.transactionType, windowId: self.windowId, sellNumber: $('#simplemodal-container').find("#SellNumber").val(), ReasonForTrade: $("#ReasonTrade").val(), otherSecuritiesOwned: $("#OtherSecuritiesOwned").val(), confirmRequestPreClearance: $("#ConfirmRequestPreClearance").is(':checked') },
            success: function (result) {
                if (result && result.IsSuccess) {
                    self.displaySuccessMessage();
                } else {
                    if (result.ErrorMessage)
                        self.displayErrorMessage(result.ErrorMessage);
                }
                IC.Public.hideLoading();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    
    displaySuccessMessage: function() {
        $('#messageDiv').hide();
        $('#requestPreclearanceSuccessMessageDiv').show();
        $('#sellNumberDiv').hide();
        $('#sellNumberTextBoxDiv').hide();
        $('#reasonForTradeDiv').hide();
        $('#otherSecuritiesOwnedDiv').hide();
        $('#IsConfirmRequestPreClearanceDiv').hide();
        this.requestPreClearance.parent().hide();
    },
    
    displayErrorMessage: function(errorMessage) {
        $('#mainErrorDiv').html("<ol><li>" + errorMessage + "</li></ol>");
    },

    adjustModalSizeAndPosition: function () {
        $('#simplemodal-container').css('height', 'auto');
        $.modal.setPosition();
    }
};
;IC.Public.Plans.performance = function (sourcePage) {
    this.init(sourcePage);
};

IC.Public.Plans.performance.prototype = {    
    init: function (sourcePage) {
        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.Performance);
        $('#vestingCalculatorDiv').hide();
        
        //ViewBar change event
        var self = this;
        this.sourcePage = sourcePage;
        var holdingFilter = $("form.holdingFilterForm");
        if (holdingFilter.length) {
            holdingFilter.unbind("applyHoldingFilter");
            holdingFilter.bind("applyHoldingFilter", { obj: self }, self.onFilterClick);
        }
        
        this.conversionCurrency = $('#conversionCurrency');
        if (this.sourcePage == IC.Public.Plans.Common.SourcePage.Performance) {
            this.conversionCurrency.unbind('applyCurrencyChange');
            this.conversionCurrency.bind('applyCurrencyChange', { obj: this }, this.onApplyCurrencyChange);
        }

        this.performanceGrid = $('#PerformanceGrid');
        this.gview_PerformanceGrid = $('#gview_PerformanceGrid');
        this.performanceGridTotals = $('#performanceGridTotals');
        this.gridCompleteEvent();

        this.performanceForm = $('#performanceGridForm');
        this.reportErrorMessage = "The report you have requested cannot be located at this time.";

        $("a.cluetooltip,A.cluetooltip").each(function () {
            if ($(this).hasClass('latestTestResult'))
                $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 175 });
        });
        $("a.performance-chart-link").on("click", { obj: this }, this.onPerforamnceChartClick);
        $("a.performance-report-link").on("click", { obj: this }, this.onDownloadReportLinkClick);
    },
    
    onApplyCurrencyChange: function (eventArgs, currencyCode) {
        var url = OC.MVC.util.getLink("Performance", "Index");
        OC.MVC.util.loadMainContainerView(url, null);
    },
    
    onFilterClick: function (events) {
        var self = events.data.obj;
        var url = OC.MVC.util.getLink("Performance", "Index");
        if (self.sourcePage == IC.Public.Plans.Common.SourcePage.PlanSummary) {
            url = OC.MVC.util.getLink("Summary", "Summary");
        }
        
        OC.MVC.util.loadMainContainerView(url, null);
    },
    
    setGridColumnCurrencyCodes: function (currencyCode, isCurrencyConversionApplied) {
        currencyCode = (!isCurrencyConversionApplied) ? $('#hdnCurrency').val() : currencyCode;
        if (currencyCode != "-1") {
            $('#gview_PerformanceGrid').find('#valueColumnCurrency').text("(" + currencyCode + ")");
        }
    },
    
    gridCompleteEvent: function() {
        var self = this;
        var performanceGridTotals = this.performanceGridTotals;
        this.performanceGrid.bind("grid_gridComplete", { obj: this }, function (events, gridContext) {

            $(this).find("a.performance-chart-link").unbind("click");
            $(this).find("a.performance-chart-link").bind("click", { obj: self }, self.onPerforamnceChartClick);
            
            $(this).find("a.performance-report-link").unbind("click");
            $(this).find("a.performance-report-link").bind("click", { obj: self }, self.onDownloadReportLinkClick);

            // get the values from returning json model
            var isConverted = $(this).getUserDataItem('IsConverted') === 'True';
            var currency = $(this).getUserDataItem('Currency');
            var lastPrice = $(this).getUserDataItem('LastPrice');
            var origCurrency = $(this).getUserDataItem('OriginalCurrency');
            var origLastPrice = $(this).getUserDataItem('OriginalPrice');
            var fxrate = $(this).getUserDataItem('FxRate');
            var ratedate = $(this).getUserDataItem('RateDate');
            
            var conversionCurrency = self.conversionCurrency.val();
            self.setGridColumnCurrencyCodes(conversionCurrency, isConverted);
            
            var currencyConversionFailed = $(this).getUserDataItem('CurrencyConversionFailed');
            if (currencyConversionFailed) {
                IC.Public.Currency.displayErrorMessage($('#hdnCurrency').val(), $('#hidCurrencyConversionErrorRateNotFound').val(), $('#conversionCurrency'));
            }
            
            var totalIndicativeVestingAmount = $(this).getUserDataItem('TotalIndicativeVestingAmount');
            var totalIndicativeVestingValue = $(this).getUserDataItem('TotalIndicativeVestingValue');
            var originalTotalIndicativeVestingValue = $(this).getUserDataItem('OriginalTotalIndicativeVestingValue');
            
            var isPlanNameHidden = $(self.gview_PerformanceGrid.find('th').eq(0)).css('display') == 'none';
            var isDescriptionHidden = $(self.gview_PerformanceGrid.find('th').eq(1)).css('display') == 'none';
            var isGrantDateHidden = $(self.gview_PerformanceGrid.find('th').eq(2)).css('display') == 'none';
            var isPercentageVestedHidden = $(self.gview_PerformanceGrid.find('th').eq(10)).css('display') == 'none';
            var isCommencementDateHidden = $(self.gview_PerformanceGrid.find('th').eq(3)).css('display') == 'none';
            var totalRow = performanceGridTotals.find('tr').eq(0);

            var dummyRow = performanceGridTotals.find('tr').eq(2);

            if (dummyRow.length) {
                dummyRow.find('td').eq(0).attr("style", "width:" + $(self.gview_PerformanceGrid.find('th').eq(0)).width() + "px");
                dummyRow.find('td').eq(1).attr("style", "width:" + $(self.gview_PerformanceGrid.find('th').eq(1)).width() + "px");
                dummyRow.find('td').eq(2).attr("style", "width:" + $(self.gview_PerformanceGrid.find('th').eq(2)).width() + "px");
                dummyRow.find('td').eq(3).attr("style", "width:" + $(self.gview_PerformanceGrid.find('th').eq(3)).width() + "px");
                dummyRow.find('td').eq(4).attr("style", "width:" + $(self.gview_PerformanceGrid.find('th').eq(4)).width() + "px");
                dummyRow.find('td').eq(5).attr("style", "width:" + $(self.gview_PerformanceGrid.find('th').eq(5)).width() + "px");
                dummyRow.find('td').eq(6).attr("style", "width:" + $(self.gview_PerformanceGrid.find('th').eq(6)).width() + "px");
                dummyRow.find('td').eq(7).attr("style", "width:" + $(self.gview_PerformanceGrid.find('th').eq(7)).width() + "px");
                dummyRow.find('td').eq(8).attr("style", "width:" + $(self.gview_PerformanceGrid.find('th').eq(8)).width() + "px");
                dummyRow.find('td').eq(9).attr("style", "width:" + $(self.gview_PerformanceGrid.find('th').eq(9)).width() + "px");
                dummyRow.find('td').eq(10).attr("style", "width:" + $(self.gview_PerformanceGrid.find('th').eq(10)).width() + "px");
                dummyRow.find('td').eq(11).attr("style", "width:" + $(self.gview_PerformanceGrid.find('th').eq(11)).width() + "px");
                dummyRow.find('td').eq(12).attr("style", "width:" + $(self.gview_PerformanceGrid.find('th').eq(12)).width() + "px");
                
                if (isPlanNameHidden)
                    dummyRow.find('td').eq(0).hide();
                if (isDescriptionHidden)
                    dummyRow.find('td').eq(1).hide();
                if (isCommencementDateHidden)
                    dummyRow.find('td').eq(3).hide();
                if (isGrantDateHidden)
                    dummyRow.find('td').eq(2).hide();
                if (isPercentageVestedHidden)
                    dummyRow.find('td').eq(10).hide();
            }
            
            if (totalRow.length) {
                totalRow.find('td').eq(7).text(totalIndicativeVestingAmount);
                
                if (isDescriptionHidden)
                    totalRow.find('td').eq(1).hide();
                
                if (isPlanNameHidden)
                    totalRow.find('td').eq(2).hide();

                if (isCommencementDateHidden || isGrantDateHidden)
                    totalRow.find('td').eq(3).hide();
            }

            var totalValueRow = performanceGridTotals.find('tr').eq(1);
            if (totalValueRow.length) {
                totalValueRow.find('td').eq(7).find('a').text(totalIndicativeVestingValue);
                var tooltip = totalValueRow.find('td').eq(7).find('div');
                tooltip.html(tooltip.html().tooltipReplace(currency, lastPrice, '', totalIndicativeVestingValue, totalIndicativeVestingAmount));
                if (isConverted) {
                    tooltip.html(tooltip.html().tooltipReplaceWithConversion(origCurrency, origLastPrice, originalTotalIndicativeVestingValue, fxrate, ratedate));
                }
                
                // cluetip initialisation
                totalValueRow.find('a').each(function () {
                    $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 280, positionBy: 'fixed', topOffset: -60, leftOffset:70 });
                });
                
                if (isDescriptionHidden)
                    totalValueRow.find('td').eq(1).hide();

                if (isPlanNameHidden)
                    totalValueRow.find('td').eq(2).hide();

                if (isCommencementDateHidden || isGrantDateHidden)
                    totalValueRow.find('td').eq(3).hide();
            }

            performanceGridTotals.show();
            
            $("a.cluetooltip,A.cluetooltip").each(function () {
                if ($(this).hasClass('latestTestResult'))
                    $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 175 });
            });
        });
    },
    
    onPerforamnceChartClick: function (events) {
        var self = events.data.obj;
        IC.Public.removeErrorFor(undefined, self.performanceForm, self.reportErrorMessage);
        
        var grantId = $(this).attr('data-grant-id');
        var modal = new IC.Public.Plans.performanceChartModal();
        modal.showModal(grantId);
    },
    
    onDownloadReportLinkClick: function (events) {
        IC.Public.showLoading();
        var grantId = $(this).attr('data-grant-id');

        var self = events.data.obj;
        IC.Public.removeErrorFor(undefined, self.performanceForm, self.reportErrorMessage);
        
        $.ajax({
            url: OC.MVC.util.getLink("Performance", "IsTsrReportExists"),
            type: 'GET',
            data: { grantId: grantId },
            success: function (result) {
                IC.Public.hideLoading();
                if (result && result.Exists) {
                    var url = OC.MVC.util.getLink("Performance", "DownloadTsrReport");
                    window.location = url + "?grantId=" + grantId;
                } else {
                    IC.Public.displayErrorFor(undefined, self.performanceForm, self.reportErrorMessage);
                }
            },
            error: function (err) {
                IC.Public.hideLoading();
                IC.Public.displayErrorFor(undefined, self.performanceForm, self.reportErrorMessage);
            }
        });
    }
};;IC.Public.Plans.performanceChartModal = function () {
    
};

IC.Public.Plans.performanceChartModal.prototype = {
    showModal: function (grantId) {
        var self = this;
        self.grantId = grantId;
        OC.MVC.util.showModal('Performance', 'Chart',
               { grantId: grantId },
               { minWidth: 700, width: 700, hideCloseButton: false }, function () { self.init(self.grantId); }
           );
    },

    init: function (grantId) {
        var self = this;
        var chart = self.initChart();
        $.ajax({
            url: OC.MVC.util.getLink("Performance", "ChartData"),
            type: "GET",
            dataType: "json",
            data: { grantId: grantId },
            success: function (data) {
                if (data) {
                    var maxPercentitleLegendText = $('#hdnMaxPercentitleLegendText').val();
                    var minPercentitleLegendText = $('#hdnMinPercentitleLegendText').val();
                    var messages = { MaxPercentitleLegendText: maxPercentitleLegendText, MinPercentitleLegendText: minPercentitleLegendText };
                    IC.Public.chart.buildChart(data, chart, messages);
                    self.updateYAxisValues(data, chart);
                    self.updateTitle(data, chart);
                }
            }
        });
        self.adjustModalSizeAndPosition();
    },

    updateTitle: function (data, chart) {
        $.each(data.Series, function (index, series) {
            chart.yAxis[index].update({ title: { text: series.Title } });
        });
    },

    updateYAxisValues: function (data, chart) {
        $.each(data.Series, function (index, series) {
            if (series.ChangeYAxisValues && series.MaximumYAxisValue <= IC.Public.Plans.Common.ImrMaximumYAxisValue) {
                chart.yAxis[index].update({ tickPositions: [0, 10, 20, 30, 40, 50] });
            }
            else if (series.ShowGraphLines) {
                chart.yAxis[index].update({
                    plotLines: [{
                        id: 'limit-min',
                        color: IC.Public.Plans.Common.GraphLineMinValueColor,
                        width: 2,
                        value: IC.Public.Plans.Common.GraphLineMinValue,
                        zIndex: 0
                    }, {
                        id: 'limit-max',
                        color: IC.Public.Plans.Common.GraphLineMaxValueColor,
                        width: 2,
                        value: IC.Public.Plans.Common.GraphLineMaxValue,
                        zIndex: 0
                    }]
                });
            }
        });
    },

    adjustModalSizeAndPosition: function () {
        $('#simplemodal-container').css('height', 'auto');
        $.modal.setPosition();
    },

    initChart: function () {
        var dataPoints = [];

        var chart = new Highcharts.Chart({

            chart: {
                renderTo: 'performanceChartContainer',
                height: 200,
                type: 'line',
                events: {
                    redraw: function () {
                    }
                }
            },

            title: {
                text: ''
            },

            xAxis: {
                type: 'datetime',
                dateTimeLabelFormats: {
                    month: '%e. %b',
                    year: '%b'
                },
                lineWidth: 1,
                lineColor: '#CCCCCC',
                labels: {
                    formatter: function () {
                        return Highcharts.dateFormat('%e %b %y', this.value);
                    },
                    overflow: 'justify',
                    style: { fontFamily: 'arial' }
                },
                startOnTick: true,
                endOnTick: true
            },

            yAxis: {
                title: {
                    text: 'TSR (%)'
                },
                tickPositions: [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
            },

            tooltip: {
                useHTML: true,
                formatter: function () {
                    var d = new Date(this.x);
                    var s = '';
                    s += '<b>' + Highcharts.dateFormat('%d/%m/%Y', this.x) + '</b><br />';
                    $.each(this.points, function (i, point) {
                        s += '<b><span style = "color:' + point.series.color + ';">' + point.series.name + ' </span>' + ' : ' + point.y + '</b><br />';
                    });
                    return s;
                },
                shared: true
            },

            plotOptions: {
                area: {
                    stacking: 'percent',
                    lineColor: '#ffffff',
                    lineWidth: 1,
                    marker: {
                        lineWidth: 1,
                        lineColor: '#ffffff'
                    }
                },
                series: {
                    marker: {
                        enabled: false,
                        states: {
                            hover: {
                                enabled: true
                            }
                        }
                    }
                }
            },
            legend: {
                layout: 'vertical',
                align: 'right',
                verticalAlign: 'middle',
                borderWidth: 0
            },
            series: dataPoints,
            credits: false
        });

        chart.showLoading('Loading data from server...');

        return chart;
    }
};;//*******************************************************************************************************
//  
//  File:        ic.public.employeeLogin.js
//  Author:      Vladimir Nechypurenko
//  Description: JS for EmployeeLogin -> Index page
//  Review:      
//
//*******************************************************************************************************

IC.Public.employeeLogin = function (options) {
    if (options) {
        $.extend(this, options);
    }
    this.init();
};

IC.Public.employeeLogin.prototype = {
    init: function () {
        this.sidePanel = $("#sidePanel");
        this.mainMenu = $("#mainMenu");
        this.actionLinks = $("#actionLinks");

        if (this.sidePanel.length > 0)
            this.sidePanel.remove();

        if (this.mainMenu.length > 0) {
            this.mainMenu.find('ul').remove();
            this.mainMenu.removeClass('mainmenu');
            this.mainMenu.addClass('noMenu');
        }
        
        var links = this.actionLinks.find('li');
        $.each(links, function(index, value) {
            var a = $(value).find('a');
            if (a.attr('id') == 'contactUs') {
                $(value).addClass('noLeftBar');
            }
            else {
                $(value).remove();
            }
        });
    }
};IC.Public.Message = function (options) {

};

IC.Public.Message.prototype = {

    init: function (preClearanceButtonDisplayed, sellNumberShown, reasonForTradeShown, otherSecuritiesOwnedShown, confirmCheckBoxShown, isExercisePreClearance) {
        var self = this;

        this.preClearanceButtonShown = preClearanceButtonDisplayed;

        this.sellNumberShown = sellNumberShown;
        this.reasonForTradeShown = reasonForTradeShown;
        this.otherSecuritiesOwnedShown = otherSecuritiesOwnedShown;
        this.confirmCheckBoxShown = confirmCheckBoxShown;

        if (this.sellNumberShown) {
            $('#sellNumberDiv').show();
            $('#sellNumberTextBoxDiv').show();
        }
        if (this.reasonForTradeShown) {
            $('#reasonForTradeDiv').show();
        }
        if (this.otherSecuritiesOwnedShown) {
            $('#otherSecuritiesOwnedDiv').show();
        }
        
        if (this.confirmCheckBoxShown) {
            $('#IsConfirmRequestPreClearanceDiv').show();
        }

        var totSecurityShares = "";
        if (isExercisePreClearance) {
            // Exercise Securities
            totSecurityShares = $("#TotalSelected").text();
        }
        else {
            // Sell/Transfer Securities
            totSecurityShares = $("#TotalSharesSelected").text();
        }
        totSecurityShares = isNaN(parseInt(totSecurityShares.replace(",", ""))) ? 0 : parseInt(totSecurityShares.replace(",", ""));
        ////Sellnumber textbox
        if (this.sellNumberShown) {
            this.SellNumberTextBox = $('#simplemodal-container').find('#SellNumber');
            if (this.SellNumberTextBox != null) {
                this.SellNumberTextBox.attr("value", totSecurityShares);

                this.SellNumberTextBox.unbind('blur');
                this.SellNumberTextBox.bind('blur', { obj: this }, this.onSellNumberBlur);
                
                this.SellNumberTextBox.numeric();

                this.SellNumberTextBox.unbind('keypress');
                
                this.SellNumberTextBox.bind('keypress', { obj: this }, this.onSellNumberKeyPress);
                this.SellNumberTextBox.unbind('keyup');
                this.SellNumberTextBox.bind("keyup", { obj: this }, this.onSellNumberKeyUp);
                this.SellNumberTextBox.width(80);
                //this.SellNumberTextBox.focus();
            }
        }
        //Reason for trade textbox
        if (this.reasonForTradeShown) {
            this.defaultReasonForTrade = $("#hdReasonForTrade");
            this.ReasonForTrade = $("#ReasonTrade");
            if (this.ReasonForTrade != null) {
                this.ReasonForTrade.unbind('focus');
                this.ReasonForTrade.bind("focus ", { obj: this }, this.onReasonOfTradeFocus);
                this.ReasonForTrade.unbind('click');
                this.ReasonForTrade.bind("click", { obj: this }, this.onReasonOfTradeClick);
                this.ReasonForTrade.unbind('keyup');
                this.ReasonForTrade.bind("keyup", { obj: this }, this.onReasonOfTradeKeyUp);
                this.ReasonForTrade.unbind('blur');
                this.ReasonForTrade.bind("blur", { obj: this }, this.onReasonOfTradeBlur);
                this.ReasonForTrade.height(50);
            }
        }
        //Other securities owned textbox
        if (this.otherSecuritiesOwnedShown) {
            this.OtherSecuritiesOwnedMessage = $("#OthersecuritiesOwnedMessage");
            this.defaultOtherSecuritiesOwnedTextBoxMessage = $("#hdOtherSecuritiesOwned");
            this.OtherSecuritiesOwnedTextBox = $("#OtherSecuritiesOwned");
            if (this.OtherSecuritiesOwnedTextBox != null) {
                this.OtherSecuritiesOwnedTextBox.unbind('focus');
                this.OtherSecuritiesOwnedTextBox.bind("focus ", { obj: this }, this.onOtherSecuritiesOwnedTextBoxFocus);
                this.OtherSecuritiesOwnedTextBox.unbind('click');
                this.OtherSecuritiesOwnedTextBox.bind("click", { obj: this }, this.onOtherSecuritiesOwnedTextBoxClick);
                this.OtherSecuritiesOwnedTextBox.unbind('keyup');
                this.OtherSecuritiesOwnedTextBox.bind("keyup", { obj: this }, this.onOtherSecuritiesOwnedTextBoxKeyUp);
                this.OtherSecuritiesOwnedTextBox.unbind('blur');
                this.OtherSecuritiesOwnedTextBox.bind('blur', { obj: this }, this.onOtherSecuritiesOwnedTextBoxBlur);
                this.OtherSecuritiesOwnedTextBox.height(50);
            }
        }
        // Checkbox confirmation
        if (this.confirmCheckBoxShown) {
            this.confirmRequestPreClearanceMessage = $("#ConfirmRequestPreClearanceMessage");
            this.confirmRequestPreClearanceCheckBox = $("#ConfirmRequestPreClearance");
            if (this.confirmRequestPreClearanceCheckBox != null) {
                this.confirmRequestPreClearanceCheckBox.unbind('click');
                this.confirmRequestPreClearanceCheckBox.bind('click', { obj: this }, this.onConfirmRequestPreClearanceCheckBoxClick);
                this.confirmRequestPreClearanceCheckBox.width(13);
            }
        }
        //PreClearance button
        this.btnRequestPreClearance = $("#btnRequestPreClearance");
        this.enablePreClearanceButton();
        // TODO Sathish - [29/04/2015] Since the button is always disabled when all the newly added controls are switched off in client configuration
        // TODO Sathish - [29/04/2015] we need to comment these lines, once the bug is fixed, we can uncomment this.
        //if (this.btnRequestPreClearance != null) {
        //    this.btnRequestPreClearance.parent().addClass("disabled");
        //    this.btnRequestPreClearance.attr("disabled", "disabled");
        //}
    },

    onSellNumberKeyPress: function (events) {
        var self = this;

        var count = $(this).val().length;
        if (count > 9) {
            return false;
        }

        if (events.which != 8 && events.which != 0 && (events.which < 48 || events.which > 57)) {
            return false;
        }

    },
    onSellNumberKeyUp: function (events) {
        
        var _obj = events.data.obj;
        _obj.enablePreClearanceButton();

    },
    onConfirmRequestPreClearanceCheckBoxClick: function (events) {
        
        var _obj = events.data.obj;
        _obj.enablePreClearanceButton();
    },

    onSellNumberBlur: function (events) {
        
        var _obj = events.data.obj;

        

        _obj.enablePreClearanceButton();
    },
    onReasonOfTradeFocus: function (events) {
        
        var _obj = events.data.obj;
        var messageReasonForTrade = _obj.ReasonForTrade.val();
        var defaultReasonForTrade = _obj.defaultReasonForTrade.val();
        if (messageReasonForTrade == defaultReasonForTrade) {
            _obj.ReasonForTrade.val("");


        }
        _obj.enablePreClearanceButton();


    },
    onReasonOfTradeClick: function (events) {
        
        var _obj = events.data.obj;
        var messageReasonForTrade = _obj.ReasonForTrade.val();
        var defaultReasonForTrade = _obj.defaultReasonForTrade.val();

        if (messageReasonForTrade.indexOf(defaultReasonForTrade) > -1) {
            _obj.ReasonForTrade.val("");
        }

        
        if (messageReasonForTrade == defaultReasonForTrade) {
            _obj.ReasonForTrade.val("");

        }
       
    },
    onReasonOfTradeKeyUp: function (events) {
        
        var _obj = events.data.obj;
        var messageReasonForTrade = _obj.ReasonForTrade.val();
        var defaultReasonForTrade = _obj.defaultReasonForTrade.val();

        if (messageReasonForTrade.indexOf(defaultReasonForTrade) > -1) {
            _obj.ReasonForTrade.val("");
        }

        //if (messageReasonForTrade == "") {
        //    _obj.ReasonForTrade.val(defaultReasonForTrade);
        //}
        if (messageReasonForTrade == defaultReasonForTrade) {
            _obj.ReasonForTrade.val("");

        }
        _obj.enablePreClearanceButton();


    },
    onReasonOfTradeBlur: function (events) {
        
        var _obj = events.data.obj;
        var messageReasonForTrade = _obj.ReasonForTrade.val();
        var defaultReasonForTrade = _obj.defaultReasonForTrade.val();

        if (messageReasonForTrade.indexOf(defaultReasonForTrade) > -1) {
            _obj.ReasonForTrade.val("");
        }

        if (messageReasonForTrade == "") {
            _obj.ReasonForTrade.val(defaultReasonForTrade);
        }
        if (messageReasonForTrade == defaultReasonForTrade) {
            _obj.ReasonForTrade.val("");

        }
        _obj.enablePreClearanceButton();


    },
    onOtherSecuritiesOwnedTextBoxFocus: function (events) {
       
        var _obj = events.data.obj;
        var messageOtherSecuritiesOwnedTextBoxValue = _obj.OtherSecuritiesOwnedTextBox.val();
        var defaultOtherSecuritiesOwnedMessage = _obj.defaultOtherSecuritiesOwnedTextBoxMessage.val();
        if (messageOtherSecuritiesOwnedTextBoxValue == defaultOtherSecuritiesOwnedMessage) {
            _obj.OtherSecuritiesOwnedTextBox.val("");

        }
        _obj.enablePreClearanceButton();
    },
    onOtherSecuritiesOwnedTextBoxClick: function (events) {

        var _obj = events.data.obj;
        var messageOtherSecuritiesOwnedTextBoxValue = _obj.OtherSecuritiesOwnedTextBox.val();
        var defaultOtherSecuritiesOwnedTextBoxMessage = _obj.defaultOtherSecuritiesOwnedTextBoxMessage.val();

        if (messageOtherSecuritiesOwnedTextBoxValue.indexOf(defaultOtherSecuritiesOwnedTextBoxMessage) > -1) {
            _obj.OtherSecuritiesOwnedTextBox.val("");
        }
        
        if (messageOtherSecuritiesOwnedTextBoxValue == defaultOtherSecuritiesOwnedTextBoxMessage) {
            _obj.OtherSecuritiesOwnedTextBox.val("");
        }

    },
    onOtherSecuritiesOwnedTextBoxKeyUp: function (events) {
        
        var _obj = events.data.obj;
        var messageOtherSecuritiesOwnedTextBoxValue = _obj.OtherSecuritiesOwnedTextBox.val();
        var defaultOtherSecuritiesOwnedTextBoxMessage = _obj.defaultOtherSecuritiesOwnedTextBoxMessage.val();

        if (messageOtherSecuritiesOwnedTextBoxValue.indexOf(defaultOtherSecuritiesOwnedTextBoxMessage) > -1) {
            _obj.OtherSecuritiesOwnedTextBox.val("");
        }
        //if (messageOtherSecuritiesOwnedTextBoxValue == "") {
        //    _obj.OtherSecuritiesOwnedTextBox.val(defaultOtherSecuritiesOwnedTextBoxMessage);
        //}
        if (messageOtherSecuritiesOwnedTextBoxValue == defaultOtherSecuritiesOwnedTextBoxMessage) {
            _obj.OtherSecuritiesOwnedTextBox.val("");
        }

        _obj.enablePreClearanceButton();
    },

    onOtherSecuritiesOwnedTextBoxBlur: function (events) {

        var _obj = events.data.obj;
        var messageOtherSecuritiesOwnedTextBoxValue = _obj.OtherSecuritiesOwnedTextBox.val();
        var defaultOtherSecuritiesOwnedTextBoxMessage = _obj.defaultOtherSecuritiesOwnedTextBoxMessage.val();

        if (messageOtherSecuritiesOwnedTextBoxValue.indexOf(defaultOtherSecuritiesOwnedTextBoxMessage) > -1) {
            _obj.OtherSecuritiesOwnedTextBox.val("");
        }
        if (messageOtherSecuritiesOwnedTextBoxValue == "") {
            _obj.OtherSecuritiesOwnedTextBox.val(defaultOtherSecuritiesOwnedTextBoxMessage);
        }
        if (messageOtherSecuritiesOwnedTextBoxValue == defaultOtherSecuritiesOwnedTextBoxMessage) {
            _obj.OtherSecuritiesOwnedTextBox.val("");
        }

        _obj.enablePreClearanceButton();
    },

    enablePreClearanceButton: function () {
        var self = this;

        //check if the button is visible
        if (self.preClearanceButtonShown) {
            // Enable the button
            this.btnRequestPreClearance.parent().removeClass("disabled");
            this.btnRequestPreClearance.removeAttr("disabled");
        }
        // Disable the button in the below cases

        var data = null;
        if (self.SellNumberTextBox != undefined)
            data = self.SellNumberTextBox.val();

        if (self.sellNumberShown && data != null){
            for (var i = 0; i < data.toLocaleString().length; i++) {

                if (!(data[i] >= 0 || data[i] <= 9)) {
                    this.btnRequestPreClearance.parent().addClass("disabled");
                    this.btnRequestPreClearance.attr("disabled", "disabled");
                    return false;
                }
            }
        }
        

        if (self.sellNumberShown && (self.SellNumberTextBox == undefined || self.SellNumberTextBox.val() == ""
                || self.SellNumberTextBox.val() <= 0)) {

            this.btnRequestPreClearance.parent().addClass("disabled");
            this.btnRequestPreClearance.attr("disabled", "disabled");

        }

        if (self.reasonForTradeShown && self.ReasonForTrade.val() == "") {
            this.btnRequestPreClearance.parent().addClass("disabled");
            this.btnRequestPreClearance.attr("disabled", "disabled");

        }
        if (self.reasonForTradeShown && self.ReasonForTrade.val() == self.defaultReasonForTrade.val()) {
            this.btnRequestPreClearance.parent().addClass("disabled");
            this.btnRequestPreClearance.attr("disabled", "disabled");
        }

        if (self.otherSecuritiesOwnedShown && self.OtherSecuritiesOwnedTextBox.val() == "") {
            this.btnRequestPreClearance.parent().addClass("disabled");
            this.btnRequestPreClearance.attr("disabled", "disabled");
        }
        if (self.otherSecuritiesOwnedShown && self.OtherSecuritiesOwnedTextBox.val() == self.defaultOtherSecuritiesOwnedTextBoxMessage.val()) {
            this.btnRequestPreClearance.parent().addClass("disabled");
            this.btnRequestPreClearance.attr("disabled", "disabled");
        }

        if (self.confirmRequestPreClearanceCheckBox != null) {
            var confirmChecked = self.confirmRequestPreClearanceCheckBox.is(':checked');
            if (self.confirmCheckBoxShown && confirmChecked == false) {
                this.btnRequestPreClearance.parent().addClass("disabled");
                this.btnRequestPreClearance.attr("disabled", "disabled");
            }
        }

    }
};;IC.Public.security = {
    validateTransactionPassword: function (formData, onSucessHandler, onRetryCountExceededHandler) {
        if (formData.transactionPassword == '') {
            $('#passwordValidationErrorDiv').show();
            $('#passwordValidationErrorSpan').html("Please enter the Transaction Password.");
            return;
        }
        IC.Public.showLoading("");
        $.ajax({
            url: IC.Public.urls.Settings.validateTransactionPassword,
            type: 'POST',
            data: formData,
            success: function (result) {
                if (result.IsValid == true) {
                    IC.Public.hideLoading();
                    onSucessHandler();
                } else {
                    IC.Public.hideLoading();
                    $('#passwordValidationErrorDiv').show();
                    $('#passwordValidationErrorSpan').html(result.ErrorMessage);
                    $('#hdnRetryCount').val(result.RetryCount);

                    if (result.IsRetryCountExceeded == true) {
                        $('#transactionPasswordDiv').hide();
                        onRetryCountExceededHandler();
                    }
                }
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    
    validateTransactionPin : function(formData, onSucessHandler) {
        IC.Public.showLoading("");
        this.transactionPinError = $('#transactionPinError');
        $('#transactionPinErrorDiv').hide();
        
        $.ajax({
            url: IC.Public.urls.Settings.validateTransactionPin,
            type: 'POST',
            data: formData,
            success: function (result) {
                IC.Public.hideLoading();
                if (result.IsValidData) {
                    onSucessHandler();
                } else {
                    $('#transactionPinErrorDiv').show();
                    $('#transactionPinError').text(result.ErrorMessage);
                }
            },
            error: function (err) {
                IC.Public.hideLoading();
                onSucessHandler();
            }
        });
    }
};

IC.Public.security.transactionPasswordEvents = function (continueButton, validateTransactionPassword) {
    //if (validateTransactionPassword) {
    //    continueButton.parent().addClass('disabled');
    //    this.init(continueButton);
    //}
    this.resendLink = $('#resendTransactionPinLink');
    this.transactionType = $('#validationModel_TransactionType');
    this.smsPinRegisterLink = $('#smsPinRegisterLink');
    this.transactionPinModalMessageContainer = $('#transactionPinModalMessageContainer');
    this.transactionPinModal = $('#transactionPinModal');
    this.smsPinBanner = $('#smsPinBanner');
    this.lnkSmsPinBannerClose = $('#lnkSmsPinBannerClose');
    this.initControls();
};

IC.Public.security.transactionPasswordEvents.prototype = {
    init: function (continueButton) {
        this.continueButton = continueButton;

        this.transactionPasswordTextBox = $('#transactionPassword');
        this.transactionPasswordTextBox.unbind('keyup');
        this.transactionPasswordTextBox.bind('keyup', { obj: this }, this.onTransactionPasswordChange);
    },
    
    onTransactionPasswordChange: function (events) {
        var self = events.data.obj;
        if (self.transactionPasswordTextBox.val().length > 0)
            self.continueButton.parent().removeClass('disabled');
        else
            self.continueButton.parent().addClass('disabled');
    },
    
    initControls: function () {
        this.resendLink.unbind('click');
        this.resendLink.bind('click', { obj: this }, this.onResendLinkClick);
        
        this.smsPinRegisterLink.unbind('click');
        this.smsPinRegisterLink.bind('click', { obj: this }, this.onSmsPinRegisterLinkClick);
        
        this.lnkSmsPinBannerClose.unbind('click');
        this.lnkSmsPinBannerClose.bind('click', { obj: this }, this.onCloseSmsPinBannerLinkClick);
    },
    
    onCloseSmsPinBannerLinkClick: function(eventArgs) {
        var self = eventArgs.data.obj;
        self.smsPinBanner.hide();
    },
    
    onResendLinkClick: function(eventArgs) {
        var hrnKeys = $(this).attr('hrnKeys');
        var self = eventArgs.data.obj;
        
        $.ajax({
            url: OC.MVC.util.getLink('SmsPin', 'ResendTransactionPin'),
            data: { transactionType: self.transactionType.val(), hrnKeys: hrnKeys },
            type: 'POST',
            beforeSend: function() {
                IC.Public.showLoading("");
            },
            complete: function() {
                IC.Public.hideLoading();
            },
            success: function(response) {
                if (response) {
                    if (response.IsSuccess) {
                        self.transactionPinModalMessageContainer.text(response.Message);
                        self.showModal();
                    } else {
                        self.transactionPinModalMessageContainer.text(response.ErrorMessage);
                        self.showModal();
                    }
                } else {
                    self.transactionPinModalMessageContainer.text("An error occurred while resending the transaction pin. Please try again after some time.");
                    self.showModal();
                }
            },
            error: function(err) {
                self.transactionPinModalMessageContainer.text("An error occurred while resending the transaction pin. Please try again after some time.");
                self.showModal();
            }
        });
    },
    
    showModal: function () {
        var parentModal = $('#simplemodal-container');
        if (parentModal.length > 0) {
            this.transactionPinModalMessageContainer.addClass('transactionPinMessage');
            $('.transactionPinDiv').prepend(this.transactionPinModalMessageContainer);
            return;
        }
        this.transactionPinModal.modal({
            onShow: function(dialog) {
                $('#btnOk', dialog.data[0]).click(function() {
                    $.modal.close();
                });
            }
        });
    },
    
    onSmsPinRegisterLinkClick: function (eventArgs) {
        var parentModal = $('#simplemodal-container');
        if (parentModal.length > 0) {
            $.modal.close();
        }
        
        var url = IC.Public.urls.Settings.smsPinRegistration;
        OC.MVC.util.loadMainContainerView(url);
    }
}
;IC.Public.ForgotPin = function (options) {
};

IC.Public.ForgotPin.prototype = {

    init: function () {
        OC.MVC.validate.validationMsgProcess();
        $('#EmployeeID').focus();

        this.lnkCancel = $("#lnkCancel");

        $("#lnkCancel").bind("click", this.CancelClick);
        $('#frmForgotPin').bind("submit", { self: this }, this.formSubmit);

    },
    CancelClick: function () {
        window.location = $("#FromPageURL").val();
    },
    formSubmit: function (events) {
        var url = OC.MVC.util.getLink("OpenAccess", "ForgotPinRequest");
        var data = $("#frmForgotPin").serialize();
        $("#loadingSpinner").removeAttr("style");
        $.post(url, data, function (htmlContent) {
            $("#" + OC.MVC.constants.mainContainer).html(htmlContent);
            $("#loadingSpinner").attr("style", "display: none; ");
        });
        return false;
    }
};IC.Public.grandAuthorisation = function (options) {
    if (options) {
        $.extend(this, options);
    }

    //this.hdnSecondaryUserEmail = $('#hdnSecondaryUserEmail');
    //this.txtSecondaryUserEmail = $('#txtSecondaryUserEmail');
    //this.grandAuthorisationGrid = $("#GrandAuthorisationGrid");
    //this.hdnAction = $("#hdnAction").val();
    this.step1 = $('#pStep1');
    this.step2 = $('#bStep2');
    this.btnNext = $("#btnNext");
    this.btnCancel = $("#btnCancel");
    //this.hdnSecondaryUserID = $("#hdnSecondaryUserID").val();
    this.init();
};

IC.Public.grandAuthorisation.prototype = {
    init: function() {
        var self = this;
        //self.ManipulateSecondaryUserEmail(self);
        //self.grandAuthorisationGrid.bind("grid_gridComplete", { obj: self }, self.SetPrivilege);
        self.btnNext.bind('click', { obj: self }, self.onNextClick);
        self.btnCancel.bind('click', { obj: self }, self.onCancelClick);
        this.errorContainer = $('.errorContainer');
        if (this.errorContainer.html() == '')
            this.errorContainer.hide();
        IC.Public.hideCampaignsPanel();

    },

    onNextClick: function(e) {
        var form = $("form#GrantAuthorisationForm");
        $('#IsConfirmation').val(true);
        var data = form.serializeObject();
        IC.Public.showLoading("");
        $.ajax({
            url: OC.MVC.util.getLink("Authorisation/Authorisation", "ManageUsers"), type: 'POST', data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                ("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        IC.Public.hideLoading();
    },

    onCancelClick: function () {
        var data = { isAuthorised: true };
        var AuthorisedUsersExist = $('#AuthorisedUsersExist').val();
        var _url;
        if (AuthorisedUsersExist.toLowerCase()=="true")
            _url = OC.MVC.util.getLink("Authorisation/Authorisation", "ManageUsers");
        else
            _url = OC.MVC.util.getLink("Holdings","Index");
        IC.Public.showLoading("");
        $.ajax({
            url: _url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                IC.Public.hideLoading();
                OC.MVC.util.errorMessage(err.responseText);
                ;
            }
        });
    },

    SetPrivilege: function (e) {
        $(e.data.obj.grandAuthorisationGrid).find("input[type='radio'][value=" + true + "]").attr('checked', 'checked');       
    },

    GrandAuthorisationGridModelData: function (formData) {
        var data = {};
        var tempHolidingsAccessList = [];
        var gridRows = $(formData.grandAuthorisationGrid).find("tr");
        var emailAddress = formData.hdnAction === "EDIT" ? formData.hdnSecondaryUserEmail.val() : formData.txtSecondaryUserEmail.val();

        gridRows.each(function () {
            var columnData = $(this);
            
            tempHolidingsAccessList.push({
                IssuerCode: columnData.find("td[aria-describedby*='IssuerCode']").text(),
                PostCode: columnData.find("td[aria-describedby*='PostCode']").text(),
                CountrtyCode: columnData.find("td[aria-describedby*='CountrtyCode']").text(),
                InvestorCode: columnData.find("td[aria-describedby*='InvestorCode']").text(),
                HrnMasked: columnData.find("td[aria-describedby='GrandAuthorisationGrid_Holding']").text(),
                HrnNumber: columnData.find("td[aria-describedby*='Holding Key']").text(),
                HolderName: columnData.find("td[aria-describedby*='Registered Holder Name']").text(),
                IsReadOnly: columnData.find("td[aria-describedby*='Read Only'] input").is(":checked"),
                AllowEdit: columnData.find("td[aria-describedby*='Edit'] input").is(":checked"),
                IsDisabled: columnData.find("td[aria-describedby*='Disabled'] input").is(":checked")
            });
        });

        data = {
            holdingsList: tempHolidingsAccessList,
            emailAddress: emailAddress,
            action: formData.hdnAction,
            hdnSecondaryUserID: formData.hdnSecondaryUserID
        };
        return data;
    }
};

IC.Public.RadioButton = {
    Action: function (cellValue, options, rowObject) {
        var div = $('<div>');
        var displayRadioButton = $('<INPUT type="radio" name=Radio_' + rowObject[5] + ' value=' + cellValue + ' data-key=' + rowObject[5] + '></INPUT>');
        displayRadioButton.appendTo(div);
        return div.html();
    }
};

;IC.Public.ManageUser = function (options) {
    if (options) {
        $.extend(this, options);
    }
    //this.init();
};
//portfolioLink
var deleteAuthorisedUserData;
IC.Public.ManageUser.prototype = {
    init: function () {
        var _obj = this;
        this.isLoading = true;
        var isDisplayDeleteAuthorisedUserModal = $('#hdnIsDisplayDeleteAuthorisedUserModal').val();
        //var isDisplayDeleteAuthorisedUserModal = $("#hdnIsDisplayDeleteAuthorisedUserModal")[0].value;
        $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        if ((isDisplayDeleteAuthorisedUserModal != undefined) && (isDisplayDeleteAuthorisedUserModal.toLowerCase() === "true")) {
            $("#deleteAuthorisedUserModal").show();
        }

        $("#deleteAuthorisedUserModal").hide();
        
        $("#btnAddAuthorisation").bind('click', { obj: this }, _obj.onAddClick);
        $("#btnConfirmAuthorisation").bind('click', { obj: this }, _obj.onConfirmAuthorisationClick);
        $("#btnCancelAuthorisation").bind('click', { obj: this }, _obj.onCancelAuthorisationClick);
        $("#btnBackAuthorisation").bind('click', { obj: this }, _obj.onBackAuthorisationClick);
        $("#btnBackToManageUsers").bind('click', { obj: this }, _obj.onBackToManageUsersClick);
        
        
        $("#lnkOk").bind('click', { obj: this }, _obj.onOkClick);
        this.authorisedUserGrid = $("#AuthorisedUserGrid");
        this.authorisedUserGrid.unbind("grid_gridComplete");
        this.authorisedUserGrid.bind("grid_gridComplete", { obj: this }, function (events) {
            
            $('.actionAuthorisedUserDelete').each(function (index, value) { $(value).bind('click', { obj: $(value).attr('data') }, _obj.onDeleteClick); });
            
        });
        $("#btnConfirm").bind('click', { obj: _obj }, _obj.onConfirmClick);
        $("#btnCancel").bind('click', { obj: _obj }, _obj.onCancelClick);

        
               
        IC.Public.hideCampaignsPanel();
        this.isLoading = false;
    },
    onBackAuthorisationClick: function (events) {
        var dataObj = {
            firstName: $('#AuthorisedUserGrid tr td').eq(0).text(),
            lastName: $('#AuthorisedUserGrid tr td').eq(1).text(),
            EmailAddress: $('#AuthorisedUserGrid tr td').eq(2).text(),
        };
        IC.Public.showLoading("");
        $.ajax({
            url: OC.MVC.util.getLink("Authorisation/Authorisation", "GrantAuthorisation"),
            type: 'POST',
            data: dataObj,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                IC.Public.hideLoading();
                OC.MVC.util.errorMessage(err.responseText);;
            }
        });
    },
    onBackToManageUsersClick: function(events) {
    
        var url = OC.MVC.util.getLink("Authorisation/Authorisation", "ManageUsers");
        OC.MVC.util.loadMainContainerView(url);
    },
    onCancelAuthorisationClick: function (events) {
        
        
        var url = OC.MVC.util.getLink("Authorisation/Authorisation", "GrantAuthorisation");
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: {userAction :'ADD',error:''},
                success: function (result) {
                    $("#" + OC.MVC.constants.mainContainer).html(result);
                    IC.Public.hideLoading();
                    //LockBox.set("lastUrl", OC.MVC.util.getLink("Holdings", "Index"));
                    //window.location.reload();

                },
                error: function (err) {
                    IC.Public.hideLoading();
                }
            });
       },
    onReturnClick: function (events) {
        
        var url = $(this).attr("actionUrl");
        var data = { type: $(this).attr("type") };
        if ($(this).attr("type").toLowerCase() == "authorisedportfolio") {
            $("#mainMenu").css("visibility", "hidden");
            $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
        } else {
            $("#mainMenu").css("visibility", "visible");
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        }
        if (url) {
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function (result) {
                    $("#" + OC.MVC.constants.mainContainer).html(result);
                    IC.Public.hideLoading();
                    //LockBox.set("lastUrl", OC.MVC.util.getLink("Holdings", "Index"));
                    //window.location.reload();

                },
                error: function (err) {
                    IC.Public.hideLoading();
                }
            });
        }
    },
    onConfirmClick: function(events) {
        $.modal.close();
        var data = deleteAuthorisedUserData;
        IC.Public.showLoading("");
        $.ajax({
            url: OC.MVC.util.getLink("Authorisation/Authorisation", "DeleteAuthorisedUser"),
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                IC.Public.hideLoading();
                OC.MVC.util.errorMessage(err.responseText);
            }
        });
    },
    onCancelClick: function (events) {
        $.modal.close();
    },
    onOkClick: function (events) {
        $("#divSectionAddPersonEmailMessage").hide();
    },
    onDeleteClick: function (events) {
        deleteAuthorisedUserData = events.data.obj;
        var arr = decodeURI(deleteAuthorisedUserData).split('&');
        var firstname = arr[0].split('=');
        var lastname = arr[1].split('=');
        var email = arr[2].split('=');
        $("#labelName").text(firstname[1] + " " + lastname[1]);
        $("#labelEmail").text(email[1]);
        $("#deleteAuthorisedUserModal").modal('show');
    },

    onConfirmAuthorisationClick: function(events) {
        var dataObj = {
            firstName: $('#AuthorisedUserGrid tr td').eq(0).text(),
            lastName: $('#AuthorisedUserGrid tr td').eq(1).text(),
            EmailAddress: $('#AuthorisedUserGrid tr td').eq(2).text(),
            error:''
        };
        IC.Public.showLoading("");
        $.ajax({
            url: OC.MVC.util.getLink("Authorisation/Authorisation", "ConfirmAuthorisedUser"),
            type: 'POST',
            data: dataObj,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                IC.Public.hideLoading();
                OC.MVC.util.errorMessage(err.responseText);;
            }
        });
    },
    onAddClick: function (events) {
        var data = events.data.obj;
        IC.Public.showLoading("");
        $.ajax({
            url: OC.MVC.util.getLink("Authorisation/Authorisation", "GrantAuthorisation"),
            type: 'POST',
            data: { FirstName: '',LastName:'',EmailAddress: '' },
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                IC.Public.hideLoading();
                OC.MVC.util.errorMessage(err.responseText);;
            }
        });
    },
    onEditClick: function (events) {
        var data = events.data.obj;
        IC.Public.showLoading("");
        $.ajax({
            url: OC.MVC.util.getLink("Authorisation/Authorisation", "GrantAuthorisation"),

            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                IC.Public.hideLoading();
                OC.MVC.util.errorMessage(err.responseText);;
            }
        });
    }

  
}
IC.Public.AuthorisedUserList = {
    Action: function (cellValue, options, rowObject) {
        var div = $('<div>');
        var a = $('<a>');
        a.attr("href", "javascript:void(0);");
        a.addClass("actionAuthorisedUserDelete");
        a.html("Delete");
        a.attr('data', OC.MVC.JSON.ToUrl({ firstName: rowObject[0], lastName: rowObject[1], emailAddress: rowObject[2], userEmailTokenId: rowObject[5] }));
        a.appendTo(div);

        return div.html();
    }
};

IC.Public.ManageUserTooltip = {
    holdingTooltip: function (cellValue, options, rowObject) {
        var div = $('<div>');
        return commaSeparatedTooltipForEachHolding(rowObject, div);
    }
};

IC.Public.Tooltip = {
    UserEmail: function (cellValue, options, rowObject) {
        var div = $('<div>');
        var span = $('<span>');
        var split = emailUnAuthorizedTooltip(cellValue);
        span.html(split);
        span.attr("title", split);
        span.attr("data", cellValue);
        span.addClass("tooltip");
        span.html(cellValue);
        span.appendTo(div);
        return div.html();
    },
    FirstName: function (cellValue, options, rowObject) {
        var div = $('<div>');
        var span = $('<span>');
        span.attr("title", rowObject[0]);
        span.addClass("tooltip");
        span.html(rowObject[0]);
        span.appendTo(div);
        return div.html();
    },
    LastName: function (cellValue, options, rowObject) {
        var div = $('<div>');
        var span = $('<span>');
        span.attr("title", rowObject[1]);
        span.addClass("tooltip");
        span.html(rowObject[1]);
        span.appendTo(div);
        return div.html();
    }
};
var emailUnAuthorizedTooltip = function (cellValue) {
    var ar = "";
    var email = "";
    var str = cellValue;
    var split = str.split(",");
    for (var i = 0; i < split.length; i++) {
        ar = split[i];
        if (email.length == 0) {
            email = ar;
        }
        else {
            email = email + "," + ar;
        }
    }
    return email;
}
;IC.Public.authorisedPortfolios = function (options) {
    if (options) {
        $.extend(this, options);
    }
    this.planSummaryContainer = $('#planSummaryContainer');
    this.init();
};
var removePortfolioUser;
IC.Public.authorisedPortfolios.prototype = {
    init: function() {
        this.registerEvents();
        this.isLoading = true;
        var _obj = this;
        $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
        $("#mainMenu").css("visibility", "hidden");
        if ($("#hdnIsThirdPartyImpersonateUser").val() !== undefined) {
            if ($("#hdnIsThirdPartyImpersonateUser").val().toLowerCase() == "true") {
                $("#impersonateUserName").removeClass("impersonationName");
                $("#impersonateUserName").html("Logged in as " + $("#hdnLoggedInUserName").val());
            }
        }
        $("#btnBackToAuthorisedPortfolio").css("visibility", "hidden");
        $('#removePortfolioUserModal').hide();
        $("#btnConfirm").unbind('click');
        $("#btnCancel").unbind('click');
        $("#btnConfirm").bind('click', { obj: _obj }, _obj.onConfirmClick);
        $("#btnCancel").bind('click', { obj: _obj }, _obj.onCancelClick);

        IC.Public.hideCampaignsPanel();
        this.isLoading = false;
    },
    onConfirmClick: function (events) {
        $.modal.close();
        var data = removePortfolioUser;
        IC.Public.showLoading("");
        $.ajax({
            url: OC.MVC.util.getLink("Authorisation/Authorisation", "RemoveAuthorisedPortfolioUser"),
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                IC.Public.hideLoading();
                OC.MVC.util.errorMessage(err.responseText);
            }
        });
    },
    onCancelClick: function (events) {
        $.modal.close();
    },
    onRemoveClick: function (events) {

        removePortfolioUser = events.data.obj;
        $("#removePortfolioUserModal").modal('show');
    },
    onEditClick: function(events) {
        
        var data = decodeURI(events.data.obj).split('&');
        var editSection = data[0];
        var authorisedUserIdSection = data[1];
        var authoriserUserIdSection = data[2];
        var portfolioName = editSection.split('=')[1];
        var authorisedUserId = authorisedUserIdSection.split('=')[1];
        var authoriserUserId = authoriserUserIdSection.split('=')[1];

        var div = $('<div>');
        div.html(GetDynamicTextBox(portfolioName, authoriserUserId));
        $("#CurrentAuthorisedPortfoliosGrid").find('div[id$="' + authoriserUserId + '"]')[0].innerHTML = div.html();
        var divAction = $('<div>');
        var a = $('<a>');
        a.attr("href", "javascript:void(0);");
        a.addClass("actionAuthorisedPortfolioSave");
        a.html("Save");
        a.attr('data', OC.MVC.JSON.ToUrl({ portfolioName: portfolioName }));
        a.appendTo(divAction);
        var tab = $('<span>');
        tab.html("&emsp;");
        tab.appendTo(divAction);

        a = $('<a>');
        a.attr("href", "javascript:void(0);");
        a.addClass("actionAuthorisedPortfolioCancel");
        a.html("Cancel");
        a.attr('data', OC.MVC.JSON.ToUrl({ portfolioName: portfolioName }));
        a.appendTo(divAction);

        $("#CurrentAuthorisedPortfoliosGrid").find('div[id$="Action' + authoriserUserId + '"]')[0].innerHTML = divAction.html();
        $("#CurrentAuthorisedPortfoliosGrid").find('a[class$=actionAuthorisedPortfolioSave]').bind('click', { portfolioName: $('.actionAuthorisedPortfolioTxtBox').val(), authorisedUserId: authorisedUserId, authoriserUserId: authoriserUserId }, function (event) {
            var data = event.data;
            data.portfolioName = $('#actionAuthorisedPortfolioTxtBox' + authoriserUserId).val();
            $.ajax({
                url: OC.MVC.util.getLink("Authorisation/Authorisation", "SaveAuthorisedPortfolioData"),

                type: 'POST',
                data: data,
                success: function(result) {
                    $("#CurrentAuthorisedPortfoliosGrid").trigger("reloadGrid");
                },
                error: function(err) {
                    IC.Public.hideLoading();
                    OC.MVC.util.errorMessage(err.responseText);;
                }
            });
        });

        $("#CurrentAuthorisedPortfoliosGrid").find('a[class$=actionAuthorisedPortfolioCancel]').bind('click', function() {

            $("#CurrentAuthorisedPortfoliosGrid").trigger("reloadGrid");

        });


    },
    displayLinkOnClick: function (events) {
        var data = { current: true };
        // if not already selected
        if (!$(this).hasClass('selected')) {
            IC.Public.showLoading("");
            var _obj = events.data.obj;
            _obj.planSummaryContainer.find('.displayLinks a').each(function() {
                $(this).removeClass('selected');
            });
            $(this).addClass('selected');
            var url = events.data.url;
            if ($(this).attr("gridtype") == "1") {
                data = { current: false };
            }

            IC.Public.authorisedPortfolios.plansummaryPostAjax("divAuthorisedPortfoliosGrid", url, data);
        }
    },
    registerEvents: function () {
        var self = this;
        this.planSummaryContainer.find('.displayLinks a').each(function() {
            $(this).bind('click', { obj: self, url: $(this).attr('actionurl') }, self.displayLinkOnClick);
        });

        this.authorisedUserGrid = this.planSummaryContainer.find("#CurrentAuthorisedPortfoliosGrid");
        this.authorisedUserGrid.unbind("grid_gridComplete");
        this.authorisedUserGrid.bind("grid_gridComplete", { obj: this }, function (events) {
            $('.actionAuthorisedPortfolioRemove').each(function (index, value) { $(value).bind('click', { obj: $(value).attr('data') }, self.onRemoveClick); });
            $('.actionAuthorisedPortfolioEdit').each(function (index, value) { $(value).bind('click', { obj: $(value).attr('data') }, self.onEditClick); });
            $('.actionAuthorisedPortfolioSelect').each(function() {
                $(this).bind('click', { obj: self }, self.impersonateUser);
            });
        });
    },
    impersonateUser: function(events) {
        var key = $(this).attr("data");
        var _obj = events.data.obj;

        var impersonateUrl = _obj.getImpersonateUrl(key);
        
        if (impersonateUrl != '') {
            window.location = impersonateUrl;
        }
    },
    getImpersonateUrl: function(impersonateUserID) {
        var result;
        $.ajax({
            type: "POST",
            url: OC.MVC.util.getLink("Authorisation/Authorisation", "GetImpersonateUrl"),
            data: { impersonateUserID: impersonateUserID },
            async: false,
            success: function(data) { result = data; },
            error: function() { result = ''; }
        });

        return result;
    }
 };
function GetDynamicTextBox(value,ID) {

    return '<input name = "' + value + '" id="actionAuthorisedPortfolioTxtBox' + ID + '" style= "width:100px" maxlength=255 class="actionAuthorisedPortfolioTxtBox" type="text" value = "' + value + '" />&nbsp;';

};

IC.Public.authorisedPortfolios.plansummaryPostAjax = function(container, url, data) {
    $.ajax({
        url: url,
        data: data,
        type: "POST",
        success: function(html) {
            OC.MVC.util.loadHtml(container, html);
        }
    });
};

IC.Public.AuthorisedPortfolioList = {
    Action: function (cellValue, options, rowObject) {
        var divMain = $('<div>');
        var div = $('<div>');
        div.attr("id", "divAction" + rowObject[0]);
        var a = $('<a>');
        a.attr("href", "javascript:void(0);");
        a.addClass("actionAuthorisedPortfolioSelect");
        a.html("Select");
        a.attr('data', OC.MVC.JSON.ToUrl({ id: rowObject[0] }));

        a.appendTo(div);
        var tab = $('<span>');
        tab.html("&emsp;");
        tab.appendTo(div);

        a = $('<a>');
        a.attr("href", "javascript:void(0);");
        a.addClass("actionAuthorisedPortfolioRemove");
        a.html("Remove");
        a.attr('data', OC.MVC.JSON.ToUrl({ id: rowObject[0], AuthoriserUserName: rowObject[4] }));
        a.appendTo(div);

        tab = $('<span>');
        tab.html("&emsp;");
        tab.appendTo(div);

        var displayName = "";
        if (rowObject[1] == '') {
            displayName = rowObject[4];
        } else {
            displayName = rowObject[1];
        }
        a = $('<a>');
        a.attr("href", "javascript:void(0);");
        a.addClass("actionAuthorisedPortfolioEdit");
        a.html("Edit Label");
        a.attr('data', OC.MVC.JSON.ToUrl({ editPortfolioName: displayName, authorisedUserId: rowObject[3], authoriserUserId: rowObject[0] }));
        a.appendTo(div);
        div.appendTo(divMain);
        return divMain.html();
    },
    PortfolioName: function (cellValue, options, rowObject) {
        var divMain = $('<div>');
        var div = $('<div>');
        var displayName = "";
        if (rowObject[1] == '') {
            displayName = rowObject[4];
        } else {
            displayName = rowObject[1];
        }
        div.attr("id", "div" + rowObject[0]);
        div.attr("width", '50%');

        //Tooltip 

        var span = $('<span>');
        span.attr("title", rowObject[4]);
        span.attr("width", '50%');
        span.addClass("tooltip");
        span.html(displayName);
        span.appendTo(div);

        //var a = $('<a>');
        //a.attr("href", "javascript:void(0);");
        //a.addClass("actionAuthorisedPortfolioEdit");
        //a.html("Edit Label");
        //a.attr("style", 'float:right');
        //a.attr('data', OC.MVC.JSON.ToUrl({ editPortfolioName: displayName, authorisedUserId: rowObject[3], authoriserUserId: rowObject[0] }));

        //a.appendTo(div);
        div.appendTo(divMain);

        return divMain.html();
    },
   
    onEditClick: function (events) {
        var data = events.data.obj;
        IC.Public.showLoading("");
        $.ajax({
            url: OC.MVC.util.getLink("Authorisation/Authorisation", "GrantAuthorisation"),

            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                IC.Public.hideLoading();
                OC.MVC.util.errorMessage(err.responseText);;
            }
        });
    },
    HistoryTooltip: function (cellValue, options, rowObject) {
        var div = $('<div>');
        var span = $('<span>');
        span.attr("title", rowObject[4]);
        span.addClass("tooltip");
        span.html(rowObject[1]);
        span.appendTo(div);
        return div.html();
    }
};
;IC.Public.smsPin = function(options) {
    this.btnCancel = $('#btnCancel');
    this.btnNext = $('#btnNext');
    this.btnVerify = $('#btnVerify');
    this.btnBackToPortfolio = $('#btnBackToPortfolio');
    this.btnBackToPlanSummary = $('#btnBackToPlanSummary');

    this.hrnGrid = $('#SmsPinGrid');
    this.mobilePhoneVerificationDiv = $('#mobilePhoneVerificationDiv');
    this.showMobilePinVerificationDiv = false;
    this.errorDiv = $('#mainErrorDiv');
    this.verifyButtonDiv = $('#verifyButtonDiv');
    this.forgottenTransactionPasswordDiv = $('#forgottenTransactionPasswordDiv');
    this.confirmationSuccessMessage = $('#confirmationSuccessMessage');
};

IC.Public.smsPin.prototype = {
    init: function() {
        this.btnCancel.unbind('click');
        this.btnCancel.bind('click', { obj: this }, this.onBtnCancelClick);
        
        this.btnNext.unbind('click');
        this.btnNext.bind('click', { obj: this }, this.onBtnNextClick);
        
        this.btnVerify.unbind('click');
        this.btnVerify.bind('click', { obj: this }, this.onBtnVerifyClick);
        
        this.btnBackToPortfolio.unbind('click');
        this.btnBackToPortfolio.bind('click', { obj: this }, this.onBtnBackToPortfolioClick);
        
        this.btnBackToPlanSummary.unbind('click');
        this.btnBackToPlanSummary.bind('click', { obj: this }, this.onBtnBackToPlanSummaryClick);
        
        this.hrnGrid.unbind(OC.MVC.grid.events.loadComplete);
        this.hrnGrid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onLoadComplete);
        
        var showMobileVerification = $('#ShowMobilePinVerificationDiv').val();
        this.showMobilePinVerificationDiv = (showMobileVerification != undefined && showMobileVerification.toLowerCase() == "true");
        if (this.showMobilePinVerificationDiv) {
            this.showMobilePhoneVerificationDiv();
        }
    },
    
    onBtnBackToPortfolioClick: function (eventArgs) {
        var url = OC.MVC.util.getLink("Holdings", "Index");
        OC.MVC.util.loadMainContainerView(url);
    },

    onBtnBackToPlanSummaryClick: function (eventArgs) {
        var url = OC.MVC.util.getLink("Summary", "Summary");
        OC.MVC.util.loadMainContainerView(url);
    },
    
    onBtnCancelClick: function (eventArgs) {
        $.ajax({
            url: OC.MVC.util.getLink('SmsPin', 'ClearHrnSmsPinCache'),
            type: 'POST',
            success: function (response) {
                var url = IC.Public.urls.Settings.settings;
                OC.MVC.util.loadMainContainerView(url);
            },
            error: function (err) {
                var url = IC.Public.urls.Settings.settings;
                OC.MVC.util.loadMainContainerView(url);
            }
        });
        
    },
    
    onBtnNextClick: function(eventArgs) {
        var url = IC.Public.urls.Settings.smsPinRegistration;
        OC.MVC.util.loadMainContainerView(url);
    },
    
    onBtnVerifyClick: function (eventArgs) {
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        
        var self = eventArgs.data.obj;
        self.clearErrorMessage();
        self.confirmationSuccessMessage.hide();
        
        $.ajax({
            url: OC.MVC.util.getLink('SmsPin', 'VerifyHrns'),
            contentType: 'application/json; charset=utf-8',
            dataType: 'json',
            type: 'POST',
            data: self.getHrnVerificationData(),
            beforeSend: function() {
                IC.Public.showLoading("");
            },
            complete: function() {
                IC.Public.hideLoading();
            },
            success: function(response) {
                if (response) {
                    if (response.IsSuccess) {
                        self.hrnGrid.trigger('reloadGrid');
                        if (!response.ShowVerifyButton) {
                            self.btnVerify.parent().hide();
                        }
                        if (!response.DisplayForgottenTransactionPasswordLink) {
                            self.forgottenTransactionPasswordDiv.hide();
                        }
                        self.displayErrorMessages(response.ErrorMessages);
                        self.showMobilePhoneVerificationDiv();
                    } else {
                        self.displayErrorMessages(response.ErrorMessages);
                    }
                } else {
                    self.displayErrorMessage("An error occurred while verifying the pin. Please try again later.");
                }
            },
            error: function(err) {
                self.displayErrorMessage("An error occurred while verifying the pin. Please try again later.");
            }
        });
    },
    
    displayErrorMessages: function (errorMessages) {
        var errorHtml = "";
        $.each(errorMessages, function () {
            errorHtml += "<ol><li>" + this + "</li></ol>";
        });
        this.errorDiv.html(errorHtml);
    },
    
    displayErrorMessage: function (errorMessage) {
        this.errorDiv.html("<ol><li>" + errorMessage + "</li></ol>");
    },
    
    clearErrorMessage : function() {
        this.errorDiv.empty();
    },
    
    getHrnVerificationData: function () {
        var hrnModels = [];
        $.each($('.hrnSecurityTokenTextBox'), function () {
            var tokenValue = $(this).val();
            if (tokenValue != undefined && tokenValue != "") {
                hrnModels.push({ SecurityToken: $(this).val(), HrnKey: $(this).attr('hrnKey'), Sequence: $(this).attr('sequence'), IssuerCode: $(this).attr('issuerCode'), RegisteredHolderName: $(this).attr('registeredHolderName')});
            }
        });
        
        $.each($('.hrnTransactionPasswordTextBox'), function () {
            var transactionPassword = $(this).val();
            if (transactionPassword != undefined && transactionPassword != "") {
                hrnModels.push({ TransactionPassword: $(this).val(), HrnKey: $(this).attr('hrnKey'), Sequence: $(this).attr('sequence'), IssuerCode: $(this).attr('issuerCode'), RegisteredHolderName: $(this).attr('registeredHolderName'), IsEmployeePlan: true });
            }
        });

        return JSON.stringify(
            {
                hrnSecurityModels: hrnModels
            });
    },
    
    showMobilePhoneVerificationDiv: function () {
        var self = this;
        $.ajax({
            url: OC.MVC.util.getLink('SmsPin', 'VerifyMobile'),
            type: 'GET',
            beforeSend: function() {
                IC.Public.showLoading("");
            },
            complete: function() {
                IC.Public.hideLoading();
            },
            success: function(response) {
                //self.mobilePhoneVerificationDiv.html(response);
                OC.MVC.util.loadHtml('mobilePhoneVerificationDiv', response);
            },
            error: function(err) {
                alert("An error occurred while verifying the mobile pin. Please try again later.");
            }
        });
    },
    
    onLoadComplete: function (events, gridObj, data) {
        var self = events.data.obj;
        
        var requestPinLink = $('.smsPinRequestPinActionLink');
        requestPinLink.unbind('click');
        requestPinLink.bind('click', { obj: self }, self.onRequestPinLink);
        
        var enterPinLink = $('.smsPinEnterPinActionLink');
        enterPinLink.unbind('click');
        enterPinLink.bind('click', { obj: self }, self.onEnterPinClick);
        
        var enterTransactionPwdLink = $('.smsPinEnterTransactionPasswordActionLink');
        enterTransactionPwdLink.unbind('click');
        enterTransactionPwdLink.bind('click', { obj: self }, self.onEnterTransactionPwdLinkClick);
        
        var securityTokenTextBox = $('.hrnSecurityTokenTextBox');
        securityTokenTextBox.unbind('keyup');
        securityTokenTextBox.bind('keyup', { obj: self }, self.handleSecurityTokenKeyUpEvent);

        var transactionPasswordTextBox = $('.hrnTransactionPasswordTextBox');
        transactionPasswordTextBox.unbind('keyup');
        transactionPasswordTextBox.bind('keyup', { obj: self }, self.handleTransactionPasswordKeyUpEvent);
        
        self.processVerifyButtonState();
    },
    
    onRequestPinLink: function (eventArgs) {
        var self = eventArgs.data.obj;
        self.confirmationSuccessMessage.hide();
        
        var hrnKey = $(this).attr('hrnKey');
        var sequence = $(this).attr('sequence');
        var issuerCode = $(this).attr('issuerCode');
        
        $.ajax({
            url: OC.MVC.util.getLink('SmsPin', 'GenerateSecurityToken'),
            type: 'POST',
            data: { issuerCode: issuerCode, hrnKey: hrnKey, sequence: sequence },
            beforeSend: function () {
                IC.Public.showLoading("");
            },
            complete: function () {
                IC.Public.hideLoading();
            },
            success: function (response) {
                if (response) {
                    if (response.IsSuccess) {
                        self.hrnGrid.trigger('reloadGrid');
                        self.verifyButtonDiv.show();
                    } else {
                        self.displayErrorMessage(response.ErrorMessage);
                    }
                } else {
                    self.displayErrorMessage("An error occurred while verifying the pin. Please try again later.");
                }
            },
            error: function (err) {
                alert("An error occurred while verifying the mobile pin. Please try again later.");
            }
        });
    },
    
    onEnterPinClick: function(eventArgs) {
        var pinTextBox = $(this).parent().find('.hrnSecurityTokenTextBox');
        if (pinTextBox != undefined) {
            pinTextBox.show();
            $(this).hide();
        }
    },
    
    onEnterTransactionPwdLinkClick: function (eventArgs) {
        var transactionPwdTextBox = $(this).parent().find('.hrnTransactionPasswordTextBox');
        if (transactionPwdTextBox != undefined) {
            transactionPwdTextBox.show();
            $(this).hide();
        }
    },
    
    handleSecurityTokenKeyUpEvent: function (eventArgs) {
        var self = eventArgs.data.obj;
        self.processVerifyButtonState();
    },
    
    handleTransactionPasswordKeyUpEvent: function (eventArgs) {
        var self = eventArgs.data.obj;
        self.processVerifyButtonState();
    },

    processVerifyButtonState: function () {
        var self = this;
        self.clearErrorMessage();

        self.btnVerify.parent().addClass('disabled');
        var securityTokens = [];
        $.each($('.hrnSecurityTokenTextBox'), function () {
            var tokenValue = $(this).val();
            if (tokenValue != undefined && tokenValue.trim().length > 0) {
                securityTokens.push(tokenValue);
            }
        });

        if (securityTokens.length > 0) {
            self.btnVerify.parent().removeClass('disabled');
            return;
        }

        var transactionPasswords = [];
        $.each($('.hrnTransactionPasswordTextBox'), function () {
            var transactionPassword = $(this).val();
            if (transactionPassword != undefined && transactionPassword.trim().length > 0) {
                transactionPasswords.push(transactionPassword);
            }
        });

        if (transactionPasswords.length > 0) {
            self.btnVerify.parent().removeClass('disabled');
        }
    }
};IC.Public.mobilePinVerification = function(options) {
    this.btnBack = $('#btnBack');
	this.btnCancel = $('#btnCancel');
	this.btnConfirm = $('#btnConfirm');

    this.mobilePhoneGrid = $('#MobilePhoneGrid');
    this.errorDiv = $('#mobilePinErrorDiv');
    
};

IC.Public.mobilePinVerification.prototype = {    
    init: function () {
        this.btnBack.unbind('click');
        this.btnBack.bind('click', { obj: this }, this.onBtnBackClick);

	    this.btnCancel.unbind('click');
	    this.btnCancel.bind('click', { obj: this }, this.onBtnCancelClick);

	    this.btnConfirm.unbind('click');
	    this.btnConfirm.bind('click', { obj: this }, this.onBtnConfirmClick);
	    
	    this.mobilePhoneGrid.unbind(OC.MVC.grid.events.loadComplete);
	    this.mobilePhoneGrid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onLoadComplete);
	    
	    var isMobilePhoneVerified = $('#IsMobilePhoneVerified').val();
	    var enableConfirmButton = (isMobilePhoneVerified != undefined && isMobilePhoneVerified.toLowerCase() == "true");
	    if (enableConfirmButton) {
	        this.btnConfirm.parent().removeClass('disabled');
	    }
    },
    
    onBtnBackClick: function (eventArgs) {
        $.ajax({
            url: OC.MVC.util.getLink('SmsPin', 'ClearMobileNumber'),
            type: 'POST',
            beforeSend: function () {
                IC.Public.showLoading("");
            },
            complete: function () {
                IC.Public.hideLoading();
            },
            success: function (response) {
                var url = IC.Public.urls.Settings.smsPinRegistration;
                OC.MVC.util.loadMainContainerView(url);
            },
            error: function (err) {
                self.displayErrorMessage("An error occurred while clearing the mobile number. Please try again later.");
            }
        });
    },

    onBtnCancelClick: function (eventArgs) {
	    $.ajax({
	        url: OC.MVC.util.getLink('SmsPin', 'ClearHrnSmsPinCache'),
	        type: 'POST',
	        beforeSend: function () {
	            IC.Public.showLoading("");
	        },
	        complete: function () {
	            IC.Public.hideLoading();
	        },
	        success: function (response) {
	            var url = IC.Public.urls.Settings.settings;
	            OC.MVC.util.loadMainContainerView(url);
	        },
	        error: function (err) {
	            var url = IC.Public.urls.Settings.settings;
	            OC.MVC.util.loadMainContainerView(url);
	        }
	    });
	},

	onBtnConfirmClick: function (eventArgs) {
	    if ($(this).parent().hasClass('disabled')) {
			events.preventDefault();
			return;
		}

	    var self = eventArgs.data.obj;
	    self.clearErrorMessage();
	    $('#mainErrorDiv').empty();
	    
		$.ajax({
		    url: OC.MVC.util.getLink('SmsPin', 'ConfirmMobileNumber'),
		    type: 'POST',
		    beforeSend: function () {
		        IC.Public.showLoading("");
		    },
		    complete: function () {
		        IC.Public.hideLoading();
		    },
		    success: function (response) {
		        if (response) {
		            if (response.IsSuccess) {
		                var url = IC.Public.urls.Settings.smsPinRegistration;
		                OC.MVC.util.loadMainContainerView(url);
		            } else {
		                self.displayErrorMessage(response.ErrorMessage);
		            }
		        } else {
		            self.displayErrorMessage("An error occurred while confirming the mobile number. Please try again later.");
		        }
		    },
		    error: function (err) {
		        self.displayErrorMessage("An error occurred while confirming the mobile number. Please try again later.");
		    }
		});
	},
	
	onLoadComplete: function (events, gridObj, data) {
		var self = events.data.obj;

		var generatePinLink = $('.hrnGenerateMobilePinLink');
		generatePinLink.unbind('click');
		generatePinLink.bind('click', { obj: self }, self.onGeneratePinLink);

		var verifyPinLink = $('.hrnVerifyMobilePinLink');
		verifyPinLink.unbind('click');
		verifyPinLink.bind('click', { obj: self }, self.onVerifyPinClick);
	    
		self.callingCodeDropdown = $('.hrnCallingCodeDropdown');
		self.callingCodeDropdown.unbind('change');
		self.callingCodeDropdown.bind('change', { obj: self }, self.onCallingCodeDropdownChange);

		self.mobileNumberTextBox = $('.hrnMobileNumberTextBox');
		self.callingCodeTextBox = $('.hrnCallingCodeTextBox');

	},
    
	onCallingCodeDropdownChange: function (eventArgs) {
	    var self = eventArgs.data.obj;
        var callingCode = $(this).val();
        
        if (callingCode == "-1") {
            self.callingCodeTextBox.show();
        } else {
            self.callingCodeTextBox.hide();
        }
    },
	
	onGeneratePinLink: function (eventArgs) {
		var self = eventArgs.data.obj;
		self.clearErrorMessage();
	    
		var mobileNumber = self.getMobileNumber();
		if (mobileNumber == "") {
	        return;
	    }

		var hrnKeys = self.mobileNumberTextBox.attr('hrnKeys');
	    $.ajax({
	        url: OC.MVC.util.getLink('SmsPin', 'GenerateMobilePin'),
	        data: { hrnKeys: hrnKeys, mobileNumber: mobileNumber },
	        type: 'POST',
	        beforeSend: function() {
	            IC.Public.showLoading("");
	        },
	        complete: function() {
	            IC.Public.hideLoading();
	        },
	        success: function(response) {
				if (response) {
				    if (response.IsSuccess) {
				        self.mobilePhoneGrid.trigger('reloadGrid');
				        self.btnBack.show();
				    } else {
				    	self.displayErrorMessage(response.ErrorMessage);
				    }
				} else {
					self.displayErrorMessage("An error occurred while generating the mobile pin. Please try again later.");
				}
	        },
	        error: function(err) {
	        	self.displayErrorMessage("An error occurred while generating the mobile pin. Please try again later.");
	        }
	    });
		
	},
    
	getMobileNumber: function() {
	    var self = this;
	    var mobileNumber = self.mobileNumberTextBox.val().trim();
	    if (mobileNumber == "") {
	        alert("Please enter mobile number to proceed.");
	        return "";
	    }
	    
	    if (mobileNumber.startsWith('0')) {
	        mobileNumber = mobileNumber.substring(1);
	    }

        var callingCodeDropdownValue = self.callingCodeDropdown.val();
	    if (callingCodeDropdownValue != "-1") {
            return callingCodeDropdownValue + mobileNumber;
        }

	    var callingCodeTextBoxValue = self.callingCodeTextBox.val().trim();
	    if (callingCodeTextBoxValue == "") {
	        alert("Please enter country code to proceed.");
	        return "";
	    }

	    return callingCodeTextBoxValue + mobileNumber;
	},
	
	onVerifyPinClick: function(eventArgs) {
		var self = eventArgs.data.obj;
		self.clearErrorMessage();

		var mobilePinTextBox = $('.hrnMobilePinTextBox');
		var hrnKeys = mobilePinTextBox.attr('hrnKeys');
		var mobilePin = mobilePinTextBox.val();

		$.ajax({
			url: OC.MVC.util.getLink('SmsPin', 'VerifyMobilePin'),
			data: { hrnKeys: hrnKeys, mobilePin: mobilePin },
			type: 'POST',
			beforeSend: function () {
				IC.Public.showLoading("");
			},
			complete: function () {
				IC.Public.hideLoading();
			},
			success: function (response) {
				if (response) {
					if (response.IsSuccess) {
						self.mobilePhoneGrid.trigger('reloadGrid');
					    self.btnConfirm.parent().removeClass('disabled');
					} else {
						self.displayErrorMessage(response.ErrorMessage);
					}
				} else {
					self.displayErrorMessage("An error occurred while verifying the mobile pin. Please try again later.");
				}
			},
			error: function (err) {
				self.displayErrorMessage("An error occurred while verifying the mobile pin. Please try again later.");
			}
		});
	},
	
	displayErrorMessage: function (errorMessage) {
		this.errorDiv.html("<ol><li>" + errorMessage + "</li></ol>");
	},

	clearErrorMessage: function () {
		this.errorDiv.empty();
	}
};;IC.Public.registrationMobilePinVerification = function (options) {
    this.btnGeneratePin = $('#btnGeneratePin');
    this.btnVerifyPin = $('#btnVerifyPin');
    this.btnResendPin = $('#btnResendPin');
    this.hrnKey = $('#HrnKey');
    this.errorDiv = $('#mainErrorDiv');

};

IC.Public.registrationMobilePinVerification.prototype = {
    init: function () {
        this.btnGeneratePin.unbind('click');
        this.btnGeneratePin.bind('click', { obj: this }, this.onGeneratePinLink);

        this.btnVerifyPin.unbind('click');
        this.btnVerifyPin.bind('click', { obj: this }, this.onVerifyPinClick);

        this.btnResendPin.unbind('click');
        this.btnResendPin.bind('click', { obj: this }, this.onGeneratePinLink);

        $('#verifyMobilePINDiv').css('display', 'none');
        $('#generateMobilePINDiv').css('display', 'block');


        $('#IsMobilePhoneVerified').val();

    },
    onGeneratePinLink: function (eventArgs) {
        var self = eventArgs.data.obj;
        self.clearErrorMessage();

        var mobileNumber = self.getMobileNumber();
        if (mobileNumber == "") {
            return;
        }
        $('#MobileNumberVerify').val(mobileNumber);
        var issuerCode = $('#IssuerCode').val();
        var hrnKey = self.hrnKey.val();
        $.ajax({
            url: OC.MVC.util.getLink('SmsPin', 'GenerateRegistrationMobilePin'),
            data: { hrnKey: hrnKey, mobileNumber: mobileNumber, issuerCode: issuerCode },
            type: 'POST',
            beforeSend: function () {
                IC.Public.showLoading("");
            },
            complete: function () {
                IC.Public.hideLoading();
            },
            success: function (response) {
                if (response) {
                    if (response.IsSuccess) {
                        $('#verifyMobilePINDiv').css('display', 'block');
                        $('#generateMobilePINDiv').css('display', 'none');
                        $('#PhoneNumber').val(mobileNumber);
                        $(".verifiednumber").html("+" + mobileNumber);
                    } else {
                        self.displayErrorMessage(response.ErrorMessage);
                    }
                } else {
                    self.displayErrorMessage("An error occurred while generating the mobile pin. Please try again later.");
                }
            },
            error: function (err) {
                self.displayErrorMessage("An error occurred while generating the mobile pin. Please try again later.");
            }
        });

    },

    getMobileNumber: function () {
        var self = this;
        var mobileNumber = $('#MobileNumber').val().trim();
        if (mobileNumber == "") {
            alert("Please enter mobile number to proceed.");
            return "";
        }

        if (mobileNumber.startsWith('0')) {
            mobileNumber = mobileNumber.substring(1);
        }

        var callingCodeDropdownValue = $('#CallingCode').val();
        if (callingCodeDropdownValue != "-1") {
            return callingCodeDropdownValue + mobileNumber;
        }

        return callingCodeTextBoxValue + mobileNumber;
    },

    onVerifyPinClick: function (eventArgs) {
        var self = eventArgs.data.obj;
        self.clearErrorMessage();

        var hrnKey = self.hrnKey.val();
        var mobilePin = $('#MobilePin').val();

        $.ajax({
            url: OC.MVC.util.getLink('SmsPin', 'VerifyMobilePin'),
            data: { hrnKeys: hrnKey, mobilePin: mobilePin, isUserRegistration: true, MobileNumber: $("#PhoneNumber").val(), CacheKey: $("#CacheKey").val() },
            type: 'POST',
            beforeSend: function () {
                IC.Public.showLoading("");
            },
            complete: function () {
                IC.Public.hideLoading();
            },
            success: function (response) {
                if (response) {
                    if (response.IsSuccess) {
                        $('#IsMobilePhoneVerified').val(true);
                        $('#verifyPin').css('display', 'none');
                        $('#pinVerified').css('display', 'block');
                        self.enableOrDisableContinueButton();
                        //$('#PhoneNumber').val(self.mobileNumber);
                        //$(".labelDetail").html("+" + self.mobileNumber);
                    } else {
                        self.displayErrorMessage(response.ErrorMessage);
                    }
                } else {
                    self.displayErrorMessage("An error occurred while verifying the mobile pin. Please try again later.");
                }
            },
            error: function (err) {
                self.displayErrorMessage("An error occurred while verifying the mobile pin. Please try again later.");
            }
        });
    },
    displayErrorMessage: function (errorMessage) {
        this.errorDiv.html("<ol><li>" + errorMessage + "</li></ol>");
    },
    clearErrorMessage: function () {
        this.errorDiv.empty();
    },
    enableOrDisableContinueButton: function () {
        var isValid = true;
        $("input[type='text']:visible, input[type='password']:visible").each(function () {
            if ($(this).val().trim() == "") {
                isValid = false;
            }
        });

        $("#btnContinue").parent().removeClass('disabled');

        if (!isValid) {
            $("#btnContinue").parent().addClass('disabled');
        } else if (isValid && $("#IsMobilePhoneVerified").val().toLowerCase() == "true") {
            $("#btnContinue").parent().removeClass('disabled');
        }
    }
};;//IC.Public.urls = {
//    LoanRepayment: {
//        historicalLoanTransactions: OC.MVC.util.getLink("LoanRepayment", "ViewHistoricalLoanTransactions")
//    }
//    historicalLoanTransactions: OC.MVC.util.getLink("LoanRepayment", "ViewHistoricalLoanTransactions")
//};


IC.Public.Plans.RepaymentTypes =
{
    RepaymentCashDirectCredit: "CASH_Direct_Credit",
    RepaymentCash: "CSH",
    RepaymentSellShares: "SDS",
    RepaymentSellToCover: "STC",
    RepaymentPBAY: "BPY",
    RepaymentNone: "NON",
};
IC.Public.Plans.FormTypeValue =
{
    Sales: "Sales",
    PartialSales: "PartialSales",
    Cash: "Cash",
    ConfirmCash: "ConfirmCash",
    ConfirmSellShares: "ConfirmSellShares",
    ConfirmSellSharesPartial: "ConfirmSellSharesPartial",
};
IC.Public.Plans.LoanShareDeliveryMethods = {
    ManageShares: "MS",
    TransferToSRN: "TSRN",
    RestrictedShares:"RS"
};
IC.Public.Plans.GrantLevel =
{
    VestedRestricted: "VestedRestricted",
    VestedUnRestricted: "VestedUnRestricted",
    UnVested: "UnVested"
};

IC.Public.Plans.LoanRepaymentCustomFormatters =
{
    formatGrantDate: function (cellValue, options, rowObject) {
        //If grant description exists, display it as tooltip

        var uniqueId = IC.Public.gridFormatter.getCellValue($(this), "GrantId", rowObject);
        var grantDescription = IC.Public.gridFormatter.getCellValue($(this), "GrantDescription", rowObject);
        if (grantDescription == undefined || grantDescription.trim().length == 0)
            return cellValue;

        var clueToolTip = $('<div>');

        var a = $('<a>');
        a.attr("href", "#");
        a.attr("rel", ".tooltip" + uniqueId);
        a.attr("class", "grantDateTooltip cluetooltip");
        a.html(cellValue);
        a.appendTo(clueToolTip);

        var toolTipContent = $('<div>');
        toolTipContent.attr("class", "tooltip" + uniqueId);
        var tooltipTitle = "<p class='grantDetailsTooltipTitle'>Grant Description:</p>";
        toolTipContent.html(tooltipTitle);
        var tooltipMessage = "<p>" + grantDescription + "</p>";
        toolTipContent.html(toolTipContent.html() + tooltipMessage);

        toolTipContent.appendTo(clueToolTip);
        return clueToolTip.html();
    },

   
    planNameTooltip: function (cellValue, options, rowObject) {
        var div = $('<div>');
        var span = $('<span>');
        var index = $(this).getColIndex("PlanName");
        if (index < 0)
            index = $(this).getColIndex("Name");

        if (index >= 0) {
            span.attr("title", rowObject[index].replace('&amp;', '&').replace('&apos;', '\'').replace('&gt;', '>').replace('&lt;', '<'));
        }
        span.addClass("tooltip");
        span.html(cellValue);
        span.appendTo(div);
        return div.html();
    }
};//IC.Public.Plans.RepaymentDetails = function () {
//	//this.init(validateTransactionPassword);
//};
IC.Public.Plans.repaymentOption = {
    loanRepayment: 1,
    loanRepaymentTransactions: 2
};

IC.Public.Plans.RepaymentDetails = function () {
    this.init();
};
//IC.Public.Plans.RepaymentDetails = function (validateTransactionPassword) {
//    this.init(validateTransactionPassword);
//};
IC.Public.Plans.RepaymentDetails.prototype = {
    init: function () {
        this.action = $("[name=Action]");
        this.action.bind('click', { obj: this }, this.onActionClick);
        this.viewKey = $("#ViewKey");

        //ViewBar change event
        var self = this;
        var holdingFilter = $("form.holdingFilterForm");
        if (holdingFilter.length) {
            holdingFilter.unbind("applyHoldingFilter");
            holdingFilter.bind("applyHoldingFilter", { obj: self }, self.onFilterClick);
        }

       // IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.Exercise);
    },
    onFilterClick: function (events) {
        var url = OC.MVC.util.getLink("LoanRepayment", "Index");
        OC.MVC.util.loadMainContainerView(url, null);
    },

    onActionClick: function (events) {
        var _obj = events.data.obj;
        var value = $(this).val();
        var url = IC.Public.urls.LoanRepayment.loanRepaymentList;
        if (value == IC.Public.Plans.repaymentOption.loanRepaymentTransactions) {
            url = IC.Public.urls.LoanRepayment.historicalLoanTransactionsListItems;
            $("#divbottomPanel").hide();
            $("#repaymentOption").hide();
        }
        else if (value == IC.Public.Plans.repaymentOption.loanRepayment) {
            url = IC.Public.urls.LoanRepayment.loanRepaymentList;
            $("#divbottomPanel").show();
            $("#repaymentOption").show();
        }
       
        OC.MVC.util.loadView("grid-container", url, { viewKey: _obj.viewKey.val() });
    },
  
};IC.Public.Plans.repaymentDetailsList = function () {
    this.init();
};
//var repaymentOptionClicked = false;
//var selectedClicked = false;

IC.Public.Plans.repaymentDetailsList.RepaymentTypes =
{
    RepaymentCashDirectCredit: "CASH_Direct_Credit",
    RepaymentCash: "CSH",
    RepaymentSellShares: "SDS",
    RepaymentSellToCover: "STC",
    RepaymentPBAY: "BPY",
    RepaymentNone: "NON",
};

IC.Public.Plans.repaymentDetailsList.RepaymentOptions =
{
    PartialSellShares : 1,
    SellShares: 2,
    CashRepayment: 3
};

IC.Public.Plans.repaymentDetailsList.prototype = {
    init: function () {
        this.grid = $("#LoanRepaymentDetailsListGrid");
        this.grid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onLoadComplete);
        this.btnContinue = $("#btnContinue");
        this.btnContinue.bind('click', { obj: this }, this.onContinueClick);
        this.repaymentOption = $("[name=RepaymentOptionValue]");
        this.repaymentOption.bind('click', { obj: this }, this.onRepaymentOptionClick);
    },

    getSelectedPlans: function () {
        var plans = $('.select-checkbox:checked').map(function () {
            return $(this).attr('code');
        }).get();
        return plans;
    },
    DisplayPaymentOptions: function () {
        
        var items = $('.select-checkbox');
        var displayPartialSellShares = false;
        var displaySellShares = false;
        var displayCash = false;
        $.each(items, function (i) {
            var res = $(this).attr('paytype').split(";");
            if ($.inArray(IC.Public.Plans.RepaymentTypes.RepaymentCash, res) > -1
                || $.inArray(IC.Public.Plans.RepaymentTypes.RepaymentPBAY, res) > -1) {
                displayCash = true;
            }
            if ($.inArray(IC.Public.Plans.RepaymentTypes.RepaymentSellShares, res) > -1
            || $.inArray(IC.Public.Plans.RepaymentTypes.RepaymentSellToCover, res) > -1) {
                if ($(this).attr('AllowPartial')=="Y") {
                    displayPartialSellShares = true;
                }
                else { displaySellShares = true; }
            }
        });
        var repaymentOption = $("[name=RepaymentOptionValue]");
        $.each(repaymentOption, function (i) {
            $(this).next().css("text-decoration", "underline");
            IC.Public.Plans.repaymentDetailsList.prototype.displayORhideRePaymentOptionsValues($(this), displayPartialSellShares, displaySellShares, displayCash);
        });
    },
    displayORhideRePaymentOptionsValues: function (thisObj, displayPartialSellShares, displaySellShares, displayCash)
    {
        var sellShares = (thisObj.attr('value') == IC.Public.Plans.repaymentDetailsList.RepaymentOptions.SellShares);
        var partialSellShares = (thisObj.attr('value') == IC.Public.Plans.repaymentDetailsList.RepaymentOptions.PartialSellShares);
        var sellCash = (thisObj.attr('value') == IC.Public.Plans.repaymentDetailsList.RepaymentOptions.CashRepayment);

        if (sellShares || partialSellShares) {
            var tooltip = $("#resourceSellShares").val();
            thisObj.next().attr('title', tooltip);
        }
        if (sellCash) {
            var tooltip = $("#resourceCashPay").val();
            thisObj.next().attr('title', tooltip);
        }

        if ((displaySellShares) && sellShares) {
            thisObj.parent().removeAttr('hidden');
        }
        else if ((displayPartialSellShares) && partialSellShares) {
            thisObj.parent().removeAttr('hidden');
        }
        else if (displayCash && sellCash) {
            thisObj.parent().removeAttr('hidden');
        }
        else {
            thisObj.parent().attr('hidden', "hidden");
        }
    },
    onRepaymentOptionClick: function (events) {
        var _obj = events.data.obj;
        var plans = _obj.getSelectedPlans();
        if (plans.length > 0 && $("[name=RepaymentOptionValue]:checked").length>0) {
            _obj.btnContinue.parent().removeClass('disabled');
        }
        else {
            _obj.btnContinue.parent().addClass('disabled');
        }
    },
    onSelectClick: function (events) {
        var _obj = events.data.obj;
        var plans = _obj.getSelectedPlans();

        if (plans.length > 0 && $("[name=RepaymentOptionValue]:checked").length > 0) {
            _obj.btnContinue.parent().removeClass('disabled');
        }
        else {
            _obj.btnContinue.parent().addClass('disabled');
        }
        if ($(this).attr('checked') && $(this).attr('AllowMultiple')!="Y") {
            $('.select-checkbox').attr('checked', false);
            $(this).attr('checked', true);
        }
        //if (IC.Public.IsThirdPartyImpersonateUser())
        //    _obj.btnExercise.parent().addClass('disabled');
    },
    //$('input.example').on('change', function() {
    //    $('input.example').not(this).prop('checked', false);  
    //});
    onLoadComplete: function (events) {
        var _obj = events.data.obj;
        $('.cluetooltip').bind('click', _obj.onLinkClick);
        $('.select-checkbox').bind('click', { obj: _obj }, _obj.onSelectClick);
        _obj.DisplayPaymentOptions();
        if (_obj.grid.getRowData().length == 0) {
             $("#repaymentOption").hide(); 
        }
        $("#divbottomPanel").show();
    },
    onLinkClick: function () {
        var type = $(this).attr("type");
        var code = $(this).attr("code");
        var viewKey = $("#ViewKey").val();
        var transactionType = $(this).attr("transactionType");
        var windowId = $(this).attr("windowId");

        var modal = new IC.Public.Plans.LoanRepaymentMessage();
        modal.showModal(type, code, viewKey, transactionType, windowId, IC.Public.Plans.Common.MenuSubItemTypes.LoanRepayment);
    },

    onContinueClick: function (events) {
        
        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        var url = '';
            if ($("[name=RepaymentOptionValue]:checked").val() == IC.Public.Plans.repaymentDetailsList.RepaymentOptions.SellShares) {
                url = IC.Public.urls.LoanRepayment.loanRepaymentShares;
            }
           else if ($("[name=RepaymentOptionValue]:checked").val() == IC.Public.Plans.repaymentDetailsList.RepaymentOptions.CashRepayment) {
                url = IC.Public.urls.LoanRepayment.loanRepaymentCash;
           }
           else if ($("[name=RepaymentOptionValue]:checked").val() == IC.Public.Plans.repaymentDetailsList.RepaymentOptions.PartialSellShares) {
               url = IC.Public.urls.LoanRepayment.loanRepaymentPartialShares;
           }
        var viewKey = $('#ViewKey').val();
        var plans = _obj.getSelectedPlans().join(',');
        
        OC.MVC.util.loadMainContainerView(url, { viewKey: viewKey, plans: plans });
    }
};IC.Public.Plans.HistoricalLoanTransactions = function () {
    this.init();
};

IC.Public.Plans.HistoricalLoanTransactions.prototype = {
        init: function () {
            var gridLinksInitialised = false;
            var rowCount = 0;
            this.historicalLoanTransactionsTransactionsGrid = $("#HistoricalLoanTransactionsGrid");
            this.historicalLoanTransactionsTransactionsGrid.bind("grid_gridComplete", { obj: this }, function (events, gridContext) {
                rowCount = gridContext;
                var _obj = events.data.obj;
                $(this).find("a.view-confirmation").each(function (index) {
                    if ($(this).attr('key') && $(this).attr('key').trim().length > 0) {
                        gridLinksInitialised = true;
                        $(this).bind("click", { obj: _obj }, _obj.viewConfirmationClick);
                    }
                });
                $("#divbottomPanel").hide();
              //  this.hideRepaymentOptions();
                //SetRepaymentOptions(IC.Public.Plans.repaymentOption.loanRepaymentTransactions);
            });

            // Sometimes the grid is loaded before the gridComplete event can be binded, so we need to reload so that View Confirmation click event is binded
            if (rowCount > 0 && !gridLinksInitialised) {
                this.historicalLoanTransactionsTransactionsGrid.trigger('reloadGrid');
            }
            //IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.Exercise);
        },
      
        viewConfirmationClick: function () {
            var repayId = $(this).attr('key');
            var url = OC.MVC.util.getLink("LoanRepayment", "ViewConfirmation");
            OC.MVC.util.loadMainContainerView(url, { viewKey: $('#ViewKey').val(), repaymentId: repayId });
        }
    };
;IC.Public.Plans.RepaymentDetailsCash = function (validateTransactionPassword) {
    this.init(validateTransactionPassword);
};

IC.Public.Plans.RepaymentDetailsCash.prototype = {
    init: function (validateTransactionPassword) {

        this.blockRepaymentMethodProcessing = false;
        this.validateTransactionPassword = validateTransactionPassword;
        var self = this;
        this.registerGridRelatedControlEvents();
        this.registerButtonEvents();
        this.TermsAndConditionsAccepted = $("#TermsAndConditionsAccepted");
        this.TermsAndConditionsAccepted.change(function () {
            self.validateData();
        });

        this.salesTermsAndConditions = $('.salesTermsAndConditions');
        this.transferTermsAndConditions = $('.transferTermsAndConditions');
        this.generalTermsAndConditions = $('.generalTermsAndConditions');
        this.displayTermsAndConditions();
        this.errorContainer = $('.errorContainer');
        if (this.errorContainer.html() == '')
            this.errorContainer.hide();

        $('.toolTipMessage').each(function () {
            $('.helpLink-tooltip').cluetip({ showTitle: false, local: "true" });
        });
        $("a.cluetooltip").cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 350, cluetipClass: "exercise" });
        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.LoanRepaymentDetailsCash);
    },
    displayTermsAndConditions: function () {

        var SalesMethod = $("[name=SalesMethod]");
        var salesTermsAndConditions = $('.salesTermsAndConditions');
        var transferTermsAndConditions = $('.transferTermsAndConditions');

        if (SalesMethod.is(':checked')) {
            salesTermsAndConditions.show();
        } else {
            salesTermsAndConditions.hide();
        }

        var shareDeliveryMethod = $('input[name=ShareDeliveryMethod]:checked').val();
        if (shareDeliveryMethod == IC.Public.Plans.ShareDeliveryMethods.TransferToSRN) {
            transferTermsAndConditions.show();
        } else {
            transferTermsAndConditions.hide();
        }
    },
    registerGridRelatedControlEvents: function () {
        this.loanRepaymentAllCheckBox = $("#IsRepayAll");
        this.loanRepaymentAllCheckBox.unbind('click');
        this.loanRepaymentAllCheckBox.bind('click', { obj: this }, this.onRepayAllClick);

        this.loanRepaymentAmountCheckBox = $("#IsRepayAmount");
        this.loanRepaymentAmountCheckBox.unbind('click');
        this.loanRepaymentAmountCheckBox.bind('click', { obj: this }, this.onRepayAmountClick);

        this.repaymentAmountTextBox = $("#RepayAmount");
        this.repaymentAmountTextBox.unbind('blur');
        this.repaymentAmountTextBox.bind('blur', { obj: this }, this.onRepayAmountBlur);
       // this.repaymentAmountTextBox.numeric();
        //  this.TotalAvailable = $("#TotalAvailable");
        this.TotalRepaymentAmount = $("#TotalRepaymentAmount");
        this.TotalOutStatndingLoanAmount = $("#TotalOutStatndingLoanAmount");

        this.grid = $("#LoanRepaymentDetailsGridCash");
        this.grid.unbind(OC.MVC.grid.events.loadComplete);
        this.grid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onLoadComplete);
    },
    onRepayAllClick: function (events) {
        var _obj = events.data.obj;
        var value = $(this).is(':checked');
        _obj.toggleOptionsToTransactValues(value);
        _obj.toggleRepayAmountInputs(!value);

        if (!_obj.blockRepaymentMethodProcessing)
            _obj.processRepaymentMethods();
    },
    toggleRepayAmountInputs: function (value) {
        $("#IsRepayAmount").attr('checked', value);
        $("#RepayAmount").attr('disabled', !value);
        if (!value) {
            $("#RepayAmount").val('');
        }
    },

    processRepaymentMethods: function () {
        var totalAmountSelected = $('#TotalRepaymentAmount').text();
        if (totalAmountSelected == "" || totalAmountSelected == "0.00") {

            $('#loanRepaymentMethodLi_' + IC.Public.Plans.RepaymentTypes.RepaymentCash).show();//replace with RepaymentTypes.RepaymentCash,
            $('#loanRepaymentMethodLi_' + IC.Public.Plans.RepaymentTypes.RepaymentCashDirectCredit).show();//replace with RepaymentTypes.RepaymentCashDirectCredit

            this.validateData();

            return;

        }

        var _totalCashRepaymentPrice = $('.total-price').map(function () {
            var _value = parseFloat($(this).text().replaceAll(',', ''));
            return isNaN(_value) ? 0 : _value;
        }).get();
        if (_totalCashRepaymentPrice.sum() == 0) {
            ;
            this.displayOrHideRepaymentMethod(IC.Public.Plans.RepaymentTypes.RepaymentCash, false); //replace with RepaymentTypes.RepaymentCash,
            this.displayOrHideRepaymentMethod(IC.Public.Plans.RepaymentTypes.RepaymentCashDirectCredit, false);//replace with RepaymentTypes.RepaymentCashDirectCredit

        } else {

            this.displayOrHideRepaymentMethod(IC.Public.Plans.RepaymentTypes.RepaymentCash, true);//replace with RepaymentTypes.RepaymentCash,
            this.displayOrHideRepaymentMethod(IC.Public.Plans.RepaymentTypes.RepaymentCashDirectCredit, true);//replace with RepaymentTypes.RepaymentCashDirectCredit

        }
        this.validateData();
    },
    //need to be tested
    displayOrHideRepaymentMethod: function (repaymentMethodCode, show) {
        var isMethodSeletced = $('#LoanRepaymentMethods' + repaymentMethodCode).is(':checked');
        if (show) {
            $('#loanRepaymentMethodLi_' + repaymentMethodCode).show();
            if (isMethodSeletced)
                $('#LoanRepaymentMethods' + repaymentMethodCode + '_container').show();
        } else {
            $('#loanRepaymentMethodLi_' + repaymentMethodCode).hide();
            $('#LoanRepaymentMethods' + repaymentMethodCode + '_container').hide();
        }
    },

    onRepayAmountClick: function (events) {
        var _obj = events.data.obj;
        var value = $(this).is(':checked');
        _obj.toggleRepayAllInputs(_obj, !value);
        _obj.toggleRepayAmountInputs(value);
        $("#RepayAmount").focus();
    },
    toggleRepayAllInputs: function (obj, value) {
        $("#IsRepayAll").attr('checked', value);
        obj.toggleOptionsToTransactValues(value);
    },
    onRepayAmountChange: function (events) {
        var self = events.data.obj;
        self.handleGridRepayAmountChange($(this), true);
        //need to check this
        if (!self.blockRepaymentMethodProcessing)
            self.processRepaymentMethods();
    },
    onAmountToRepayBlur: function (events) {
        if ($(this).val() != "")
            $(this).val($.formatNumber($(this).val().replaceAll(",", ""), "#,0.00"));
    },
    onRepayAmountBlur: function (events) {
        if ($(this).val() != "")
            $(this).val($.formatNumber($(this).val().replaceAll(",", ""), "#,0.00"));
        var repayAmountText = $(this).val().replaceAll(",", "");
        if (repayAmountText == "")
            return;
        var repayAmount = parseFloat(repayAmountText);
        if (isNaN(repayAmount))
            return;

        var _obj = events.data.obj;
        _obj.distributeRepayAmount(repayAmount);
        
        // if (!_obj.blockExerciseMethodProcessing)
        //     _obj.processExerciseMethods();
    },
    distributeRepayAmount: function (repayAmount) {
        var self = this;
        $.each($('.numberToRepayLoan'), function (i, item) {
            if (repayAmount <= 0)
                repayAmount = 0;//return;

            var tr = $(item).parents('tr');
            var availableSecurities = parseFloat(tr.getColumnValue('OutStandingLoanAmount').replaceAll(",", ""));
            //  var availableSecurities = parseInt(tr.getColumnValue('Available Securities').replaceAll(",", ""));
            var repayAmountTextBox = $(item);
            if (repayAmount <= availableSecurities) {
                repayAmountTextBox.val($.formatNumber(repayAmount, "#,0.00"));
            } else {
                repayAmountTextBox.val($.formatNumber(availableSecurities, "#,0.00"));
            }
            self.handleGridRepayAmountChange(repayAmountTextBox, false);
            repayAmount -= availableSecurities;
        });
    },
    toggleOptionsToTransactValues: function (value) {
        var self = this;
        $.each($('.numberToRepayLoan'), function (i, item) {
            var tr = $(item).parents('tr');
            var availableSecurities = parseFloat(tr.getColumnValue('OutStandingLoanAmount').replaceAll(",", ""));
            //var availableSecurities = parseInt(tr.getColumnValue('Available Securities').replaceAll(",", ""));
            var repayAmountTextBox = $(item);
            if (value == true) {
                repayAmountTextBox.val($.formatNumber(availableSecurities, "#,0.00"));
            } else {
                repayAmountTextBox.val('');
            }
            //numberToExerciseTextBox.trigger('keyup');
            self.handleGridRepayAmountChange(repayAmountTextBox, false);
        });
    },
    registerButtonEvents: function () {
        this.btnContinue = $('#btnContinue');
        this.btnContinue.unbind('click');
        this.btnContinue.bind("click", { obj: this }, this.onContinueButtonClick);

        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind('click');
        this.btnCancel.bind('click', function () {
            var url = OC.MVC.util.getLink("LoanRepayment", "Index");
            OC.MVC.util.loadMainContainerView(url);
        });
    },

    handleGridRepayAmountChange: function (repayAmountControl, unCheckRepayAmountChecboxes) {
        var self = this;
        var tr = repayAmountControl.parents('tr');
        var td = tr.getColumnCell('TotalRepaymentAmount');
        var value = repayAmountControl.val();
        var totalEl = td.find('.total');

        if (value) {
            var loanBalancePrice = parseFloat(tr.getColumnValue('OutStandingLoanAmount').replaceAll(",", ""));
            var _loanBalancePrice = parseFloat(loanBalancePrice);
            var _amount = parseFloat(repayAmountControl.val());

            if (!isNaN(_loanBalancePrice) && !isNaN(_amount)) {
                //var availableSecurities = parseInt(tr.getColumnValue('Available Securities').replaceAll(",", ""));
                if (_loanBalancePrice < _amount) {
                    alert('Amount to Repayment must be less or equal to Out Standing Loan Amount options');
                    totalEl.hide();
                    td.find('.total-price').text('');

                    repayAmountControl.val('');
                    self.processRepayAmountCheckBoxes(unCheckRepayAmountChecboxes);
                    self.updateTotals();
                    return;
                }

                //var total = _loanBalancePrice * repayAmountControl.val();
                var total = _loanBalancePrice;
                totalEl.show();
                td.find('.total-price').text($.formatNumber(total, "#,0.00"));

                self.processRepayAmountCheckBoxes(unCheckRepayAmountChecboxes);
            }
        } else {
            totalEl.hide();
            td.find('.total-price').text('');
        }
        self.updateTotals();
        self.ValidateShareDeliveryMethods(self);
    },
    processRepayAmountCheckBoxes: function (uncheck) {
        if (uncheck) {
            $("#IsRepayAll").attr('checked', false);
            $("#IsRepayAmount").attr('checked', false);
            $("#RepayAmount").val('');
        }
    },
    onLoadComplete: function (events, gridObj, data) {
        var _obj = events.data.obj;
        if (data && data.userdata)
            _obj.TotalAvailable.text($.formatNumber(data.userdata.TotalAvailable, "#,0"));
        var amountoRepay = $('.numberToRepayLoan');
        //amountoRepay.numeric();
        amountoRepay.unbind('keyup');
        amountoRepay.bind('keyup', { obj: _obj }, _obj.onRepayAmountChange);
        amountoRepay.bind('blur', { obj: _obj }, _obj.onAmountToRepayBlur);
        var warnings = $('div.underwater-warning');
        var warningTextContainer = $('div.underwater-warning-container');
        if (warnings.length > 0)
            warningTextContainer.show();
        else
            warningTextContainer.hide();

        amountoRepay.each(function () {
            var hasValue = ($(this).val() == "");
            //need to check 
            _obj.blockRepaymentMethodProcessing = hasValue;
            $(this).trigger('keyup');
            //need to check 
            _obj.blockRepaymentMethodProcessing = false;
        });
        _obj.ValidateShareDeliveryMethods(_obj);
        //need to check 
        //var restrictionsLinks = $('.restrictions-link');
        //need to check this.
        // restrictionsLinks.unbind('click');
        //restrictionsLinks.bind('click', { obj: _obj }, _obj.onRestrictionsClick);
        //  var precleranceplancodes = _obj.getPreClearanceBlackoutPlans();
        _obj.updateTotals();
        $("A.grantDateTooltip").each(function () {
            $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 150 });
        });

    },
    updateTotals: function () {
        var _valueOutstanding = 0;
        var _valueAmountToRepay = 0;

        $('#LoanRepaymentDetailsGridCash > tbody  > tr').each(function () {

                var outStanding = 0;
                var amountTxt = $(this).children().find('.numberToRepayLoan');
                outStanding = parseFloat($(this).getColumnValue('OutStandingLoanAmount').replaceAll(",", ""));
                var _value = $(amountTxt).val();
                _valueAmountToRepay += isNaN(parseFloat(_value.replaceAll(",", ""))) ? 0 : parseFloat(_value.replaceAll(",", ""));
                _valueOutstanding += isNaN(parseFloat(outStanding)) ? 0 : parseFloat(outStanding);
        });
        var totalRepaymentAmount = _valueAmountToRepay.toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
        this.TotalRepaymentAmount.text(totalRepaymentAmount);
        //this.TotalRepaymentAmount.text($.formatNumber(_valueAmountToRepay, "#,0.00"));
        this.TotalOutStatndingLoanAmount.text($.formatNumber(_valueOutstanding, "#,0.00"));
        this.validateData();
    },
    getSellPrice: function (element) {
        var sellprice = element.find('.sell-price');
        var inputSellPrice = sellprice.find('input');
        var value;

        if (inputSellPrice.length > 0) {
            value = parseFloat(inputSellPrice.val());
        } else
            value = parseFloat(sellprice.text());

        if (isNaN(value)) return 0;
        return value;
    },
    updateEstimatedValues: function (element, methodCode) {
        if (!element) {
            var salesMethod = $('input[name=SalesMethod]:checked');
            element = salesMethod.parent().parent().find('.method-details');
            methodCode = salesMethod.val();
        }
        var EstimatedSaleValue = $("#EstimatedSaleValue");
        if (methodCode == IC.Public.Plans.SaleMethods.SellByLimit || methodCode == IC.Public.Plans.SaleMethods.GoodTillCancel ||
            methodCode == IC.Public.Plans.SaleMethods.SellByMarketOrder) {
            var sellPrice = this.getSellPrice(element);
            var estimateSaleValue = (parseFloat($("#RepaymentMethodObjectModel_TotalRepaymentAmount").text().replaceAll(",", "")) * sellPrice).toFixed(2);
            EstimatedSaleValue.text($.formatNumber(estimateSaleValue, "#,0.00"));
        }

    },
    getAmountToRepay: function () {
        var result = $('.numberToRepayLoan').map(function (index, item) {
            var grantId = $(item).attr('grantId');
            var planCode = $(item).attr('planCode');
            var amountoRepay = $(item).val().replace(/,/g, '');
            var grantLevel = $(item).attr('grantLevel');
            var grantLvlitems = grantLevel.split(';');
            var array = [];
            if (grantLvlitems.length != 0) {
                $.each(grantLvlitems, function (index, iLvl) {
                    array.push(iLvl.split('#')[0]);
                });
                grantLevel = array.join(';');
            }
            if (amountoRepay == '' || amountoRepay == '0') return null;
            return grantId + "|" + amountoRepay + "|" + planCode + "|" + grantLevel;
        }).get();
        return result.join(',');
    },
    getselectedPlans: function () {
        var result = $('.numberToRepayLoan').map(function (index, item) {
            var planCode = $(item).attr('planCode');
            var amountoRepay = $(item).val();
            if (amountoRepay == '' || amountoRepay == '0') return null;
            return planCode;
        }).get();

        if (result.length != 0) {
            result = jQuery.unique(result);
        }
        return result.join(',');

    },
    getPreClearanceBlackoutPlans: function () {

        var preclearance = $("#preClearancetxt").val();
        var blackOut = $("#blackOuttxt").val();
        var planCodes = '';
        $('#LoanRepaymentDetailsGridCash > tbody  > tr').each(function () {

            var txtPlan = ($(this).getColumnValue('RepaymentAction').replaceAll(",", ""));
            if (preclearance == txtPlan || blackOut == txtPlan) {
                var code = ($(this).getColumnValue('PlanName'));
                if (planCodes == '')
                { planCodes = code; }
                else { planCodes = planCodes + ',' + code; }
            }

        });
        return planCodes;
    },
    ValidateShareDeliveryMethods: function (self) {
        if ($('input[name=ShareDeliveryMethod]') == null || $('input[name=ShareDeliveryMethod]').length == 0) {
            return;
        }
     //   var sharedeliveryMethodRS = false;//
        var displayShareDeliveryMethod = false;
        $('input[name=ShareDeliveryMethod]').attr("disabled", true);

        $('.numberToRepayLoan').each(function () {
            var amountoRepay = $(this).val();
            var grantLevel = $(this).attr('grantLevel');
            if (amountoRepay != '' && amountoRepay != '0') {
                //new 
                var tr = $(this).parents('tr');
                var loanBalancePrice = parseFloat(tr.getColumnValue('OutStandingLoanAmount').replaceAll(",", ""));
                if (parseFloat(amountoRepay) >= loanBalancePrice)
                {
                    displayShareDeliveryMethod = true;
                }
            }
        });

        if (displayShareDeliveryMethod) {
            self.showHideShareDeliveryMethod(true);
        } 
        else {
            self.showHideShareDeliveryMethod(false);
        }

        //if (sharedeliveryMethodRS)
        //{ self.enableShareDeliveryMethodsRS(); }
        //else { self.disableShareDeliveryMethodsRS(); }
    },
    showHideShareDeliveryMethod: function (val) {
        $.each($('input[name=ShareDeliveryMethod]'), function () {
            if (!val) {
                $('p[id=shareDeliveryHeaderId]').hide();
                $(this).closest('ul').hide();
                $(this).attr("checked", false);
            }
            else {
                $('p[id=shareDeliveryHeaderId]').show();
                $('input[name=ShareDeliveryMethod]').attr("disabled", false);
                $(this).closest('ul').show();
            }

        });
    },
    disableShareDeliveryMethodsRS: function () {
        var shareDelivery = $('input[name=ShareDeliveryMethod]:checked').val();
        if (IC.Public.Plans.LoanShareDeliveryMethods.RestrictedShares == shareDelivery) {
            $('input[name=ShareDeliveryMethod]').attr("checked", false);
        }
        $.each($('input[name=ShareDeliveryMethod]'), function () {
            if ($(this).val() == IC.Public.Plans.LoanShareDeliveryMethods.RestrictedShares) {
                $(this).attr('disabled', true);
            }
            else {
                if ($(this).attr('preblack') == 'false') {
                    $(this).attr('disabled', false);
                }
            }
        });
    },
    enableShareDeliveryMethodsRS: function () {
        var shareDelivery = $('input[name=ShareDeliveryMethod]:checked').val();
        if (IC.Public.Plans.LoanShareDeliveryMethods.RestrictedShares != shareDelivery) {
            $('input[name=ShareDeliveryMethod]').attr("checked", false);
        }
        $.each($('input[name=ShareDeliveryMethod]'), function () {
            if ($(this).val() != IC.Public.Plans.LoanShareDeliveryMethods.RestrictedShares) {
                $(this).attr('disabled', true);
            }
            else {
                if ($(this).attr('preblack') == 'false') {
                    $(this).attr('disabled', false);
                }
            }
        });
    },
    validateData: function () {
        var btnContinue = $('#btnContinue');
        if (!$('input[name=LoanRepaymentMethods]:checked').is(':visible')) {
            btnContinue.parent().addClass('disabled');
            return false;
        }
        var repaymentMethod = $('input[name=LoanRepaymentMethods]:checked').val();
        var isValid = repaymentMethod != null;
        var selectedGrants = this.getAmountToRepay();
        isValid = isValid &&
            (selectedGrants && selectedGrants != '') &&
            $("#TermsAndConditionsAccepted").is(':checked') &&
            this.checkMethodDetails(repaymentMethod) && //neeed to check this method relevance for cash
            $("#HasUnregisteredHrns").val().toLowerCase() == "false";
        if (isValid)
            btnContinue.parent().removeClass('disabled');
        else
            btnContinue.parent().addClass('disabled');
        return isValid;
    },

    checkMethodDetails: function (repaymentMethod) {
        var isValid = true;
        if ($('p[id=shareDeliveryHeaderId]').css('display') == 'none' || $('p[id=shareDeliveryHeaderId]').css('display') == undefined) { return isValid; };

        var shareDelivery = $('input[name=ShareDeliveryMethod]:checked').val();
        isValid = (shareDelivery != null)
        if (isValid && shareDelivery == IC.Public.Plans.ShareDeliveryMethods.TransferToSRN) {
            isValid = this.checkTransferToSrnShareDeliveryMethodDetails(repaymentMethod);
        }
        return isValid;
    },

    checkTransferToSrnShareDeliveryMethodDetails: function (repayMethod) {
        var isValid = true;
        var methodDetailsContainer = $('#LoanRepaymentMethods' + repayMethod + '_container');
        var transferToSrnTextBox = methodDetailsContainer.find("#UserSRN[type='text']");
        if (transferToSrnTextBox != undefined && transferToSrnTextBox.val() != undefined) {
            isValid = (transferToSrnTextBox.val().trim().length > 0);
        }

        return isValid;
    },
    validateLoanRepaymentCash: function (data) {
        //alert("Post data populated;Confirmation Screen navigation to be enabled")
        var url = OC.MVC.util.getLink("LoanRepayment", "ValidateLoanRepayment");
        IC.Public.showLoading("");
        $.ajax({
            url: url,//confirm url 
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    onContinueButtonClick: function (events) {

        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        //var plans = _obj.grid.getPostDataItem('plans');
        var plans = _obj.getselectedPlans();
        var selectedGrants = _obj.getAmountToRepay();
        if (!selectedGrants) return;
        //var selectedPlans = plans;
        $('#hdnPlans').val(plans);
        $('#hdnSelectedGrants').val(selectedGrants);
        var form = $("form#confirmRepaymentCashForm");
        var data = form.serializeObject();

        var repaymentMethod = $('input[name=LoanRepaymentMethods]:checked').val();
        var methodDetailsContainer = $('#LoanRepaymentMethods_' + repaymentMethod + '_container');
        if (data.ShareDeliveryMethod == IC.Public.Plans.ShareDeliveryMethods.TransferToSRN) {
            var srnControl = methodDetailsContainer.find("[name='UserSRN']");
            var srnValue = srnControl.val();
            if (srnValue == "") srnValue = srnControl.text();
            var noDataAttributeValue = srnControl.attr('data-no-value');
            if (noDataAttributeValue == "true") srnValue = "";
            $('#hdnTransferToSrn').val(srnValue);
        }
        if (data.SalesMethod == IC.Public.Plans.SaleMethods.SellByLimit)
            $('#hdnLimitPrice').val(methodDetailsContainer.find('#SellByLimitPrice').val());
        else if (data.SalesMethod == IC.Public.Plans.SaleMethods.GoodTillCancel)
            $('#hdnLimitPrice').val(methodDetailsContainer.find('#GoodTillCancelPrice').val());

        data = form.serializeObject();
        _obj.validateLoanRepaymentCash(data);
    }

}
;//IC.Public.Plans.loanRepaymentTermsAndConditions = function () {
//    this.init();
//};

//IC.Public.Plans.FormType = {
//    Sales: "Sales",
//    PartialSales: "PartialSales",
//    Cash: "Cash"
//};

//IC.Public.Plans.loanRepaymentTermsAndConditions.prototype = {
//    init: function () {
//        this.action = $("#TermsAndConditionsAccepted");
//        this.action.bind('click', { obj: this }, this.onProcessAction);
//        var formType = $("#FormType").val();
//        if(formType==IC.Public.Plans.FormType.Sales ||formType==IC.Public.Plans.FormType.PartialSales ||formType==IC.Public.Plans.FormType.Cash )
//        {
//            $("#termsDiv").show();
//        }
//    },
//    onProcessAction: function () {
//        var formType = $("#FormType").val();
//        if (formType == IC.Public.Plans.FormType.Sales) {
//            //sales methods 
//            alert('sales')
//        }
//        else if (formType == IC.Public.Plans.FormType.PartialSales) {
//            //partial sales methods here 
//            alert('PartialSales sales')
//        }
//        else if (formType == IC.Public.Plans.FormType.Cash) {
//            //cash methods here 
//            alert('cash')
//        }
//    }
//}

;IC.Public.Plans.loanRepaymentPaymentMethodCash = function (validateTransactionPassword) {
    this.init(validateTransactionPassword);
};



IC.Public.Plans.loanRepaymentPaymentMethodCash.prototype = {
    init: function (validateTransactionPassword) {

        var divContent = '';
        $("[name='containerDivItem']").each(function () {
            divContent += $(this).html();
            $(this).remove();
        });
        $('#repaymentMethodList').after(divContent);
        this.validateTransactionPassword = validateTransactionPassword;
        var self = this;
        this.blockLoanRepaymentMethodProcessing = false;
       // this.SalesMethod = $("[name=SalesMethod]");
       // this.EstimatedSaleValue = $("#EstimatedSaleValue");
        this.registerRepaymentMethodEvents(self);

        if ($('#repaymentMethodList').children('li').length == 1) {
            var loanRepaymentMethodRadio = $('#repaymentMethodList').find('input[type=radio]');
            if (!loanRepaymentMethodRadio.attr('checked')) {
                loanRepaymentMethodRadio.attr('checked', true);
                loanRepaymentMethodRadio.trigger('click');
            }
        }

    },

    registerRepaymentMethodEvents: function (self) {
        var _parentObj = IC.Public.Plans.RepaymentDetailsCash.prototype;
        this.LoanRepaymentMethods = $("[name=LoanRepaymentMethods]");
        this.LoanRepaymentMethods.unbind('click');
        this.LoanRepaymentMethods.bind("click", { obj: this }, this.onMethodSelect);

        var availableFor = ".AvailableFor_" + this.LoanRepaymentMethods.val();
        $(availableFor).show();

        this.methodDetailItems = $('.method-detail-items').find('input[type=radio]');
        this.methodDetailItems.unbind('click');
        this.methodDetailItems.bind('click', { obj: this }, this.onMethodDetailsClick);

        this.transferToSrnTextBox = $("[name=UserSRN]");
        this.transferToSrnTextBox.unbind('keyup');
        this.transferToSrnTextBox.bind('keyup', function () {
            _parentObj.validateData();
        });

        this.updateBankAccountLink = $('.update-bank-account');
        this.updateBankAccountLink.unbind('click');
        this.updateBankAccountLink.bind("click", { obj: this }, this.onUpdateBankAccountLinkClick);

        this.updateIbwAccountLink = $('.ibw-update-account');
        this.updateIbwAccountLink.unbind('click');
        this.updateIbwAccountLink.bind("click", { obj: this }, this.onUpdateIbwAccountLinkClick);

        this.updateAddressLink = $('.update-address');
        this.updateAddressLink.unbind('click');
        this.updateAddressLink.bind("click", { obj: this }, this.onUpdateAddressLinkClick);

        this.repaymentMessageLink = $('.repaymentMessageLink');
        this.repaymentMessageLink.unbind('click');
        this.repaymentMessageLink.bind('click', this.onRepaymentMessageLinkClick);
    },

    onMethodSelect: function (events) {
        var _parentObj = IC.Public.Plans.RepaymentDetailsCash.prototype;
        var _obj = events.data.obj;
        var optionId = $(this).val();
        var containers = $('.exercise-method-container');
        containers.hide();
        $('.method-details').hide();
        containers.find("input:radio").removeAttr("checked");

        var id = "#LoanRepaymentMethods_" + optionId + "_container";
        $(id).show();

        //$('.sellTotals').hide();
        //var availableFor = ".AvailableFor_" + optionId;
        //$(availableFor).show();

        $("#EstimatedSaleValue").text($.formatNumber(0, "#,0.00"));
        _parentObj.validateData();
        _parentObj.displayTermsAndConditions();
    },

    onUpdateBankAccountLinkClick: function (events) {
        var self = events.data.obj;
        //var proceedSeqNumber = parseInt($(this).attr('proceedSeqNumber'));
        var proceedSeqNumber = $(this).attr('proceedSeqNumber');
        IC.Public.showLoading("");

        var paymentMethodCode = $(this).attr('paymentMethodTypeCode');
        var paymentMethodDesc = $(this).attr('paymentMethodTypeDesc');
        var modal = new IC.Public.Plans.BankAccountDetails();
        modal.showModal(proceedSeqNumber, IC.Public.Plans.Common.MenuSubItemTypes.LoanRepayment, self.validateTransactionPassword, paymentMethodCode, paymentMethodDesc);
    },

    onUpdateIbwAccountLinkClick: function (events) {
        //var proceedSeqNumber = parseInt($(this).attr('proceedSeqNumber'));
        var proceedSeqNumber = $(this).attr('proceedSeqNumber');
        var modal = new IC.Public.Plans.InternationalBankWire();
        IC.Public.showLoading("");
        modal.showModal(proceedSeqNumber, IC.Public.Plans.Common.MenuSubItemTypes.LoanRepayment);
    },

    onUpdateAddressLinkClick: function () {
        var url = OC.MVC.util.getLink("Address", "Edit");
        OC.MVC.util.loadMainContainerView(url);
    },

    onMethodDetailsClick: function (events) {
        var _parentObj = IC.Public.Plans.RepaymentDetailsCash.prototype;
        var _obj = events.data.obj;
        $(this).parents('ul.method-detail-items').find('.method-details').hide();
        var methodDetails = $(this).parents('ul.method-detail-items li').find('.method-details');
        if (methodDetails.length == 0)
            methodDetails = $(this).parents('ul.method-detail-items li').find('.view-details');

        if ($(this).val() == 'FDC' || $(this).val() == 'IBW') {
            $("div .callout").show();
        } else {
            $("div .callout").hide();
        }

        methodDetails.show();
         _obj.updateEstimatedValues(methodDetails, $(this).val());
         _parentObj.validateData();
         _parentObj.displayTermsAndConditions();
    },

    updateEstimatedValues: function (element, methodCode) {
        var _parentObj = IC.Public.Plans.RepaymentDetailsCash.prototype;
        if (!element) {
            var salesMethod = $('input[name=SalesMethod]:checked');
            element = salesMethod.parent().parent().find('.method-details');
            methodCode = salesMethod.val();
        }
        var EstimatedSaleValue = $("#EstimatedSaleValue");
        if (methodCode == IC.Public.Plans.SaleMethods.SellByLimit || methodCode == IC.Public.Plans.SaleMethods.GoodTillCancel ||
            methodCode == IC.Public.Plans.SaleMethods.SellByMarketOrder) {
            var sellPrice = _parentObj.getSellPrice(element);
            var estimateSaleValue = (parseInt($("#TotalRepaymentAmount").text().replaceAll(",", "")) * sellPrice).toFixed(2);
            EstimatedSaleValue.text($.formatNumber(estimateSaleValue, "#,0.00"));
        }

    },

    onRepaymentMessageLinkClick: function () {
        var _parentObj = IC.Public.Plans.RepaymentDetailsCash.prototype;
       // var planCodes = _parentObj.getPreClearanceBlackoutPlans();
        var type = $(this).attr("type");
        var code = _parentObj.getPreClearanceBlackoutPlans();// $(this).attr("code");
        var viewKey = $("#ViewKey").val();
        var transactionType = $(this).attr("transactionType");
        var windowId = $(this).attr("windowId");
        var modal = new IC.Public.Plans.LoanRepaymentMessage();
        //need to check this functionality
        modal.showModal(type, code, viewKey, transactionType, windowId, IC.Public.Plans.Common.MenuSubItemTypes.LoanRepayment);
    }
};IC.Public.Plans.RepaymentDetailsPartialShares = function (validateTransactionPassword) {
    this.init(validateTransactionPassword);
};

IC.Public.Plans.RepaymentDetailsPartialShares.prototype = {
    init: function (validateTransactionPassword) {
        this.viewKey = $("#ViewKey");
        var self = this;

        this.validateTransactionPassword = validateTransactionPassword;
        this.blockLoanRepaymentMethodProcessing = false;

        this.registerRepaymentMethodEvents(self);
        this.registerEvents(self);

        this.TermsAndConditionsAccepted = $("#TermsAndConditionsAccepted");
        this.TermsAndConditionsAccepted.change(function () {
            self.validateData();
        });
        this.salesTermsAndConditions = $('.salesTermsAndConditions');
        this.transferTermsAndConditions = $('.transferTermsAndConditions');
        this.generalTermsAndConditions = $('.generalTermsAndConditions');
        this.displayTermsAndConditions();
        this.errorContainer = $('.errorContainer');

        if (this.errorContainer.html() == '')
            this.errorContainer.hide();

        if ($('#repaymentMethodList').children('li').length == 1) {
            var loanRepaymentMethodRadio = $('#repaymentMethodList').find('input[type=radio]');
            if (!loanRepaymentMethodRadio.attr('checked')) {
                loanRepaymentMethodRadio.attr('checked', true);
                loanRepaymentMethodRadio.trigger('click');
            }
        }
        $('.toolTipMessage').each(function () {
            $('.helpLink-tooltip').cluetip({ showTitle: false, local: "true" });
        });
        $("a.cluetooltip").cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 350, cluetipClass: "exercise" });
        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.LoanRepaymentDetailsCash);
    },
    getPreClearanceBlackoutPlans: function () {

        var preclearance = $("#preClearancetxt").val();
        var blackOut = $("#blackOuttxt").val();
        var planCodes = '';
        $('#PartialSharesSellingRepaymentDetailsGrid > tbody  > tr').each(function () {

            var txtPlan = ($(this).getColumnValue('NumberToSell').replaceAll(",", ""));
            if (preclearance == txtPlan || blackOut == txtPlan) {
                var code = ($(this).getColumnValue('PlanName'));
                if (planCodes == '')
                { planCodes = code; }
                else { planCodes = planCodes + ',' + code; }
            }

        });
        return planCodes;
    },
    registerRepaymentMethodEvents: function (self) {
        this.LoanRepaymentMethods = $("[name=LoanRepaymentMethods]");
        this.LoanRepaymentMethods.unbind('click');
        this.LoanRepaymentMethods.bind("click", { obj: this }, this.onMethodSelect);

        var availableFor = ".AvailableFor_" + this.LoanRepaymentMethods.val();
        $(availableFor).show();

        this.methodDetailItems = $('.method-detail-items').find('input[type=radio]');
        this.methodDetailItems.unbind('click');
        this.methodDetailItems.bind('click', { obj: this }, this.onMethodDetailsClick);

        this.transferToSrnTextBox = $("[name=UserSRN]");
        this.transferToSrnTextBox.unbind('keyup');
        this.transferToSrnTextBox.bind('keyup', function () {
            self.validateData();
        });

        this.SellByLimitPrice = $("[name=SellByLimitPrice]");
        this.SellByLimitPrice.numeric({ format: "#0.00" });
        this.SellByLimitPrice.unbind('keyup');
        this.SellByLimitPrice.bind('keyup', function () {
            self.updateEstimatedValues();
            self.validateData();
        });

        this.GoodTillCancelPrice = $("[name=GoodTillCancelPrice]");
        this.GoodTillCancelPrice.numeric({ format: "#0.00" });
        this.GoodTillCancelPrice.unbind('keyup');
        this.GoodTillCancelPrice.bind('keyup', function () {
            self.updateEstimatedValues();
            self.validateData();
        });


        this.updateBankAccountLink = $('.update-bank-account');
        this.updateBankAccountLink.unbind('click');
        this.updateBankAccountLink.bind("click", { obj: this }, this.onUpdateBankAccountLinkClick);

        this.updateIbwAccountLink = $('.ibw-update-account');
        this.updateIbwAccountLink.unbind('click');
        this.updateIbwAccountLink.bind("click", { obj: this }, this.onUpdateIbwAccountLinkClick);

        this.updateAddressLink = $('.update-address');
        this.updateAddressLink.unbind('click');
        this.updateAddressLink.bind("click", { obj: this }, this.onUpdateAddressLinkClick);

        this.repaymentMessageLink = $('.repaymentMessageLink');
        this.repaymentMessageLink.unbind('click');
        this.repaymentMessageLink.bind('click', this.onRepaymentMessageLinkClick);
    },
    onRepaymentMessageLinkClick: function () {
        var _parentObj = IC.Public.Plans.RepaymentDetailsPartialShares.prototype;
        var type = $(this).attr("type");
        var code = _parentObj.getPreClearanceBlackoutPlans();// $(this).attr("code");
        var viewKey = $("#ViewKey").val();
        var transactionType = $(this).attr("transactionType");
        var windowId = $(this).attr("windowId");
        var modal = new IC.Public.Plans.LoanRepaymentMessage();

        modal.showModal(type, code, viewKey, transactionType, windowId, IC.Public.Plans.Common.MenuSubItemTypes.LoanRepayment);
    },
    onUpdateBankAccountLinkClick: function (events) {
        var self = events.data.obj;

        var proceedSeqNumber = $(this).attr('proceedSeqNumber');
        IC.Public.showLoading("");

        var paymentMethodCode = $(this).attr('paymentMethodTypeCode');
        var paymentMethodDesc = $(this).attr('paymentMethodTypeDesc');
        var modal = new IC.Public.Plans.BankAccountDetails();
        modal.showModal(proceedSeqNumber, IC.Public.Plans.Common.MenuSubItemTypes.LoanRepayment, self.validateTransactionPassword, paymentMethodCode, paymentMethodDesc);

    },

    onUpdateIbwAccountLinkClick: function (events) {

        var proceedSeqNumber = $(this).attr('proceedSeqNumber');
        var modal = new IC.Public.Plans.InternationalBankWire();
        IC.Public.showLoading("");
        modal.showModal(proceedSeqNumber, IC.Public.Plans.Common.MenuSubItemTypes.LoanRepayment);
    },

    onUpdateAddressLinkClick: function () {
        var url = OC.MVC.util.getLink("Address", "Edit");
        OC.MVC.util.loadMainContainerView(url);
    },
    onMethodDetailsClick: function (events) {
        var _obj = events.data.obj;
        $(this).parents('ul.method-detail-items').find('.method-details').hide();
        var methodDetails = $(this).parents('ul.method-detail-items li').find('.method-details');
        if (methodDetails.length == 0)
            methodDetails = $(this).parents('ul.method-detail-items li').find('.view-details');

        if ($(this).val() == 'FDC' || $(this).val() == 'IBW') {
            $("div .callout").show();
        } else {
            $("div .callout").hide();
        }

        methodDetails.show();

        _obj.validateData();
        _obj.displayTermsAndConditions();
    },
    registerEvents: function (self) {
        this.registerGridRelatedControlEvents();

        this.registerButtonEvents();

    },
    registerButtonEvents: function () {
        this.btnContinue = $('#btnContinue');
        this.btnContinue.unbind('click');
        this.btnContinue.bind("click", { obj: this }, this.onContinueButtonClick);

        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind('click');
        this.btnCancel.bind('click', function () {
            var url = OC.MVC.util.getLink("LoanRepayment", "Index");
            OC.MVC.util.loadMainContainerView(url);
        });
    },
    getselectedPlans: function () {
        var result = $('.numberToRepayLoan').map(function (index, item) {
            var planCode = $(item).attr('planCode');
            var amountoRepay = $(item).val();
            if (amountoRepay == '' || amountoRepay == '0') return null;
            return planCode;
        }).get();
        if (result.length != 0) {
            result = jQuery.unique(result);
        }
        return result.join(',');
    },
    onContinueButtonClick: function (events) {

        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        // var plans = _obj.grid.getPostDataItem('plans');
        var plans = _obj.grid.getPostDataItem('plans');
        var selectedGrants = _obj.getNumberToRepay();

        if (!selectedGrants) return;

        $('#hdnPlans').val(plans);
        $('#hdnSelectedGrants').val(selectedGrants);
        var form = $("form#SalesRepaymentForm");
        var data = form.serializeObject();


        var repaymentMethod = $('input[name=LoanRepaymentMethods]:checked').val();
        var methodDetailsContainer = $('#LoanRepaymentMethods_' + repaymentMethod + '_container');

        if (data.ShareDeliveryMethod == IC.Public.Plans.ShareDeliveryMethods.TransferToSRN) {
            var srnControl = methodDetailsContainer.find("[name='UserSRN']");
            var srnValue = srnControl.val();
            if (srnValue == "") srnValue = srnControl.text();
            var noDataAttributeValue = srnControl.attr('data-no-value');
            if (noDataAttributeValue == "true") srnValue = "";
            $('#hdnTransferToSrn').val(srnValue);
        }
        if (data.SalesMethod == IC.Public.Plans.SaleMethods.SellByLimit)
            $('#hdnLimitPrice').val(methodDetailsContainer.find('#SellByLimitPrice').val());
        else if (data.SalesMethod == IC.Public.Plans.SaleMethods.GoodTillCancel)
            $('#hdnLimitPrice').val(methodDetailsContainer.find('#GoodTillCancelPrice').val());

        data = form.serializeObject();
        _obj.validateLoanRepaymentShares(data);
    },
    validateLoanRepaymentShares: function (data) {

        var url = OC.MVC.util.getLink("LoanRepayment", "ValidateLoanRepayment");
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    registerGridRelatedControlEvents: function () {

        this.sellAllCheckBox = $("#IsSellAll");
        this.sellAllCheckBox.unbind('click');
        this.sellAllCheckBox.bind('click', { obj: this }, this.onSellAllClick);

        this.sellNumberCheckBox = $("#IsSellNumber");
        this.sellNumberCheckBox.unbind('click');
        this.sellNumberCheckBox.bind('click', { obj: this }, this.onSellNumberClick);

        this.sellNumberTextBox = $("#SellNumber");
        this.sellNumberTextBox.unbind('blur');
        this.sellNumberTextBox.bind('blur', { obj: this }, this.onSellNumberBlur);
        this.sellNumberTextBox.numeric();

        this.TotalRepaymentAmount = $("#TotalRepaymentAmount");
        this.TotalOutStatndingLoanAmount = $("#TotalOutStatndingLoanAmount");

        this.grid = $("#PartialSharesSellingRepaymentDetailsGrid");
        this.grid.unbind(OC.MVC.grid.events.loadComplete);
        this.grid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onLoadComplete);
    },
    onSellNumberClick: function (events) {
        var _obj = events.data.obj;
        var value = $(this).is(':checked');
        _obj.toggleSellAllInputs(_obj, !value);
        _obj.toggleSellNumberInputs(value);
        $("#SellNumber").focus();
    },
    toggleSellAllInputs: function (obj, value) {
        $("#IsSellAll").attr('checked', value);
        obj.toggleOptionsToTransactValues(value);
    },
    onSellNumberBlur: function (events) {
        var sellNumberText = $(this).val().replaceAll(",", "");
        if (sellNumberText == "")
            return;
        var sellNumber = parseInt(sellNumberText);
        if (isNaN(sellNumber))
            return;

        var _obj = events.data.obj;
        _obj.distributeSellNumber(sellNumber);

        if (!_obj.blockLoanRepaymentMethodProcessing)
            _obj.processRepaymentMethods();
    },

    distributeSellNumber: function (sellNumber) {
        var self = this;
        $.each($('.numberToRepayLoan'), function (i, item) {
            if (sellNumber <= 0)
                sellNumber = 0;//return;

            var tr = $(item).parents('tr');
            var availablesec = parseInt(tr.getColumnValue('Securities').replaceAll(",", ""));
            var numberToRepayTextBox = $(item);
            if (sellNumber <= availablesec) {
                numberToRepayTextBox.val(sellNumber);
            } else {
                numberToRepayTextBox.val(availablesec);
            }

            self.handleGridSellNumberChange(numberToRepayTextBox, false);
            sellNumber -= availablesec;
        });
    },

    onMethodSelect: function (events) {
        var _obj = events.data.obj;
        var optionId = $(this).val();
        var containers = $('.exercise-method-container');
        containers.hide();
        $('.method-details').hide();
        containers.find("input:radio").removeAttr("checked");
        var id = "#LoanRepaymentMethods_" + optionId + "_container";
        $(id).show();


        _obj.validateData();
        _obj.displayTermsAndConditions();
    },
    validateData: function () {
        if (!$('input[name=LoanRepaymentMethods]:checked').is(':visible')) {
            this.btnContinue.parent().addClass('disabled');
            return false;
        }
        var repaymentMethod = $('input[name=LoanRepaymentMethods]:checked').val();
        var isValid = repaymentMethod != null;
        //var selectedGrants = this.getNumberToRepay();
        isValid = isValid &&
            this.TermsAndConditionsAccepted.is(':checked') &&
            this.checkMethodDetails(repaymentMethod)
        && $("#HasUnregisteredHrns").val().toLowerCase() == "false";

        if (isValid)
            this.btnContinue.parent().removeClass('disabled');
        else
            this.btnContinue.parent().addClass('disabled');

        return isValid;
    },

    getNumberToRepay: function () {
        var result = $('.numberToRepayLoan').map(function (index, item) {
            var grantId = $(item).attr('grantId');
            var planCode = $(item).attr('planCode');
            var numberToRepay = $(item).val();
            if (numberToRepay == '' || numberToRepay == '0') return null;
            return grantId + "|" + numberToRepay + "|" + planCode;
        }).get();
        return result.join(',');
    },

    checkMethodDetails: function (repayMethod) {
        var isValid = false;
        var salesMethod, shareDelivery, paymentMethod;

        var shareDeliveryMethodValid = false;

        switch (repayMethod) {


            case IC.Public.Plans.RepaymentTypes.RepaymentSellShares:
                salesMethod = $('input[name=SalesMethod]:checked').val();
                paymentMethod = $('input[name=PaymentMethod]:checked').val();
                isValid = (salesMethod != null && paymentMethod != null && ($('input[name=PaymentMethod]').attr('data-payment-proceed-seq-number') != "-1LDC" || $('input[name=PaymentMethod]').attr('payment-proceed-seq-number') != undefined) && this.checkSalesMethodDetails(repayMethod, salesMethod));
                break;

            case IC.Public.Plans.RepaymentTypes.RepaymentSellToCover:
                salesMethod = $('input[name=SalesMethod]:checked').val();
                shareDelivery = $('input[name=ShareDeliveryMethod]:checked').val();

                if ($('p[id=shareDeliveryHeaderId]').css('display') == 'none') { shareDeliveryMethodValid = true; }
                else { shareDeliveryMethodValid = (shareDelivery != null); }

                isValid = (salesMethod != null && (shareDeliveryMethodValid) && this.checkSalesMethodDetails(repayMethod, salesMethod));
                break;
        }

        if ($('p[id=shareDeliveryHeaderId]').css('display') == 'none') { return isValid; };

        if (isValid && shareDelivery == IC.Public.Plans.ShareDeliveryMethods.TransferToSRN) {
            isValid = this.checkTransferToSrnShareDeliveryMethodDetails(repayMethod);
        }
        return isValid;
    },
    checkTransferToSrnShareDeliveryMethodDetails: function (repayMethod) {
        var isValid = true;
        var methodDetailsContainer = $('#LoanRepaymentMethods_' + repayMethod + '_container');
        var transferToSrnTextBox = methodDetailsContainer.find("#UserSRN[type='text']");
        if (transferToSrnTextBox != undefined && transferToSrnTextBox.val() != undefined) {
            isValid = (transferToSrnTextBox.val().trim().length > 0);
        }
        return isValid;
    },
    checkSalesMethodDetails: function (repayMethod, salesMethod) {
        var isValid = true;
        var methodDetailsContainer = $('#LoanRepaymentMethods_' + repayMethod + '_container');

        if (salesMethod == IC.Public.Plans.SaleMethods.SellByLimit) {
            var sellByLimitPrice = parseFloat(methodDetailsContainer.find('#SellByLimitPrice').val());
            isValid = (!isNaN(sellByLimitPrice) && sellByLimitPrice > 0);
        }
        else if (salesMethod == IC.Public.Plans.SaleMethods.GoodTillCancel) {
            var goodTillCancelPrice = parseFloat(methodDetailsContainer.find('#GoodTillCancelPrice').val());
            isValid = (!isNaN(goodTillCancelPrice) && goodTillCancelPrice > 0);
        }
        return isValid;
    },
    displayTermsAndConditions: function () {
        var SalesMethod = $("[name=SalesMethod]");
        var salesTermsAndConditions = $('.salesTermsAndConditions');
        var transferTermsAndConditions = $('.transferTermsAndConditions');

        if (SalesMethod.is(':checked')) {
            salesTermsAndConditions.show();
        } else {
            salesTermsAndConditions.hide();
        }

        var shareDeliveryMethod = $('input[name=ShareDeliveryMethod]:checked').val();
        if (shareDeliveryMethod == IC.Public.Plans.ShareDeliveryMethods.TransferToSRN) {
            transferTermsAndConditions.show();
        } else {
            transferTermsAndConditions.hide();
        }
    },
    onLoadComplete: function (events, gridObj, data) {
        var _obj = events.data.obj;

        var numberToRepay = $('.numberToRepayLoan');
        numberToRepay.numeric();
        numberToRepay.unbind('keyup');
        numberToRepay.bind('keyup', { obj: _obj }, _obj.onNumberChange);
        var warnings = $('div.underwater-warning');
        var warningTextContainer = $('div.underwater-warning-container');
        if (warnings.length > 0)
            warningTextContainer.show();
        else
            warningTextContainer.hide();
        numberToRepay.each(function () {
            var hasValue = ($(this).val() == "");
            _obj.blockLoanRepaymentMethodProcessing = hasValue;
            $(this).trigger('keyup');
            _obj.blockLoanRepaymentMethodProcessing = false;
        });

        $("A.grantDateTooltip").each(function () {
            $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 150 });
        });
        _obj.updateTotals();
        _obj.CheckForDisplayShareDeliveryMethods(_obj);
    },

    onNumberChange: function (events) {
        var self = events.data.obj;
        self.handleGridSellNumberChange($(this), true);
        if (!self.blockLoanRepaymentMethodProcessing)
            self.processRepaymentMethods();
    },
    processRepaymentMethods: function () {
        var totalSelected = this.TotalRepaymentAmount.text();
        if (totalSelected == "" || totalSelected == "0" || totalSelected == "0.00") {

            $('#loanRepaymentMethodLi_' + IC.Public.Plans.RepaymentTypes.RepaymentSellToCover).show();

            this.validateData();
            return;
        }

        var _totalRepaymentPrice = $('.total-price').map(function () {
            var _value = parseFloat($(this).text().replaceAll(',', ''));
            return isNaN(_value) ? 0 : _value;
        }).get();
        if (_totalRepaymentPrice.sum() == 0) {

            this.displayOrHideRepaymentMethod(IC.Public.Plans.RepaymentTypes.RepaymentSellToCover, false);

        } else {

            this.displayOrHideRepaymentMethod(IC.Public.Plans.RepaymentTypes.RepaymentSellToCover, true);

        }
        this.validateData();
    },


    displayOrHideRepaymentMethod: function (repaymentMethodCode, show) {
        var isMethodSeletced = $('#LoanRepaymentMethods' + repaymentMethodCode).is(':checked');
        if (show) {
            $('#loanRepaymentMethodLi_' + repaymentMethodCode).show();
            if (isMethodSeletced)
                $('#LoanRepaymentMethods' + repaymentMethodCode + '_container').show();
        } else {
            $('#loanRepaymentMethodLi_' + repaymentMethodCode).hide();
            $('#LoanRepaymentMethods' + repaymentMethodCode + '_container').hide();
        }
    },
    onSellAllClick: function (events) {
        var _obj = events.data.obj;
        var value = $(this).is(':checked');
        _obj.toggleOptionsToTransactValues(value);
        _obj.toggleSellNumberInputs(!value);
        if (!_obj.blockLoanRepaymentMethodProcessing)
            _obj.processRepaymentMethods();
    },
    toggleSellNumberInputs: function (value) {
        $("#IsSellNumber").attr('checked', value);
        $("#SellNumber").attr('disabled', !value);
        if (!value) {
            $("#SellNumber").val('');
        }
    },
    toggleOptionsToTransactValues: function (value) {
        var self = this;
        $.each($('.numberToRepayLoan'), function (i, item) {
            var tr = $(item).parents('tr');
            var availableSec = parseInt(tr.getColumnValue('Securities').replaceAll(",", ""));
            var numberToRepayTextBox = $(item);
            if (value == true) {
                numberToRepayTextBox.val(availableSec);
            } else {
                numberToRepayTextBox.val('');
            }

            self.handleGridSellNumberChange(numberToRepayTextBox, false);
        });
    },
    CheckForDisplayShareDeliveryMethods: function (self) {
        self.showHideShareDeliveryMethod(false);
        var displayShareDeliveryMethod = false;
        $.each($('.numberToRepayLoan'), function (i, item) {
            var tr = $(item).parents('tr');
            var availableSec = parseInt(tr.getColumnValue('Securities').replaceAll(",", ""));
            var loanAmount = parseFloat(tr.getColumnValue('OutStandingLoanAmount').replaceAll(",", ""));
            var loanAmountpershare = tr.getColumnValue('LoanAmountPerShare');
            var _loanAmountpershare = parseFloat(loanAmountpershare);
            if (!isNaN($(item).val()) && !isNaN(_loanAmountpershare) && displayShareDeliveryMethod == false) {
                var total = _loanAmountpershare * $(item).val();
                if (total >= loanAmount) {
                    displayShareDeliveryMethod = true;
                }
            }
        });

        if (displayShareDeliveryMethod) {
            self.showHideShareDeliveryMethod(true);
        }
        else { self.showHideShareDeliveryMethod(false); }
    },
    handleGridSellNumberChange: function (sellNumberControl, unCheckSellNumberChecboxes) {
        var self = this;
        var tr = sellNumberControl.parents('tr');
        var td = tr.getColumnCell('TotalRepaymentAmount');
        var value = sellNumberControl.val();
        var totalEl = td.find('.total');

        if (value) {
            var loanAmountpershare = tr.getColumnValue('LoanAmountPerShare');
            var _loanAmountpershare = parseFloat(loanAmountpershare);
            var _number = parseInt(sellNumberControl.val());

            if (!isNaN(_loanAmountpershare) && !isNaN(_number)) {
                var sellable = parseInt(tr.getColumnValue('Securities').replaceAll(",", ""));
                if (sellable < _number) {
                    alert('Number to Sell must be less or equal to sellable options');
                    sellNumberControl.val('');
                    totalEl.hide();
                    td.find('.total-price').text('');
                    self.processSellNumberCheckBoxes(unCheckSellNumberChecboxes);
                    self.updateTotals();
                    return;
                }

                var total = _loanAmountpershare * sellNumberControl.val();//need to check this calculation

                totalEl.show();
                td.find('.total-price').text($.formatNumber(total, "#,0.00"));

                self.processSellNumberCheckBoxes(unCheckSellNumberChecboxes);
            }
        } else {
            totalEl.hide();
            td.find('.total-price').text('');
        }
        self.updateTotals();
        self.CheckForDisplayShareDeliveryMethods(self);
    },
    showHideShareDeliveryMethod: function (val) {
        $.each($('input[name=ShareDeliveryMethod]'), function () {
            if (!val) {
                $('p[id=shareDeliveryHeaderId]').hide();
                $(this).closest('ul').hide();
                $(this).attr("checked", false);
            }
            else {
                $('p[id=shareDeliveryHeaderId]').show();
                $(this).closest('ul').show();
            }
        });
    },
    updateTotals: function () {
        var _valueOutstanding = 0;
        var _valueAmountToRepay = 0;

        $('#PartialSharesSellingRepaymentDetailsGrid > tbody  > tr').each(function () {

            var outStanding = 0;
            var numbetToSellTxt = $(this).find('.numberToRepayLoan');
            outStanding = parseFloat($(this).getColumnValue('OutStandingLoanAmount').replaceAll(",", ""));
            loanAmountpershare = parseFloat($(this).getColumnValue('LoanAmountPerShare'));
            var _value = parseFloat($(numbetToSellTxt).val()) * loanAmountpershare;
            _valueAmountToRepay += isNaN(parseFloat(_value)) ? 0 : parseFloat(_value);
            _valueOutstanding += isNaN(parseFloat(outStanding)) ? 0 : parseFloat(outStanding);
        });
        this.TotalRepaymentAmount.text($.formatNumber(_valueAmountToRepay, "#,0.00"));
        this.TotalOutStatndingLoanAmount.text($.formatNumber(_valueOutstanding, "#,0.00"));

        this.validateData();
    },


    processSellNumberCheckBoxes: function (uncheck) {
        if (uncheck) {
            $("#IsSellAll").attr('checked', false);
            $("#IsSellNumber").attr('checked', false);
            $("#SellNumber").val('');
        }
    },


};
IC.Public.Plans.RepaymentDetailsSales = function (validateTransactionPassword) {
    this.init(validateTransactionPassword);
};
IC.Public.Plans.RepaymentDetailsSales.prototype = {
    init: function (validateTransactionPassword) {
        this.viewKey = $("#ViewKey");
        this.validateTransactionPassword = validateTransactionPassword;
        this.blockLoanRepaymentMethodProcessing = false;
        var self = this;
        this.registerRepaymentMethodEvents(self);
        this.registerEvents(self);

        this.TermsAndConditionsAccepted = $("#TermsAndConditionsAccepted");
        this.TermsAndConditionsAccepted.change(function () {
            self.validateData();
        });
        this.salesTermsAndConditions = $('.salesTermsAndConditions');
        this.transferTermsAndConditions = $('.transferTermsAndConditions');
        this.generalTermsAndConditions = $('.generalTermsAndConditions');
        this.displayTermsAndConditions();
        this.errorContainer = $('.errorContainer');

        if (this.errorContainer.html() == '')
            this.errorContainer.hide();

        if ($('#repaymentMethodList').children('li').length == 1) {
            var loanRepaymentMethodRadio = $('#repaymentMethodList').find('input[type=radio]');
            if (!loanRepaymentMethodRadio.attr('checked')) {
                loanRepaymentMethodRadio.attr('checked', true);
                loanRepaymentMethodRadio.trigger('click');
            }
        }
        $('.toolTipMessage').each(function () {
            $('.helpLink-tooltip').cluetip({ showTitle: false, local: "true" });
        });
        $("a.cluetooltip").cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 350, cluetipClass: "exercise" });
        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.LoanRepaymentDetailsCash);
    },
    getPreClearanceBlackoutPlans: function () {

        var preclearance = $("#preClearancetxt").val();
        var blackOut = $("#blackOuttxt").val();
        var planCodes = '';
        $('#ShareSellingRepaymentDetailsGrid > tbody  > tr').each(function () {

            var txtPlan = ($(this).getColumnValue('RepayLoan').replaceAll(",", ""));
            if (preclearance == txtPlan || blackOut == txtPlan) {
                var code = ($(this).getColumnValue('PlanName'));
                if (planCodes == '')
                { planCodes = code; }
                else { planCodes = planCodes + ',' + code; }
            }

        });
        return planCodes;
    },
    registerRepaymentMethodEvents: function (self) {
        this.LoanRepaymentMethods = $("[name=LoanRepaymentMethods]");
        this.LoanRepaymentMethods.unbind('click');
        this.LoanRepaymentMethods.bind("click", { obj: this }, this.onMethodSelect);

        var availableFor = ".AvailableFor_" + this.LoanRepaymentMethods.val();
        $(availableFor).show();

        this.methodDetailItems = $('.method-detail-items').find('input[type=radio]');
        this.methodDetailItems.unbind('click');
        this.methodDetailItems.bind('click', { obj: this }, this.onMethodDetailsClick);

        this.transferToSrnTextBox = $("[name=UserSRN]");
        this.transferToSrnTextBox.unbind('keyup');
        this.transferToSrnTextBox.bind('keyup', function () {
            self.validateData();
        });

        this.SellByLimitPrice = $("[name=SellByLimitPrice]");
        this.SellByLimitPrice.numeric({ format: "#0.00" });
        this.SellByLimitPrice.unbind('keyup');
        this.SellByLimitPrice.bind('keyup', function () {
            self.updateEstimatedValues();
            self.validateData();
        });

        this.GoodTillCancelPrice = $("[name=GoodTillCancelPrice]");
        this.GoodTillCancelPrice.numeric({ format: "#0.00" });
        this.GoodTillCancelPrice.unbind('keyup');
        this.GoodTillCancelPrice.bind('keyup', function () {
            self.updateEstimatedValues();
            self.validateData();
        });


        this.updateBankAccountLink = $('.update-bank-account');
        this.updateBankAccountLink.unbind('click');
        this.updateBankAccountLink.bind("click", { obj: this }, this.onUpdateBankAccountLinkClick);

        this.updateIbwAccountLink = $('.ibw-update-account');
        this.updateIbwAccountLink.unbind('click');
        this.updateIbwAccountLink.bind("click", { obj: this }, this.onUpdateIbwAccountLinkClick);

        this.updateAddressLink = $('.update-address');
        this.updateAddressLink.unbind('click');
        this.updateAddressLink.bind("click", { obj: this }, this.onUpdateAddressLinkClick);

        this.repaymentMessageLink = $('.repaymentMessageLink');
        this.repaymentMessageLink.unbind('click');
        this.repaymentMessageLink.bind('click', this.onRepaymentMessageLinkClick);
    },
    onRepaymentMessageLinkClick: function () {
        var _parentObj = IC.Public.Plans.RepaymentDetailsSales.prototype;
        var type = $(this).attr("type");
        var code = _parentObj.getPreClearanceBlackoutPlans();// $(this).attr("code");
        var viewKey = $("#ViewKey").val();
        var transactionType = $(this).attr("transactionType");
        var windowId = $(this).attr("windowId");
        var modal = new IC.Public.Plans.LoanRepaymentMessage();
        modal.showModal(type, code, viewKey, transactionType, windowId, IC.Public.Plans.Common.MenuSubItemTypes.LoanRepayment);
    },
    onUpdateBankAccountLinkClick: function (events) {
        var self = events.data.obj;
        var proceedSeqNumber = $(this).attr('proceedSeqNumber');
        IC.Public.showLoading("");

        var paymentMethodCode = $(this).attr('paymentMethodTypeCode');
        var paymentMethodDesc = $(this).attr('paymentMethodTypeDesc');
        var modal = new IC.Public.Plans.BankAccountDetails();
        modal.showModal(proceedSeqNumber, IC.Public.Plans.Common.MenuSubItemTypes.LoanRepayment, self.validateTransactionPassword, paymentMethodCode, paymentMethodDesc);
    },

    onUpdateIbwAccountLinkClick: function (events) {
        var proceedSeqNumber = $(this).attr('proceedSeqNumber');
        var modal = new IC.Public.Plans.InternationalBankWire();
        IC.Public.showLoading("");
        modal.showModal(proceedSeqNumber, IC.Public.Plans.Common.MenuSubItemTypes.LoanRepayment);
    },

    onUpdateAddressLinkClick: function () {
        var url = OC.MVC.util.getLink("Address", "Edit");
        OC.MVC.util.loadMainContainerView(url);
    },
    onMethodDetailsClick: function (events) {
        var _obj = events.data.obj;
        $(this).parents('ul.method-detail-items').find('.method-details').hide();
        var methodDetails = $(this).parents('ul.method-detail-items li').find('.method-details');
        if (methodDetails.length == 0)
            methodDetails = $(this).parents('ul.method-detail-items li').find('.view-details');

        if ($(this).val() == 'FDC' || $(this).val() == 'IBW') {
            $("div .callout").show();
        } else {
            $("div .callout").hide();
        }

        methodDetails.show();
        _obj.validateData();
        _obj.displayTermsAndConditions();
    },
    registerEvents: function (self) {
        this.registerGridRelatedControlEvents();
        this.registerButtonEvents();
    },
    registerButtonEvents: function () {
        this.btnContinue = $('#btnContinue');
        this.btnContinue.unbind('click');
        this.btnContinue.bind("click", { obj: this }, this.onContinueButtonClick);

        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind('click');
        this.btnCancel.bind('click', function () {
            var url = OC.MVC.util.getLink("LoanRepayment", "Index");
            OC.MVC.util.loadMainContainerView(url);
        });
    },
    getselectedPlans: function () {
        var result = $('.selectedToRepay').map(function (index, item) {
            var planCode = $(item).attr('planCode');
            var amountoRepay = $(item).val();
            if (amountoRepay == '' || amountoRepay == '0') return null;
            return planCode;
        }).get();
        if (result.length != 0) {
            result = jQuery.unique(result);
        }
        return result.join(',');
    },
    onContinueButtonClick: function (events) {

        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        // var plans = _obj.grid.getPostDataItem('plans');
        var plans = _obj.getselectedPlans();

        var selectedGrants = _obj.getAmountToRepay();

        if (!selectedGrants) return;

        $('#hdnPlans').val(plans);
        $('#hdnSelectedGrants').val(selectedGrants);
        var form = $("form#SalesRepaymentForm");
        var data = form.serializeObject();


        var repaymentMethod = $('input[name=LoanRepaymentMethods]:checked').val();
        var methodDetailsContainer = $('#LoanRepaymentMethods_' + repaymentMethod + '_container');

        if (data.ShareDeliveryMethod == IC.Public.Plans.ShareDeliveryMethods.TransferToSRN) {
            var srnControl = methodDetailsContainer.find("[name='UserSRN']");
            var srnValue = srnControl.val();
            if (srnValue == "") srnValue = srnControl.text();
            var noDataAttributeValue = srnControl.attr('data-no-value');
            if (noDataAttributeValue == "true") srnValue = "";
            $('#hdnTransferToSrn').val(srnValue);
        }
        if (data.SalesMethod == IC.Public.Plans.SaleMethods.SellByLimit)
            $('#hdnLimitPrice').val(methodDetailsContainer.find('#SellByLimitPrice').val());
        else if (data.SalesMethod == IC.Public.Plans.SaleMethods.GoodTillCancel)
            $('#hdnLimitPrice').val(methodDetailsContainer.find('#GoodTillCancelPrice').val());

        data = form.serializeObject();
        _obj.validateLoanRepaymentShares(data);
    },
    validateLoanRepaymentShares: function (data) {
        var url = OC.MVC.util.getLink("LoanRepayment", "ValidateLoanRepayment");
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    registerGridRelatedControlEvents: function () {
        this.repayAllCheckBox = $("#IsRepayAll");
        this.repayAllCheckBox.unbind('click');
        this.repayAllCheckBox.bind('click', { obj: this }, this.onRepayAllClick);

        this.TotalRepaymentAmount = $("#TotalRepaymentAmount");
        this.TotalOutStatndingLoanAmount = $("#TotalOutStatndingLoanAmount");

        this.grid = $("#ShareSellingRepaymentDetailsGrid");
        this.grid.unbind(OC.MVC.grid.events.loadComplete);
        this.grid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onLoadComplete);
    },
    onMethodSelect: function (events) {
        var _obj = events.data.obj;
        var optionId = $(this).val();
        var containers = $('.exercise-method-container');
        containers.hide();
        $('.method-details').hide();
        containers.find("input:radio").removeAttr("checked");
        var id = "#LoanRepaymentMethods_" + optionId + "_container";
        $(id).show();
        _obj.validateData();
        _obj.displayTermsAndConditions();
    },
    validateData: function () {
        if (!$('input[name=LoanRepaymentMethods]:checked').is(':visible')) {
            this.btnContinue.parent().addClass('disabled');
            return false;
        }
        var repaymentMethod = $('input[name=LoanRepaymentMethods]:checked').val();
        var isValid = repaymentMethod != null;
       // var selectedGrants = this.getAmountToRepay();
        isValid = isValid &&
            this.TermsAndConditionsAccepted.is(':checked') &&
            this.checkMethodDetails(repaymentMethod)
            && $("#HasUnregisteredHrns").val().toLowerCase() == "false";

        if (isValid)
            this.btnContinue.parent().removeClass('disabled');
        else
            this.btnContinue.parent().addClass('disabled');

        return isValid;
    },


    getAmountToRepay: function () {
        var result = $('.selectedToRepay').map(function (index, item) {
            if ($(this).is(':checked')) {
                var grantId = $(item).attr('grantId');
                var planCode = $(item).attr('planCode');
                var outStanding = $(item).attr('outstanding');
                var exercisable = $(item).attr('exercisable');
                if (outStanding == '' || outStanding == '0') return null;
                return grantId + "|" + exercisable + "|" + planCode;
            }
        }).get();
        return result.join(',');
    },
    checkMethodDetails: function (repayMethod) {
        var isValid = false;
        var salesMethod, shareDelivery, paymentMethod;
        var shareDeliveryMethodValid = false;
        switch (repayMethod) {

            case IC.Public.Plans.RepaymentTypes.RepaymentSellShares:
                salesMethod = $('input[name=SalesMethod]:checked').val();
                paymentMethod = $('input[name=PaymentMethod]:checked').val();
                isValid = (salesMethod != null && paymentMethod != null && ($('input[name=PaymentMethod]').attr('data-payment-proceed-seq-number') != "-1LDC" || $('input[name=PaymentMethod]').attr('payment-proceed-seq-number') != undefined) && this.checkSalesMethodDetails(repayMethod, salesMethod));
                break;

            case IC.Public.Plans.RepaymentTypes.RepaymentSellToCover:
                salesMethod = $('input[name=SalesMethod]:checked').val();
                shareDelivery = $('input[name=ShareDeliveryMethod]:checked').val();

                if ($('p[id=shareDeliveryHeaderId]').css('display') == 'none') { shareDeliveryMethodValid = true; }
                else { shareDeliveryMethodValid = (shareDelivery != null); }

                isValid = (salesMethod != null && (shareDeliveryMethodValid) && this.checkSalesMethodDetails(repayMethod, salesMethod));
                break;
        }

        if ($('p[id=shareDeliveryHeaderId]').css('display') == 'none') { return isValid; };

        if (isValid && shareDelivery == IC.Public.Plans.ShareDeliveryMethods.TransferToSRN) {
            isValid = this.checkTransferToSrnShareDeliveryMethodDetails(repayMethod);
        }
        return isValid;
    },
    checkTransferToSrnShareDeliveryMethodDetails: function (repayMethod) {
        var isValid = true;
        var methodDetailsContainer = $('#LoanRepaymentMethods_' + repayMethod + '_container');
        var transferToSrnTextBox = methodDetailsContainer.find("#UserSRN[type='text']");
        if (transferToSrnTextBox != undefined && transferToSrnTextBox.val() != undefined) {
            isValid = (transferToSrnTextBox.val().trim().length > 0);
        }
        return isValid;
    },
    checkSalesMethodDetails: function (repayMethod, salesMethod) {
        var isValid = true;
        var methodDetailsContainer = $('#LoanRepaymentMethods_' + repayMethod + '_container');

        if (salesMethod == IC.Public.Plans.SaleMethods.SellByLimit) {
            var sellByLimitPrice = parseFloat(methodDetailsContainer.find('#SellByLimitPrice').val());
            isValid = (!isNaN(sellByLimitPrice) && sellByLimitPrice > 0);
        }
        else if (salesMethod == IC.Public.Plans.SaleMethods.GoodTillCancel) {
            var goodTillCancelPrice = parseFloat(methodDetailsContainer.find('#GoodTillCancelPrice').val());
            isValid = (!isNaN(goodTillCancelPrice) && goodTillCancelPrice > 0);
        }
        return isValid;
    },
    displayTermsAndConditions: function () {
        var SalesMethod = $("[name=SalesMethod]");
        var salesTermsAndConditions = $('.salesTermsAndConditions');
        var transferTermsAndConditions = $('.transferTermsAndConditions');

        if (SalesMethod.is(':checked')) {
            salesTermsAndConditions.show();
        } else {
            salesTermsAndConditions.hide();
        }

        var shareDeliveryMethod = $('input[name=ShareDeliveryMethod]:checked').val();
        if (shareDeliveryMethod == IC.Public.Plans.ShareDeliveryMethods.TransferToSRN) {
            transferTermsAndConditions.show();
        } else {
            transferTermsAndConditions.hide();
        }
    },

    onLoadComplete: function (events, gridObj, data) {
        var _obj = events.data.obj;
        var chkselectedgrant = $('.selectedToRepay');
        chkselectedgrant.unbind('change');
        chkselectedgrant.bind('change', { obj: _obj }, _obj.onSelectedGrantChange);
        var warnings = $('div.underwater-warning');
        var warningTextContainer = $('div.underwater-warning-container');
        if (warnings.length > 0)
            warningTextContainer.show();
        else
            warningTextContainer.hide();
        chkselectedgrant.each(function () {
            var hasValue = $(this).is(':checked');
            _obj.blockLoanRepaymentMethodProcessing = hasValue;
            $(this).trigger('checked');
            _obj.blockLoanRepaymentMethodProcessing = false;
        });
        _obj.updateTotals();
        _obj.CheckForDisplayShareDeliveryMethods(_obj);
        $("A.grantDateTooltip").each(function () {
            $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 150 });
        });
    },
    onSelectedGrantChange: function (events) {
        if (!$(this).is(':checked')) {
            $("#IsRepayAll").attr('checked', false);
        }
        var self = events.data.obj;
        self.handleGridRepaySelectionChange($(this), true);
        if (!self.blockLoanRepaymentMethodProcessing)
            self.processRepaymentMethods();
    },

    processRepaymentMethods: function () {
        var totalSelected = this.TotalRepaymentAmount.text();
        if (totalSelected == "" || totalSelected == "0" || totalSelected == "0.00") {

            $('#loanRepaymentMethodLi_' + IC.Public.Plans.RepaymentTypes.RepaymentSellToCover).show();

            this.validateData();
            return;
        }

        var _totalExercisePrice = $('.total-price').map(function () {
            var _value = parseFloat($(this).text().replaceAll(',', ''));
            return isNaN(_value) ? 0 : _value;
        }).get();
        if (_totalExercisePrice.sum() == 0) {
            this.displayOrHideRepaymentMethod(IC.Public.Plans.RepaymentTypes.RepaymentSellToCover, false);
        } else {
            this.displayOrHideRepaymentMethod(IC.Public.Plans.RepaymentTypes.RepaymentSellToCover, true);
        }
        this.validateData();
    },


    displayOrHideRepaymentMethod: function (repaymentMethodCode, show) {
        var isMethodSeletced = $('#LoanRepaymentMethods' + repaymentMethodCode).is(':checked');
        if (show) {
            $('#loanRepaymentMethodLi_' + repaymentMethodCode).show();
            if (isMethodSeletced)
                $('#LoanRepaymentMethods' + repaymentMethodCode + '_container').show();
        } else {
            $('#loanRepaymentMethodLi_' + repaymentMethodCode).hide();
            $('#LoanRepaymentMethods' + repaymentMethodCode + '_container').hide();
        }
    },
    onRepayAllClick: function (events) {
        var _obj = events.data.obj;
        var value = $(this).is(':checked');
        _obj.toggleOptionsToTransactValues(value);

        if (!_obj.blockLoanRepaymentMethodProcessing)
            _obj.processRepaymentMethods();
    },

    toggleOptionsToTransactValues: function (value) {
        var self = this;
        $.each($('.selectedToRepay'), function (i, item) {
            var tr = $(item).parents('tr');
            var outStandingLoanAmount = parseFloat(tr.getColumnValue('OutStandingLoanAmount').replaceAll(",", ""));
            var RepaycheckBox = $(item);
            if (RepaycheckBox.is(':checkbox')) {
                if (value == true) {
                    RepaycheckBox.attr('checked', true);
                } else {
                    RepaycheckBox.attr('checked', false);
                }
            }

            self.handleGridRepaySelectionChange(RepaycheckBox, false);
        });
    },
    handleGridRepaySelectionChange: function (RepaycheckBox, unCheckRepayAmountChecboxes) {
        var self = this;
        var tr = RepaycheckBox.parents('tr');
        var td = tr.getColumnCell('TotalRepaymentAmount');

        var totalEl = td.find('.total');

        if (RepaycheckBox.is(':checked')) {
            var loanBalancePrice = parseFloat(tr.getColumnValue('OutStandingLoanAmount').replaceAll(",", ""));
            var _loanBalancePrice = parseFloat(loanBalancePrice);


            if (!isNaN(_loanBalancePrice)) {

                var total = _loanBalancePrice;
                totalEl.show();
                td.find('.total-price').text($.formatNumber(total, "#,0.00"));

                self.processselectedRepayCheckBoxes(unCheckRepayAmountChecboxes);
            }
        } else {
            totalEl.hide();
            td.find('.total-price').text('');
        }
        self.updateTotals();
        self.CheckForDisplayShareDeliveryMethods(self);
    },
    updateTotals: function () {
        var _valueOutstanding = 0;
        var _valueAmountToRepay = 0;

        $('#ShareSellingRepaymentDetailsGrid > tbody  > tr').each(function () {

            var outStanding = 0;
            var RepaycheckBox = $(this).find('.selectedToRepay');
            outStanding = parseFloat($(this).getColumnValue('OutStandingLoanAmount').replaceAll(",", ""));
            if (RepaycheckBox.is(':checked')) {
                _valueAmountToRepay += isNaN(parseFloat(outStanding)) ? 0 : parseFloat(outStanding);
            }
            _valueOutstanding += isNaN(parseFloat(outStanding)) ? 0 : parseFloat(outStanding);
        });
        this.TotalRepaymentAmount.text($.formatNumber(_valueAmountToRepay, "#,0.00"));
        this.TotalOutStatndingLoanAmount.text($.formatNumber(_valueOutstanding, "#,0.00"));

        this.validateData();
    },
    showHideShareDeliveryMethod: function (val) {
        $.each($('input[name=ShareDeliveryMethod]'), function () {
            if (!val) {
                $('p[id=shareDeliveryHeaderId]').hide();
                $(this).closest('ul').hide();
                $(this).attr("checked", false);
            }
            else {
                $('p[id=shareDeliveryHeaderId]').show();
                $(this).closest('ul').show();
            }
        });
    },
    CheckForDisplayShareDeliveryMethods: function (self) {
        self.showHideShareDeliveryMethod(false);
        var displayShareDeliveryMethod = false;
        $.each($('.selectedToRepay'), function (i, item) {
            var tr = $(item).parents('tr');
            var availableSec = parseInt(tr.getColumnValue('Securities').replaceAll(",", ""));
            var loanAmount = parseFloat(tr.getColumnValue('OutStandingLoanAmount').replaceAll(",", ""));
            var loanAmountpershare = tr.getColumnValue('LoanAmountPerShare');
            var _loanAmountpershare = parseFloat(loanAmountpershare);
            var _availableSec = parseFloat(availableSec);
            if (!isNaN(_availableSec) && !isNaN(_loanAmountpershare) && displayShareDeliveryMethod == false) {
                var total = _loanAmountpershare * _availableSec;
                if (total >= loanAmount && ($(this).is(':checked'))) {
                    displayShareDeliveryMethod = true;
                }
            }
        });

        if (displayShareDeliveryMethod) {
            self.showHideShareDeliveryMethod(true);
        }
        else { self.showHideShareDeliveryMethod(false); }
    },
    processselectedRepayCheckBoxes: function (uncheck) {
        if (uncheck) {
            $("#IsRepayAll").attr('checked', false);
        }
    },


};IC.Public.Plans.LoanRepaymentDetailMessage = function () {

};

IC.Public.Plans.LoanRepaymentDetailMessage.prototype = {

    init: function (preClearanceButtonDisplayed, sellNumberShown,  confirmCheckBoxShown, amountShown,formType) {
        var self = this;

        this.preClearanceButtonShown = preClearanceButtonDisplayed;
        this.sellNumberShown = sellNumberShown;
       // this.reasonForTradeShown = reasonForTradeShown;
        //this.otherSecuritiesOwnedShown = otherSecuritiesOwnedShown;
        this.amountShown = amountShown;
        this.confirmCheckBoxShown = confirmCheckBoxShown;
        this.formType = formType;

        if (this.sellNumberShown) {
            $('#sellNumberDiv').show();
            $('#sellNumberTextBoxDiv').show();
        }
        if (this.amountShown) {
            $('#amountDiv').show();
            $('#amountTextBoxDiv').show();
        }
        //if (this.reasonForTradeShown) {
        //    $('#reasonForTradeDiv').show();
        //}
        //if (this.otherSecuritiesOwnedShown) {
        //    $('#otherSecuritiesOwnedDiv').show();
        //}

        if (this.confirmCheckBoxShown) {
            $('#IsConfirmRequestPreClearanceDiv').show();
        }

        var totSecurityShares = "";
        if (formType=="Cash") {
            totSecurityShares = $("#RepaymentMethodObjectModel_TotalRepaymentAmount").text();
        }
        
        totSecurityShares = isNaN(parseInt(totSecurityShares.replace(",", ""))) ? 0 : parseInt(totSecurityShares.replace(",", ""));

        ////Sellnumber textbox
        if (this.sellNumberShown) {
            this.SellNumberTextBox = $('#simplemodal-container').find('#SellNumber');
            if (this.SellNumberTextBox != null) {
                this.SellNumberTextBox.attr("value", totSecurityShares);

                this.SellNumberTextBox.unbind('blur');
                this.SellNumberTextBox.bind('blur', { obj: this }, this.onSellNumberBlur);

                this.SellNumberTextBox.numeric();

                this.SellNumberTextBox.unbind('keypress');

                this.SellNumberTextBox.bind('keypress', { obj: this }, this.onSellNumberKeyPress);
                this.SellNumberTextBox.unbind('keyup');
                this.SellNumberTextBox.bind("keyup", { obj: this }, this.onSellNumberKeyUp);
                this.SellNumberTextBox.width(80);
                //this.SellNumberTextBox.focus();
            }
        }

        ////Amount textbox
        if (this.amountShown) {
            this.AmountTextBox = $('#simplemodal-container').find('#Amount');
            if (this.AmountTextBox != null) {
                this.AmountTextBox.attr("value", totSecurityShares);

                this.AmountTextBox.unbind('blur');
                this.AmountTextBox.bind('blur', { obj: this }, this.onSellNumberBlur);

                this.AmountTextBox.numeric();

                this.AmountTextBox.unbind('keypress');

                this.AmountTextBox.bind('keypress', { obj: this }, this.onSellNumberKeyPress);
                this.AmountTextBox.unbind('keyup');
                this.AmountTextBox.bind("keyup", { obj: this }, this.onSellNumberKeyUp);
                this.AmountTextBox.width(80);
                //this.SellNumberTextBox.focus();
            }
        }

        //Reason for trade textbox
        //if (this.reasonForTradeShown) {
        //    this.defaultReasonForTrade = $("#hdReasonForTrade");
        //    this.ReasonForTrade = $("#ReasonTrade");
        //    if (this.ReasonForTrade != null) {
        //        this.ReasonForTrade.unbind('focus');
        //        this.ReasonForTrade.bind("focus ", { obj: this }, this.onReasonOfTradeFocus);
        //        this.ReasonForTrade.unbind('click');
        //        this.ReasonForTrade.bind("click", { obj: this }, this.onReasonOfTradeClick);
        //        this.ReasonForTrade.unbind('keyup');
        //        this.ReasonForTrade.bind("keyup", { obj: this }, this.onReasonOfTradeKeyUp);
        //        this.ReasonForTrade.unbind('blur');
        //        this.ReasonForTrade.bind("blur", { obj: this }, this.onReasonOfTradeBlur);
        //        this.ReasonForTrade.height(50);
        //    }
        //}
        //Other securities owned textbox
        //if (this.otherSecuritiesOwnedShown) {
        //    this.OtherSecuritiesOwnedMessage = $("#OthersecuritiesOwnedMessage");
        //    this.defaultOtherSecuritiesOwnedTextBoxMessage = $("#hdOtherSecuritiesOwned");
        //    this.OtherSecuritiesOwnedTextBox = $("#OtherSecuritiesOwned");
        //    if (this.OtherSecuritiesOwnedTextBox != null) {
        //        this.OtherSecuritiesOwnedTextBox.unbind('focus');
        //        this.OtherSecuritiesOwnedTextBox.bind("focus ", { obj: this }, this.onOtherSecuritiesOwnedTextBoxFocus);
        //        this.OtherSecuritiesOwnedTextBox.unbind('click');
        //        this.OtherSecuritiesOwnedTextBox.bind("click", { obj: this }, this.onOtherSecuritiesOwnedTextBoxClick);
        //        this.OtherSecuritiesOwnedTextBox.unbind('keyup');
        //        this.OtherSecuritiesOwnedTextBox.bind("keyup", { obj: this }, this.onOtherSecuritiesOwnedTextBoxKeyUp);
        //        this.OtherSecuritiesOwnedTextBox.unbind('blur');
        //        this.OtherSecuritiesOwnedTextBox.bind('blur', { obj: this }, this.onOtherSecuritiesOwnedTextBoxBlur);
        //        this.OtherSecuritiesOwnedTextBox.height(50);
        //    }
        //}

        // Checkbox confirmation
        if (this.confirmCheckBoxShown) {
            this.confirmRequestPreClearanceMessage = $("#ConfirmRequestPreClearanceMessage");
            this.confirmRequestPreClearanceCheckBox = $("#ConfirmRequestPreClearance");
            if (this.confirmRequestPreClearanceCheckBox != null) {
                this.confirmRequestPreClearanceCheckBox.unbind('click');
                this.confirmRequestPreClearanceCheckBox.bind('click', { obj: this }, this.onConfirmRequestPreClearanceCheckBoxClick);
                this.confirmRequestPreClearanceCheckBox.width(13);
            }
        }
        //PreClearance button
        this.btnRequestPreClearance = $("#btnRequestPreClearance");
        this.enablePreClearanceButton();
        // TODO Sathish - [29/04/2015] Since the button is always disabled when all the newly added controls are switched off in client configuration
        // TODO Sathish - [29/04/2015] we need to comment these lines, once the bug is fixed, we can uncomment this.
        //if (this.btnRequestPreClearance != null) {
        //    this.btnRequestPreClearance.parent().addClass("disabled");
        //    this.btnRequestPreClearance.attr("disabled", "disabled");
        //}
    },
    onSellNumberKeyPress: function (events) {
        var self = this;

        var count = $(this).val().length;
        if (count > 9) {
            return false;
        }

        if (events.which != 8 && events.which != 0 && (events.which < 48 || events.which > 57)) {
            return false;
        }

    },
    onConfirmRequestPreClearanceCheckBoxClick: function (events) {

        var _obj = events.data.obj;
        _obj.enablePreClearanceButton();
    },

    onSellNumberKeyUp: function (events) {

        var _obj = events.data.obj;
        _obj.enablePreClearanceButton();

    },
    onSellNumberBlur: function (events) {

        var _obj = events.data.obj;



        _obj.enablePreClearanceButton();
    },

    //onReasonOfTradeFocus: function (events) {

    //    var _obj = events.data.obj;
    //    var messageReasonForTrade = _obj.ReasonForTrade.val();
    //    var defaultReasonForTrade = _obj.defaultReasonForTrade.val();
    //    if (messageReasonForTrade == defaultReasonForTrade) {
    //        _obj.ReasonForTrade.val("");
    //    }
    //    _obj.enablePreClearanceButton();
    //},
    //onReasonOfTradeClick: function (events) {

    //    var _obj = events.data.obj;
    //    var messageReasonForTrade = _obj.ReasonForTrade.val();
    //    var defaultReasonForTrade = _obj.defaultReasonForTrade.val();

    //    if (messageReasonForTrade.indexOf(defaultReasonForTrade) > -1) {
    //        _obj.ReasonForTrade.val("");
    //    }


    //    if (messageReasonForTrade == defaultReasonForTrade) {
    //        _obj.ReasonForTrade.val("");

    //    }

    //},
    //onReasonOfTradeKeyUp: function (events) {

    //    var _obj = events.data.obj;
    //    var messageReasonForTrade = _obj.ReasonForTrade.val();
    //    var defaultReasonForTrade = _obj.defaultReasonForTrade.val();

    //    if (messageReasonForTrade.indexOf(defaultReasonForTrade) > -1) {
    //        _obj.ReasonForTrade.val("");
    //    }

    //    //if (messageReasonForTrade == "") {
    //    //    _obj.ReasonForTrade.val(defaultReasonForTrade);
    //    //}
    //    if (messageReasonForTrade == defaultReasonForTrade) {
    //        _obj.ReasonForTrade.val("");

    //    }
    //    _obj.enablePreClearanceButton();


    //},
    //onReasonOfTradeBlur: function (events) {

    //    var _obj = events.data.obj;
    //    var messageReasonForTrade = _obj.ReasonForTrade.val();
    //    var defaultReasonForTrade = _obj.defaultReasonForTrade.val();

    //    if (messageReasonForTrade.indexOf(defaultReasonForTrade) > -1) {
    //        _obj.ReasonForTrade.val("");
    //    }

    //    if (messageReasonForTrade == "") {
    //        _obj.ReasonForTrade.val(defaultReasonForTrade);
    //    }
    //    if (messageReasonForTrade == defaultReasonForTrade) {
    //        _obj.ReasonForTrade.val("");

    //    }
    //    _obj.enablePreClearanceButton();


    //},
    //onOtherSecuritiesOwnedTextBoxFocus: function (events) {

    //    var _obj = events.data.obj;
    //    var messageOtherSecuritiesOwnedTextBoxValue = _obj.OtherSecuritiesOwnedTextBox.val();
    //    var defaultOtherSecuritiesOwnedMessage = _obj.defaultOtherSecuritiesOwnedTextBoxMessage.val();
    //    if (messageOtherSecuritiesOwnedTextBoxValue == defaultOtherSecuritiesOwnedMessage) {
    //        _obj.OtherSecuritiesOwnedTextBox.val("");

    //    }
    //    _obj.enablePreClearanceButton();
    //},
    //onOtherSecuritiesOwnedTextBoxClick: function (events) {

    //    var _obj = events.data.obj;
    //    var messageOtherSecuritiesOwnedTextBoxValue = _obj.OtherSecuritiesOwnedTextBox.val();
    //    var defaultOtherSecuritiesOwnedTextBoxMessage = _obj.defaultOtherSecuritiesOwnedTextBoxMessage.val();

    //    if (messageOtherSecuritiesOwnedTextBoxValue.indexOf(defaultOtherSecuritiesOwnedTextBoxMessage) > -1) {
    //        _obj.OtherSecuritiesOwnedTextBox.val("");
    //    }

    //    if (messageOtherSecuritiesOwnedTextBoxValue == defaultOtherSecuritiesOwnedTextBoxMessage) {
    //        _obj.OtherSecuritiesOwnedTextBox.val("");
    //    }

    //},
    //onOtherSecuritiesOwnedTextBoxKeyUp: function (events) {

    //    var _obj = events.data.obj;
    //    var messageOtherSecuritiesOwnedTextBoxValue = _obj.OtherSecuritiesOwnedTextBox.val();
    //    var defaultOtherSecuritiesOwnedTextBoxMessage = _obj.defaultOtherSecuritiesOwnedTextBoxMessage.val();

    //    if (messageOtherSecuritiesOwnedTextBoxValue.indexOf(defaultOtherSecuritiesOwnedTextBoxMessage) > -1) {
    //        _obj.OtherSecuritiesOwnedTextBox.val("");
    //    }
    //    //if (messageOtherSecuritiesOwnedTextBoxValue == "") {
    //    //    _obj.OtherSecuritiesOwnedTextBox.val(defaultOtherSecuritiesOwnedTextBoxMessage);
    //    //}
    //    if (messageOtherSecuritiesOwnedTextBoxValue == defaultOtherSecuritiesOwnedTextBoxMessage) {
    //        _obj.OtherSecuritiesOwnedTextBox.val("");
    //    }

    //    _obj.enablePreClearanceButton();
    //},

    //onOtherSecuritiesOwnedTextBoxBlur: function (events) {

    //    var _obj = events.data.obj;
    //    var messageOtherSecuritiesOwnedTextBoxValue = _obj.OtherSecuritiesOwnedTextBox.val();
    //    var defaultOtherSecuritiesOwnedTextBoxMessage = _obj.defaultOtherSecuritiesOwnedTextBoxMessage.val();

    //    if (messageOtherSecuritiesOwnedTextBoxValue.indexOf(defaultOtherSecuritiesOwnedTextBoxMessage) > -1) {
    //        _obj.OtherSecuritiesOwnedTextBox.val("");
    //    }
    //    if (messageOtherSecuritiesOwnedTextBoxValue == "") {
    //        _obj.OtherSecuritiesOwnedTextBox.val(defaultOtherSecuritiesOwnedTextBoxMessage);
    //    }
    //    if (messageOtherSecuritiesOwnedTextBoxValue == defaultOtherSecuritiesOwnedTextBoxMessage) {
    //        _obj.OtherSecuritiesOwnedTextBox.val("");
    //    }

    //    _obj.enablePreClearanceButton();
    //},

    enablePreClearanceButton: function () {
        var self = this;

        //check if the button is visible
        if (self.preClearanceButtonShown) {
            // Enable the button
            this.btnRequestPreClearance.parent().removeClass("disabled");
            this.btnRequestPreClearance.removeAttr("disabled");
        }
        // Disable the button in the below cases

        //var data = self.SellNumberTextBox.val();

        //if (self.sellNumberShown) {
        //    for (var i = 0; i < data.toLocaleString().length; i++) {

        //        if (!(data[i] >= 0 || data[i] <= 9)) {
        //            this.btnRequestPreClearance.parent().addClass("disabled");
        //            this.btnRequestPreClearance.attr("disabled", "disabled");
        //            return false;
        //        }
        //    }
        //}


        //if (self.sellNumberShown && (self.SellNumberTextBox.val() == ""
        //        || self.SellNumberTextBox.val() <= 0)) {

        //    this.btnRequestPreClearance.parent().addClass("disabled");
        //    this.btnRequestPreClearance.attr("disabled", "disabled");

        //}

        if (self.confirmRequestPreClearanceCheckBox != null) {
            var confirmChecked = self.confirmRequestPreClearanceCheckBox.is(':checked');
            if (self.confirmCheckBoxShown && confirmChecked == false) {
                this.btnRequestPreClearance.parent().addClass("disabled");
                this.btnRequestPreClearance.attr("disabled", "disabled");
            }
        }

    }
};IC.Public.Plans.LoanRepaymentMessage = function () {

};
IC.Public.Plans.LoanRepaymentMessage.prototype = {
    init: function () {
        var self = this;
        self.registerEvents();
        self.adjustModalSizeAndPosition();
    },

    showModal: function (type, planCode, viewKey, transactionType, windowId, menuSubItemType) {
        var self = this;
        var url = (type == 'blackout') ? IC.Public.urls.Message.blackoutMessage : IC.Public.urls.Message.preClearanceMessage;

        self.type = type;
        self.planCode = planCode;
        self.viewKey = viewKey;
        self.transactionType = transactionType;
        self.windowId = windowId;

        var data = {
            planCode: planCode,
            viewKey: viewKey,
            transactionType: transactionType,
            windowId: windowId,
            menuSubItemType: menuSubItemType
        };
        OC.MVC.util.showModalByUrl(url, data,
               { maxHeight: 300, maxWidth: 500, hideCloseButton: false }, function () { self.init(); });
    },

    registerEvents: function () {

        this.requestPreClearance = $('#btnRequestPreClearance');
        this.requestPreClearance.unbind('click');
        this.requestPreClearance.bind('click', { obj: this }, this.onRequestPreClearanceClick);
    },

    onRequestPreClearanceClick: function (events) {

        var self = events.data.obj;
        IC.Public.showLoading("");
        $.ajax({
            url: OC.MVC.util.getLink("Message", "RequestPreClearanceLoanRepayment"),
            type: 'POST',
            data: { viewKey: self.viewKey, transactionType: self.transactionType, windowId: self.windowId, confirmRequestPreClearance: $("#ConfirmRequestPreClearance").is(':checked') },
            success: function (result) {
                if (result && result.IsSuccess) {
                    self.displaySuccessMessage();
                } else {
                    if (result.ErrorMessage)
                        self.displayErrorMessage(result.ErrorMessage);
                }
                IC.Public.hideLoading();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },

    displaySuccessMessage: function () {
        $('#messageDiv').hide();
        $('#requestPreclearanceSuccessMessageDiv').show();
      //  $('#sellNumberDiv').hide();
       // $('#sellNumberTextBoxDiv').hide();
       // $('#reasonForTradeDiv').hide();
        //$('#otherSecuritiesOwnedDiv').hide();
        $('#IsConfirmRequestPreClearanceDiv').hide();
        this.requestPreClearance.parent().hide();
    },
    displayErrorMessage: function (errorMessage) {
        $('#mainErrorDiv').html("<ol><li>" + errorMessage + "</li></ol>");
    },
    adjustModalSizeAndPosition: function () {
        $('#simplemodal-container').css('height', 'auto');
        $.modal.setPosition();
    }
};

;
IC.Public.Plans.ConfirmLoanRepaymentCash = function (validateTransactionPassword) {
    this.init(validateTransactionPassword);
};


IC.Public.Plans.ConfirmLoanRepaymentCash.prototype = {
    init: function (validateTransactionPassword) {
        this.validateTransactionPassword = validateTransactionPassword;
       this.confirmCashRepaymentForm = $('form#confirmLoanRepaymentCashForm');

        this.TotalRepaymentAmount = $("#RepaymentMethodObjectModel_TotalRepaymentAmount");
        this.TotalOutStatndingLoanAmount = $("#RepaymentMethodObjectModel_TotalOutStatndingLoanAmount");
        this.grid = $("#LoanRepaymentDetailsGridCash");
        this.grid.unbind(OC.MVC.grid.events.loadComplete);
        this.grid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onLoadComplete);

        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind('click');
        this.btnCancel.bind("click", { obj: this }, this.onCancelButtonClick);

        this.btnBackButton = $('#btnBack');
        this.btnBackButton.unbind('click');
        this.btnBackButton.bind("click", { obj: this }, this.onBackButtonClick);

        this.btnConfirmButton = $('#btnConfirm');
        this.btnConfirmButton.unbind('click');
        this.btnConfirmButton.bind("click", { obj: this }, this.onConfirmButtonClick);
       // btnConfirmButton.parent().addClass('disabled');
        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnConfirm').click();
                }
            }
        });

        this.shareDeliveryMethod = $('#ShareDeliveryMethod');
        this.transferTermsAndConditions = $('.transferTermsAndConditions');
        this.displayTermsAndConditions();

        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.LoanRepaymentCash);
    },

    onLoadComplete: function(events, gridObj, data) {
        var self = events.data.obj;
        self.processRestrictedGrantDetails();
        var sum = 0.0;
        $("#LoanRepaymentDetailsGridCash tr").each(function () {
            var isselected = parseFloat($(this).find('td[aria-describedby="LoanRepaymentDetailsGridCash_OutStandingLoanAmount"]').text().replaceAll(",", ""));
            sum = sum + isselected;
        });
        var loanAmount = "AUD" + " " + sum.toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
        $('#LoanAmount').text(loanAmount);
        $("A.cluetooltip").each(function() {
            $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 150 });
        });
    },

    onCancelButtonClick: function() {
        var url = OC.MVC.util.getLink("LoanRepayment", "LoanRepaymentCash");
        OC.MVC.util.loadMainContainerView(url, { viewKey: $('#ViewKey').val(), plans: $('#hdnPlans').val() });
    },

    onBackButtonClick: function(events) {
    	var self = events.data.obj;
    	var formData = self.confirmCashRepaymentForm.serializeObject();
        var modelData = {
            ViewKey: formData.ViewKey,
            SelectedPlanCodes: formData.hdnPlans,
            SelectedGrants: formData.hdnSelectedGrants,
            RepaymentMethod: formData.LoanRepaymentMethods,
            SalesMethod: formData.SalesMethod,
            LimitPrice: formData.hdnLimitPrice,
            PaymentMethod: formData.PaymentMethod,
            ShareDeliveryMethod: formData.ShareDeliveryMethod,
            Srn: formData.Srn,
            IsRepayAll: formData.IsRepayAll,
            IsRepayNumber: formData.IsRepayNumber,
            RepayNumber: formData.RepayNumber
        };

        OC.MVC.util.loadMainContainerView(IC.Public.urls.LoanRepayment.CashRepaymentDetailsWithValues, modelData);
    },

    onConfirmButtonClick: function(events) {
        if ($('#IsImpersonateUser').val().toLowerCase() == "true") {
            alert(IC.Public.Plans.Common.ImperonateUserNoAccessMessage);
            return;
        }
        var _obj = events.data.obj;
        if (!_obj.validateTransactionPassword) {
            _obj.handleCashRepaymentConfirmClick();
            return;
        }

        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        var isMultiMobileExist = $("#HrnsRequiringPinValidation_IsMultiMobileExist").val();
        if (isMultiMobileExist == "True") {
            self.gotoValidateSmsPin();
        } else {
            var formData = _obj.confirmCashRepaymentForm.serializeObject();
            IC.Public.security.validateTransactionPin(formData, function () { _obj.handleCashRepaymentConfirmClick(); }, function () { _obj.disableRepaymentButtons(); });
        }
        
    },
    gotoValidateSmsPin: function () {
        var url = OC.MVC.util.getLink("LoanRepayment", "RenderConfirmCashValidatePin");
        var form = $("form#confirmLoanRepaymentCashForm");
        var data = form.serialize();
        data += '&cacheKey=' + $("#CacheKey").val();
        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err);
                IC.Public.hideLoading();
            }
        });
    },
    handleCashRepaymentConfirmClick: function () {
        var form = $('form#confirmLoanRepaymentCashForm');
        var url = OC.MVC.util.getLink("LoanRepayment", "ConfirmLoanRepayment");
        var data = form.serializeObject();
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });

	},
	disableRepaymentButtons: function () {
    	this.btnConfirmButton.parent().addClass('disabled');
    },
   
    displayTermsAndConditions: function() {
        var shareDeliveryMethod = this.shareDeliveryMethod.val();
        if (shareDeliveryMethod == IC.Public.Plans.ShareDeliveryMethods.TransferToSRN) {
            this.transferTermsAndConditions.show();
        } else {
            this.transferTermsAndConditions.hide();
        }
    },

    processRestrictedGrantDetails: function() {
        var restrictedRepaymentOptionsTotal = 0;
        $('span.restricted').each(function() {
            var numberToRepay= parseInt($(this).text());
            numberToRepay = isNaN(numberToRepay) ? 0 : numberToRepay;
            restrictedRepaymentOptionsTotal += numberToRepay;
        });

        if (restrictedRepaymentOptionsTotal != 0) {
            $('#restrictedSharesToDeliverDiv').show();
            $('#restrictedSharesToDeliverSpan').text(restrictedRepaymentOptionsTotal);
            $('#restrictedShareDeliveryDiv').show();
        }
    }
};

;
IC.Public.Plans.ConfirmLoanRepaymentSell = function (validateTransactionPassword) {
    this.init(validateTransactionPassword);
};

IC.Public.Plans.ConfirmLoanRepaymentSell.prototype = {
    init: function (validateTransactionPassword) {
        this.validateTransactionPassword = validateTransactionPassword;
        this.confirmSellRepaymentForm = $('form#confirmSaleForm');

        this.TotalRepaymentAmount = $("#RepaymentMethodObjectModel_TotalRepaymentAmount");
        this.TotalOutStatndingLoanAmount = $("#RepaymentMethodObjectModel_TotalOutStatndingLoanAmount");
        this.grid = $("#ShareSellingRepaymentDetailsGrid");
        var formType = $("#FormTypeValue").val();
        if (formType == IC.Public.Plans.FormTypeValue.ConfirmSellSharesPartial)
        {
            this.grid = $("#PartialSharesSellingRepaymentDetailsGrid");
        }
        this.formType = $("#FormTypeValue").val();
        this.grid.unbind(OC.MVC.grid.events.loadComplete);
        this.grid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onLoadComplete);

        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind('click');
        this.btnCancel.bind("click", { obj: this }, this.onCancelButtonClick);

        this.btnBackButton = $('#btnBack');
        this.btnBackButton.unbind('click');
        this.btnBackButton.bind("click", { obj: this }, this.onBackButtonClick);

        this.btnConfirmButton = $('#btnConfirm');
        this.btnConfirmButton.unbind('click');
        this.btnConfirmButton.bind("click", { obj: this }, this.onConfirmButtonClick);

        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnConfirm').click();
                }
            }
        });
       this.shareDeliveryMethod = $('#ShareDeliveryMethod');
        this.transferTermsAndConditions = $('.transferTermsAndConditions');
        this.displayTermsAndConditions();

        IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.LoanRepaymentCash);
    },

    onLoadComplete: function (events, gridObj, data) {
        var self = events.data.obj;
        self.processRestrictedGrantDetails();
       
        $("A.cluetooltip").each(function () {
            $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 150 });
        });
    },

    onCancelButtonClick: function () {
        var url = OC.MVC.util.getLink("LoanRepayment", "LoanRepaymentSalesDetails");
        OC.MVC.util.loadMainContainerView(url, { viewKey: $('#ViewKey').val(), plans: $('#hdnPlans').val() });
    },

    onBackButtonClick: function (events) {
        var self = events.data.obj;
        var formData = self.confirmSellRepaymentForm.serializeObject();
        var modelData = {
            ViewKey: formData.ViewKey,
            SelectedPlanCodes: formData.hdnPlans,
            SelectedGrants: formData.hdnSelectedGrants,
            RepaymentMethod: formData.LoanRepaymentMethods,
            SalesMethod: formData.SalesMethod,
            LimitPrice: formData.hdnLimitPrice,
            PaymentMethod: formData.PaymentMethod,
            ShareDeliveryMethod: formData.ShareDeliveryMethod,
            Srn: formData.Srn,
            IsRepayAll: formData.IsRepayAll,
            IsRepayNumber: formData.IsRepayNumber,
            RepayNumber: formData.RepayNumber,
            SaleOrPartialSale: $("#FormTypeValue").val()
        };

        OC.MVC.util.loadMainContainerView(IC.Public.urls.LoanRepayment.SaleRepaymentDetailsWithValues, modelData);
    },

    onConfirmButtonClick: function (events) {
        if ($('#IsImpersonateUser').val().toLowerCase() == "true") {
            alert(IC.Public.Plans.Common.ImperonateUserNoAccessMessage);
            return;
        }

        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handleSellRepaymentConfirmClick();
            return;
        }

        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        var isMultiMobileExist = $("#HrnsRequiringPinValidation_IsMultiMobileExist").val();
        if (isMultiMobileExist == "True") {
            self.gotoValidateSmsPin();
        } else {
            var formData = self.confirmSellRepaymentForm.serializeObject();
            IC.Public.security.validateTransactionPin(formData, function () { self.handleSellRepaymentConfirmClick(); }, function () { $("#btnConfirm").parent().addClass('disabled'); });
        }

    },
    gotoValidateSmsPin: function () {
        var url = OC.MVC.util.getLink("LoanRepayment", "RenderConfirmValidatePin");
        var form = $("form#confirmSaleForm");
        var data = form.serialize();
        data += '&cacheKey=' + $("#CacheKey").val();
        IC.Public.showLoading("");
        $.ajax({
            url: url, type: 'POST', data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err);
                IC.Public.hideLoading();
            }
        });
    },
    handleSellRepaymentConfirmClick: function () {
        var form = $('form#confirmSaleForm');
        var url = OC.MVC.util.getLink("LoanRepayment", "ConfirmLoanRepayment");
        var data = form.serializeObject();
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });

    },

    //gotoValidateSmsPin: function () {
    //    var url = OC.MVC.util.getLink("UpdatePaymentInstructionsAus", "NextAusValidatePin");
    //    var form = $("form#UpdatePaymentInstructionForm");
    //    var data = form.serialize();
    //    data += '&cacheKey=' + $("#CacheKey").val();
    //    IC.Public.showLoading("");
    //    $.ajax({
    //        url: url, type: 'POST', data: data,
    //        success: function (result) {
    //            $("#" + OC.MVC.constants.mainContainer).html(result);
    //            IC.Public.hideLoading();
    //        },
    //        error: function (err) {
    //            $("#" + OC.MVC.constants.mainContainer).html(err);
    //            IC.Public.hideLoading();
    //        }
    //    });
    //},

    displayTermsAndConditions: function () {
        var shareDeliveryMethod = this.shareDeliveryMethod.val();
        if (shareDeliveryMethod == IC.Public.Plans.ShareDeliveryMethods.TransferToSRN) {
            this.transferTermsAndConditions.show();
        } else {
            this.transferTermsAndConditions.hide();
        }
    },

    processRestrictedGrantDetails: function () {
        var restrictedRepaymentOptionsTotal = 0;
        $('span.restricted').each(function () {
            var numberToRepay = parseInt($(this).text());
            numberToRepay = isNaN(numberToRepay) ? 0 : numberToRepay;
            restrictedRepaymentOptionsTotal += numberToRepay;
        });

        if (restrictedRepaymentOptionsTotal != 0) {
            $('#restrictedSharesToDeliverDiv').show();
            $('#restrictedSharesToDeliverSpan').text(restrictedRepaymentOptionsTotal);
            $('#restrictedShareDeliveryDiv').show();
        }
    }
};



;IC.Public.Plans.loanRepaymentReceipt = function (isSaleReceipt) {
    this.init(isSaleReceipt);
};
IC.Public.Plans.loanRepaymentReceipt.prototype = {
    init: function (isSaleReceipt) {
        this.isSaleReceipt = isSaleReceipt;
        if (!isSaleReceipt) {
            this.grid = $("#LoanRepaymentDetailsGridCash");
        }
        else {
            this.grid = $("#ShareSellingRepaymentDetailsGrid");
            var formType = $("#FormTypeValue").val();
            if (formType == IC.Public.Plans.FormTypeValue.ConfirmSellSharesPartial) {
                this.grid = $("#PartialSharesSellingRepaymentDetailsGrid");
            }
        }
        this.grid.unbind(OC.MVC.grid.events.loadComplete);
        this.grid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onLoadComplete);

        this.btnDoneButton = $('#btnDone');
        this.btnDoneButton.unbind('click');
        this.btnDoneButton.bind("click", { obj: this }, this.onDoneButtonClick);

        this.aDownloadReceiptLink = $("#aDownloadReceiptLink");
        this.aDownloadReceiptLink.unbind();
        this.aDownloadReceiptLink.bind("click", { obj: this }, this.OnDownloadReceiptLinkClicked);

        //  IC.Public.updateCampaignsPanel(IC.Public.Plans.Common.CampaignNames.Exercise);
    },

    onLoadComplete: function (events, gridObj, data) {
        //IC.Public.Exercise.processRestrictedGrantDetails(); need to check the functionality

        $("A.cluetooltip").each(function () {
            $(this).cluetip({ showTitle: false, local: true, hideLocal: true, multiple: true, width: 150 });
        });
    },

    onDoneButtonClick: function () {
        OC.MVC.util.loadMainContainerView(IC.Public.urls.LoanRepayment.historicalLoanTransactionsList, { viewKey: $('#ViewKey').val() });
    },

    OnDownloadReceiptLinkClicked: function (events) {
        var self = events.data.obj;
        var url = OC.MVC.util.getLink("LoanRepayment", "GenerateLoanRepaymentSaleReceipt");
        if (!self.isSaleReceipt) {
            url = OC.MVC.util.getLink("LoanRepayment", "GenerateLoanRepaymentCashReceipt");
        }
        window.location = url + "?viewKey=" + $("#ViewKey").val() + "&repaymentId=" + $("#RepaymentId").val();
    }
};IC.Public.Plans.LoanTransactionHistory = function (options) {
    if (options) {
    }
}

IC.Public.Plans.LoanTransactionHistory.prototype = {
    onGoClick: function (eventArgs) {
        var self = eventArgs.data.obj;
        var data = self.getFilterData();
        if (data.IsValid) {
            self.LoanHistoryGrid.appendPostData(data);
            self.LoanHistoryGrid.appendPostData({ TransactionType: $("#TransactionType").val() });
            self.LoanHistoryGrid.trigger('reloadGrid', [{ page: 1 }]);
        }
        return false;
        //var reverseDateError = 'From date should be before to date.';
        //IC.Public.removeErrorFor(_obj.dateFrom, _obj.form, reverseDateError);
        //if (_obj.dateFrom.hasClass('error')) {
        //    return;
        //}
        //if (_obj.dateTo.hasClass('error')) {
        //    return;
        //}

        //var dates = { dateFrom: _obj.dateFrom.val(), dateTo: _obj.dateTo.val() };
        //var dateFromValue = $.datepicker.parseDate("dd/mm/yy", dates.dateFrom);
        //var dateToValue = $.datepicker.parseDate("dd/mm/yy", dates.dateTo);

        //if (dateFromValue > dateToValue) {
        //    IC.Public.displayErrorFor(_obj.dateFrom, _obj.form, reverseDateError);
        //    return;
        //}


        //_obj.LoanHistoryGrid.appendPostData(dates);
        //IC.Public.registerGA(window.location.hash, _obj.LoanHistoryGrid.getPostData());
        //_obj.LoanHistoryGrid.trigger('reloadGrid', [{ page: 1 }]);
    },

    onDateBlur: function (events) {
        $(this).dateValidate({ mask: "dd/mm/yy", name: events.data.name, subYears: events.data.subYears });
    },
    getFilterData: function () {
        var self = this;
        //var form = $('form#vestingCalculatorForm');

        var result = { IsValid: false };

        //if (self.dateFrom.val().length) {
        //    self.dateFrom.dateValidate({ mask: "dd/mm/yy", name: "From", allowFuture: true, allowPast: false });
        //    if (self.dateFrom.hasClass('error')) {
        //        return result;
        //    }
        //}

        //if (self.dateTo.val().length) {
        //    self.dateTo.dateValidate({ mask: "dd/mm/yy", name: "To", allowFuture: true, allowPast: false });
        //    if (self.dateTo.hasClass('error')) {
        //        return result;
        //    }
        //}

        if (self.dateTo.val().length && self.dateFrom.val().length) {
            var dateTo = $.datepicker.parseDate("dd/mm/yy", self.dateTo.val());
            var dateFrom = $.datepicker.parseDate("dd/mm/yy", self.dateFrom.val());
            var dateError = '"To" Date is not allowed to be earlier than "From" Date.';
            IC.Public.removeErrorFor(self.dateTo, self.form, dateError);

            if (dateTo < dateFrom) {
                IC.Public.displayErrorFor(self.dateTo, self.form, dateError);
                return result;
            }
        }

        result.DateFrom = this.dateFrom.val();
        result.DateTo = this.dateTo.val();
        result.IsValid = true;
        result.PlanViewKey = $("#PlanViewKey").val();
        return result;
    },
    init: function () {
        highlightMenu('payment');
        $('#downloadLink').attr('href', 'javascript:onDownloadClick("CSV");');
        if ($("#portfolioMenuLink").attr("type") != undefined && $("#portfolioMenuLink").attr("type").toLowerCase() == "authorisedportfolio") {
            //$("#mainMenu").css("visibility", "hidden");
            $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
        } else {
            $("#mainMenu").css("visibility", "visible");
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");
        }
        this.LoanHistoryGrid = $("#LoanHistoryGrid");
        //this.holdingFilter = $("form.holdingFilterForm");
        //this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onGoClick);
        this.btnGo = $("#btnGo");
        this.btnGo.button();
        this.btnGo.bind("click", { obj: this }, this.onGoClick);
        this.dateFrom = $("[name=DateFrom]");
        this.dateTo = $("[name=DateTo]");
        this.dateFrom.datepicker('option', { maxDate: new Date() });
        this.dateTo.datepicker('option', { maxDate: new Date() });

        this.LoanHistoryGrid.bind("grid_gridComplete", { obj: this }, function (events) {
            var _obj = events.data.obj;

        });
    }
};

;IC.Public.investorCertificationSummary = function () {
}
IC.Public.ForeignTaxResidency =
{
    FATCATooltip: function (cellValue, options, rowObject) {
        var div = $('<div>');
        var span = $('<span>');
        var cellValueToolTip = cellValue.replace("&lt;", "<")
        cellValueToolTip = cellValueToolTip.replace("&gt;", ">")
        span.attr('title', cellValueToolTip);
        span.addClass("tooltip");
        span.html(cellValue);
        span.appendTo(div);
        return div.html();
    },
}
IC.Public.investorCertificationSummary.prototype = {
    init: function () {

        var isCampaign = $("#IsCampaignEnabled");
        if (isCampaign != undefined && isCampaign.length != 0 && isCampaign.val().toLowerCase() == "true") {
            IC.Public.HideMenu();
        }

        this.form = $('#certificationSummaryForm');
        this.holdingFilter = $("form.holdingFilterForm");
        if (this.holdingFilter.length) {
            this.holdingFilter.unbind("applyHoldingFilter");
            this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterClick);
        }
        this.summaryInvestorList = $("select[name='InvestorSummaryDetails.RegisteredInvestor']");
        this.summaryInvestorList.unbind("change");
        this.summaryInvestorList.bind("change", { obj: this }, this.onChangeInvestor);

        this.summaryInvestorList = $("select[name='InvestorSummaryDetails.Capacity']");
        this.summaryInvestorList.unbind("change");
        this.summaryInvestorList.bind("change", { obj: this }, this.onChangeCapacity);
        this.IsOtherControlsValid = false;

        this.benefitList = $("select.benefit");
        this.benefitList.unbind("change");
        this.benefitList.bind("change", { obj: this }, this.onChangeBenefit);

        $('.toolTipMessage').each(function () {
            $('.helpLink-tooltip').cluetip({ showTitle: false, local: "true" });
        });

        this.viewKey = $("#ViewKey");
        this.btnSkip = $("#btnSkip");
        this.btnSkip.unbind("click");
        this.btnSkip.bind('click', { obj: this }, this.onSkipClick);

        this.btnNext = $("#btnNext");
        this.btnNext.unbind("click");
        this.btnNext.bind('click', { obj: this }, this.onNextClick);

        $(".conflict-fatca").on("click", { obj: this }, this.conflictLinkClick);

        //this.conflictLink = $(".conflict-fatca");
        //this.conflictLink.unbind(this.conflictLinkClick);
        //this.conflictLink.bind("click", { obj: this }, this.conflictLinkClick);

        $("#more_text").hide();
        this.btnMore = $("#read_more");
        this.btnMore.unbind("click");
        this.btnMore.bind('click', { obj: this }, this.onMoreClick);

        this.RadioActionTaxPurpose = $("[name='InvestorSummaryDetails.ActionTaxPurpose']");
        this.RadioActionTaxPurpose.unbind("change");
        this.RadioActionTaxPurpose.bind("change", { obj: this }, this.onActionTaxPurpose);

        if ($("[name='InvestorSummaryDetails.ActionDeceased']") && $("[name='InvestorSummaryDetails.ActionDeceased']").length > 0) {
            this.RadioActionActionDeceased = $("[name='InvestorSummaryDetails.ActionDeceased']");
            this.RadioActionActionDeceased.unbind("change");
            this.RadioActionActionDeceased.bind("change", { obj: this }, this.onActionDeceasedChange);
        }

        if ($("select[name='InvestorSummaryDetails.RegisteredInvestor']") != undefined) {
            var registeredInv = ($("select[name='InvestorSummaryDetails.RegisteredInvestor']").val());
            if (registeredInv != undefined) {
                $("#previousSeqValue").text(registeredInv.split('#')[0]);
                $("#previousDataAvail").val(registeredInv.split('#')[3]);
            }
        }

        this.grid = $("#InvestorCertificationGrid");
        this.grid.unbind(OC.MVC.grid.events.loadComplete);
        this.grid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onLoadComplete);

        this.IntialEvents = $("#btnIntial");
        this.IntialEvents.unbind('click');
        this.IntialEvents.bind("click", { obj: this }, this.validateNextButtonAndControlSelection);
        $('#btnIntial').trigger('click');
    },

    conflictLinkClick: function (obj) {
        var seqNumberinGrid = $(this).parent().parent().find("td:first-child").text()
        var selectedHoldingSequenceNumber = parseFloat(seqNumberinGrid == "" ? 0 : seqNumberinGrid);
        var form = $("#certificationSummaryForm");
        var data = { sequenceNumber: selectedHoldingSequenceNumber };//form.serializeObject(); //1##   562 JDOONDP PCVDDC OCZ OCL#False
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "FATCAConflict");
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    onLoadComplete: function (events) {
        var _obj = events.data.obj;
        var registeredInv = ($("select[name='InvestorSummaryDetails.RegisteredInvestor']").val());
        var selectedDesignatedAccount = $("#InvestorSummaryDetails_RegisteredInvestor").find('option:selected').text().trim();

        $(".conflict-fatca").on("click", { obj: this }, IC.Public.investorCertificationSummary.prototype.conflictLinkClick);

        if (registeredInv != undefined) {

            if (registeredInv != undefined) {
                $("#previousSeqValue").val(registeredInv.split('#')[0]);
                $("#previousDataAvail").val(registeredInv.split('#')[3]);
            }
            if (registeredInv.split('#').length > 0) {
                var seqNumber = registeredInv.split('#')[0];
                _obj.styleChangesOnGrid(seqNumber);
            }
        }
        $("#InvestorCertificationGrid tr").each(function () {
            var isselected = $(this).find('td[aria-describedby="InvestorCertificationGrid_Last Name or Entity Name"]').text();

            if (isselected == selectedDesignatedAccount) {
                $(this).find('td[aria-describedby="InvestorCertificationGrid_Last Name or Entity Name"]').find('span').css("font-weight", "bold");
                $(this).find('td[aria-describedby="InvestorCertificationGrid_Self-Certified"]').css("font-weight", "bolder");
            }
        });
    },
    styleChangesOnGrid: function (seqNumber) {
        $('#InvestorCertificationGrid > tbody  > tr').each(function () {
            var value = $(this).getColumnValue('#').replaceAll(",", "");
            if (seqNumber == value) {
                $(this).addClass('rowbold');
            }
            else {
                $(this).removeClass('rowbold');
            }
        });
    },
    onSkipClick: function (events) {
        if ($(this).children().first().hasClass('disabledLinkButton')) {
            events.preventDefault();
            return;
        }
        var title = 'Investor Summary';
        var displayResource = 'ForeignTax_Residency_InvestorSummary_SkipMessage';
        var formName = "#certificationSummaryForm";
        var modal = new IC.Public.foreignTaxResidencyMessage();
        modal.showModal(title, displayResource, formName);
    },

    onNextClick: function (events) {
        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        var form = $("#certificationSummaryForm");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorNext");

        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },

    onRegisteredInvestorSelectionChange: function (events, val) {

        var availability = '', seqNumber = '';
        if (val.split('#').length > 0) {
            var availability = val.split('#')[3];
            var seqNumber = val.split('#')[0];
        }
        var _obj = events.data.obj;
        _obj.styleChangesOnGrid(seqNumber);
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "RegisteredInvestorSelection");
        var data = { viewKey: $("#ViewKey").val(), seqNo: seqNumber, dataAvail: availability };
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            async: false,
            success: function (result) {
                $("#summaryQuestionare").html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },

    disPlayMessage: function (value) {

        var title = 'Investor Summary';
        var displayResource = value;
        var formName = "GeneralMessage";
        var modal = new IC.Public.foreignTaxResidencyMessage();
        modal.showModal(title, displayResource, formName);
    },
    onActionTaxPurpose: function (events) {
        var _obj = events.data.obj;
        $("[action=true]").each(function () {
            $(this).attr('checked', false)
        });
        _obj.validateNextButtonAndControlSelection(events);
    },
    onActionDeceasedChange: function (events) {
        var _obj = events.data.obj;
        if ($("[name='InvestorSummaryDetails.ActionDeceased'][value='0']").is(':checked')) {
            if (_obj.checkDeceased(_obj)) {
                _obj.disPlayMessage("ForeignTaxResidency_Investorsummary_Deceased_Message");
            }
        }
        if ($("[name='InvestorSummaryDetails.ActionDeceased'][value='1']").is(':checked')) {
            _obj.disPlayMessage("ForeignTaxResidency_Investorsummary_Deceased_Message_YES_WARNING");
        }
        _obj.validateNextButtonAndControlSelection(events);
    },

    validateControlsOnSelection: function (_obj) {
        var isValid = false;
        $(".ActionTaxPurpose").hide();

        if ($("#IsDAAvailable").val().toLowerCase() == "true" && $("#IsOtherEligibleForCertifyOnDA").val().toLowerCase() != "true") {
            $("select[name='InvestorSummaryDetails.RegisteredInvestor']").prop("disabled", "disabled");
        }
        $(".spanDeceasedQuestion").html("Is this Account Holder a deceased person, or an individual acting in the capacity of executor or administrator of a deceased estate, where a certified copy of the death certificate or Grant of Probate / Letters of Administration has been provided to Link Market Services Limited?");

        if ($("#IsDesignatedAccountExist").val().toLowerCase() == "true" && $("#IsDesignatedAccount").val().toLowerCase() == "true") {
            // Controls view state
            var benifits = $("select.benefit").val();
            if ($("select.benefit").length > 0 && benifits != "") {
                $(".IsDeceasedQuestion").show();
                isValid = true && $("[name='InvestorSummaryDetails.ActionTaxPurpose']").is(':checked');
                if (benifits == "IND") {
                    $(".ActionTaxPurpose").show();
                    $(".foreign-question").html("Is this Account held for a U.S. citizen OR a resident for tax purposes in a country other than Australia?");
                    $(".spanDeceasedQuestion").html("Is this Account held for a deceased person, or by an individual acting in the capacity of executor or administrator of a deceased estate, where a certified copy of the death certificate or Grant of Probate / Letters of Administration has been provided to Link Market Services?");
                    $(".spanDeceasedRadioButton").css("padding-right", "47%");
                }
                else if (benifits == "ENT") {
                    $(".ActionTaxPurpose").show();
                    $(".foreign-question").html("Is the entity a resident for tax purposes in a country other than Australia?");
                    $(".IsDeceasedQuestion").hide();
                }
                else if (benifits == "NEI") {
                    $(".ActionTaxPurpose").hide();
                    //Don’t display ‘FOREIGN’ question but default FOREIGN value to ‘NO’ as it is mandatory field
                    $("input[name='InvestorSummaryDetails.ActionTaxPurpose'][value='0']").prop("checked", "checked");
                }
            }

        }
        else {
            $(".ActionTaxPurpose").show();
        }
        $("[action=true]").each(function () {
            $(this).prop('disabled', "disabled");
            $(this).parents('ul').hide();
        });

        if ($("[name='InvestorSummaryDetails.ActionTaxPurpose']").is(':checked')) {

            if ($("[name='InvestorSummaryDetails.ActionTaxPurpose'][value='1']").is(':checked')) {

                if ($("[name='InvestorSummaryDetails.ActionDeceased']") && $("[name='InvestorSummaryDetails.ActionDeceased']").length > 0) {
                    $("[name='InvestorSummaryDetails.ActionDeceased']").removeAttr('disabled');

                    if ($(".benefit").length > 0 && $(".benefit").val() == "ENT") {
                        $("[name='InvestorSummaryDetails.ActionDeceased']").parents('ul').hide();
                    }
                    else {
                        $("[name='InvestorSummaryDetails.ActionDeceased']").parents('ul').show();
                        if (!$("[name='InvestorSummaryDetails.ActionDeceased']").is(':checked')) {
                            isValid = false;
                        }
                    }

                    if ($("[name='InvestorSummaryDetails.ActionDeceased']").is(':checked')) {
                        isValid = true;
                    }
                }
                else {
                    isValid = true;
                }
            }
            else {
                isValid = true;
            }
        }
        return (isValid);
    },

    checkDeceased: function (obj) {
        if ($("#btnTitleSelected").val() == 'EST'
            || ($("#IsDeceased").val() == 'true') || ($("#IsDeceased").val() == 'True')) {
            return true;
        } else {
            return false;
        }
    },

    validateNextButtonAndControlSelection: function (events) {
        var _obj = events.data.obj;
        var isValid = false, isDrpSelectionValid = false;

        isValid = _obj.validateControlsOnSelection(_obj);

        if ($("#InvestorSummaryDetails_RegisteredInvestor").val() != '') {
            _obj.btnSkip.children().first().removeClass('disabledLinkButton');
        } else {
            _obj.btnSkip.children().first().addClass('disabledLinkButton');
        }

        if ($("#InvestorSummaryDetails_RegisteredInvestor").val() != '' &&
            $("#InvestorSummaryDetails_Capacity").val() != '') {
            isDrpSelectionValid = true;
        }

        if (isValid && isDrpSelectionValid) {
            _obj.btnNext.parent().removeClass('disabled');
        }
        else
            _obj.btnNext.parent().addClass('disabled');

    },
    onDesignatedNeitherAlert: function (viewKey, seqNo, dataAvail) {

        var title = 'Investor Summary';
        var displayResource = 'ForeignTax_Residency_InvestorSummary_Neither_Message';
        var formName = "#certificationSummaryForm";
        var modal = new IC.Public.foreignTaxResidencyMessage();
        modal.showModalOnNeither(title, displayResource, formName, viewKey, seqNo, dataAvail);
    },
    onChangeInvestor: function (events) {
        var _obj = events.data.obj;
        var previousSeq = $("#previousSeqValue").val();
        var previousDataAvail = $("#previousDataAvail").val();
        if ($("input[type='radio']").length != 0) {
            $("input[type='radio']").attr('checked', false);
        }

        if ($(this).val().split('#').length > 0) {
            var seqNumber = $(this).val().split('#')[0];
            var availability = $(this).val().split('#')[3];
            $("#previousSeqValue").val(seqNumber);
            $("#previousDataAvail").val(availability);

        } else {
            $("#previousSeqValue").val('');
            $("#previousDataAvail").val('');
        }
        _obj.onRegisteredInvestorSelectionChange(events, $(this).val());
        _obj.validateNextButtonAndControlSelection(events);

        if ($("#IsDAAvailable").val().toLowerCase() == "true" && $("#IsDesigNeither").val().toLowerCase() == "true") {
            var val = $(this).val();
            if (val.split('#').length > 0) {
                var seqNumber = val.split('#')[0];
                var availability = val.split('#')[3];
                if (seqNumber == "0") {
                    _obj.onDesignatedNeitherAlert($("#ViewKey").val(), previousSeq, previousDataAvail)
                }
            }
        }
    },
    onChangeBenefit: function (events) {
        var _obj = events.data.obj;
        // Reset the controlls based on the value change in dropdown
        $("input[name='InvestorSummaryDetails.ActionTaxPurpose']").removeAttr("checked");
        $("input[name='InvestorSummaryDetails.ActionDeceased']").removeAttr("checked");
        $("input[name='InvestorSummaryDetails.ActionTaxPurpose']").prop("checked", false);
        $("input[name='InvestorSummaryDetails.ActionDeceased']").prop("checked", false);
        _obj.validateNextButtonAndControlSelection(events);
    },
    onChangeCapacity: function (events) {
        var _obj = events.data.obj;
        if ($("input[type='radio']").length != 0) {
            $("input[type='radio']").attr('checked', false);
        }
        _obj.validateNextButtonAndControlSelection(events);
    },
    onFilterClick: function (events) {
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "ForeignTaxResidencyCertification");
        if ($("#IsCampaignEnabled").val().toLowerCase() == "true") {
            OC.MVC.util.loadMainContainerView(url, { isRedirectedFromLoginPage: true });
        }
        else {
            OC.MVC.util.loadMainContainerView(url, null);
        }
    },

    onMoreClick: function (events) {
        events.preventDefault();
        $("#more_text").slideToggle(100);
        if ($(this).html() == "<u>More Info</u>")
            $(this).html("<u>Less Info</u>");
        else
            $(this).html("<u>More Info</u>");
    }
};
;IC.Public.foreignTaxResidencyMessage = function () {

};
IC.Public.foreignTaxResidencyMessage.prototype = {
    init: function () {
        var self = this;
       // self.registerEvents();
        self.adjustModalSizeAndPosition();
    },

    showModal: function (titleVal, displayValue, formName) {
        
        IC.Public.showLoading("");
        var self = this;
        var data = {
            title: titleVal,
            displayResource: displayValue,
            formName: formName
        };
        var urlVal = OC.MVC.util.getLink("ForeignTaxResidency", "DisplayMessage");
        OC.MVC.util.showModalByUrl(urlVal, data,
                { maxHeight: 300, maxWidth: 500, hideCloseButton: false }, function () {
                    self.init();
                    
                    if (!(document.addEventListener)) {
                        $("#simplemodal-overlay").css('filter', 'alpha(opacity=70)');
                    }
     
                    //CRS.PCR.010 - Change the 'Cancel' button text to 'Continue' - Individual and entity screen DEC
                    if (displayValue == "ForeignTaxResidency_Investorsummary_Deceased_Message_YES_WARNING" || displayValue == "ForeignTax_Residency_GIIN_InvalidWarning")// Cancel to continue text for the button
                    {
                        $("#btnClose").html("Continue");
                    }
                    else {
                        $("#btnClose").html("Cancel");
                    }
                });
    },
    showModalOnNeither: function (titleVal, displayValue, formName,viewKey, seqNo, dataAvail) {
        IC.Public.showLoading("");
        var self = this;
        var data = {
            title: titleVal,
            displayResource: displayValue,
            formName: formName,
            viewKeyVal: viewKey,
            seqNumberVal: seqNo,
            dataAvail: dataAvail
        };
        var urlVal = OC.MVC.util.getLink("ForeignTaxResidency", "DisplayMessageOnNeither");
        OC.MVC.util.showModalByUrl(urlVal, data,
                { maxHeight: 300, maxWidth: 500, hideCloseButton: false }, function () { self.init(); });
    },
    //registerEvents: function () {
    //    this.btnConfirm = $('#btnConfirm');
    //    this.btnConfirm.unbind('click');
    //    this.btnConfirm.bind('click', { obj: this }, this.onConfirmClick);
    //},
    //onConfirmClick: function (events) {
    //    var self = events.data.obj;
    //    IC.Public.showLoading("");
    //    $.modal.close();
    //    var url = $("#ConfirmUrl").val();
    //    OC.MVC.util.loadMainContainerView(url);

    //    var form = $("#certificationSummaryForm");
    //    var data = form.serializeObject();
    //    var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorNext");
    //    form.validate(IC.Public.getValidatorCfg());
    //    if (form.valid()) {
    //        IC.Public.showLoading("");
    //        $.ajax({
    //            url: url,
    //            type: 'POST',
    //            data: data,
    //            success: function (result) {
    //                $("#" + OC.MVC.constants.mainContainer).html(result);
    //                IC.Public.hideLoading();
    //            },
    //            error: function (err) {
    //                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
    //                IC.Public.hideLoading();
    //            }
    //        });
    //    }
    //},

    displaySuccessMessage: function () {
        $('#messageDiv').hide();
    },
    displayErrorMessage: function (errorMessage) {
        $('#mainErrorDiv').html("<ol><li>" + errorMessage + "</li></ol>");
    },
    adjustModalSizeAndPosition: function () {
        $('#simplemodal-container').css('height', 'auto');
        $('#simplemodal-container').css('width', '450px');
        $.modal.setPosition();
    }
};

;IC.Public.foreignTaxResidencyMessagePopUp = function () {

};
IC.Public.foreignTaxResidencyMessagePopUp.prototype = {
    init: function () {
        var self = this;
        self.registerEvents();
        IC.Public.hideLoading();
    },
    registerEvents: function () {
        this.btnConfirm = $('#btnConfirm');
        this.btnConfirm.unbind('click');
        this.btnConfirm.bind('click', { obj: this }, this.onConfirmClick);
        this.btnCloseNeither = $('#btnCloseNeither');
        this.btnCloseNeither.unbind('click');
        this.btnCloseNeither.bind('click', { obj: this }, this.onNoClick);

        this.btnClose = $('#btnClose');
        this.btnClose.unbind('click');
        this.btnClose.bind('click', { obj: this }, this.onCloseClick);
    },
    onCloseClick: function (events) {
        $.modal.close();
    },
    onNoClick: function (events) {
        var _obj = events.data.obj;
        if ($(this).attr("disabled") == "disabled") {
            events.preventDefault();
            return;
        }
        $(this).attr("disabled", "disabled");
        var self = events.data.obj;
        if ($("#IsNeither").val().toString().toLowerCase() == "true") {
            var viewKey = $("#ViewKey").val();
            var seqNumber = "0";
            var availability = "true";

            var url = OC.MVC.util.getLink("ForeignTaxResidency", "RegisteredInvestorSelection");
            var data = { viewKey: $("#ViewKey").val(), seqNo: seqNumber, dataAvail: availability };
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function (result) {
                    $("#summaryQuestionare").html(result);
                    IC.Public.hideLoading();
                },
                error: function (err) {
                    $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
        //  IC.Public.showLoading("");

        //var formName = $("#FormName").val();
        //var form = $(formName);
        $.modal.close();
    },
    onConfirmClick: function (events) {
        var self = events.data.obj;
        if ($(this).attr("disabled") == "disabled") {
            events.preventDefault();
            return;
        }
        $(this).attr("disabled", "disabled");
        //  IC.Public.showLoading("");

        //var formName = $("#FormName").val();
        //var form = $(formName);

        //if ($("#IsCampaignEnabled").val().toLowerCase() == "true") {
          
        //}
        if ($("#IsNeither").val().toString().toLowerCase() == "true") {
            var viewKey = $("#ViewKey").val();
            var seqNumber = $("#SequenceNumberOnNeither").val();
            var availability = $("#DataAvailabilityOnNeither").val();

            var url = OC.MVC.util.getLink("ForeignTaxResidency", "RegisteredInvestorSelection");
            var data = { viewKey: $("#ViewKey").val(), seqNo: seqNumber, dataAvail: availability };
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function (result) {
                    $("#summaryQuestionare").html(result);
                    IC.Public.hideLoading();
                },
                error: function (err) {
                    $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
        else {
            //var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorNextOnSkip");
            //IC.Public.showLoading("");
            //$.ajax({
            //    url: url,
            //    type: 'POST',
            //    //data: data,
            //    success: function (result) {
            //        $("#" + OC.MVC.constants.mainContainer).html(result);
            //        IC.Public.hideLoading();
            //    },
            //    error: function (err) {
            //        $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
            //        IC.Public.hideLoading();
            //    }
            //});
            var formName = $("#FormName").val();
            var form = $(formName);
            var isEmployeeLogin = $('#IsEmployeeLogin').val();
            var issuerCode = $('#IssuerCode').val();
            $.modal.close();

            var data = form.serializeObject();
            var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorSkip");
            //form.validate(IC.Public.getValidatorCfg());

            //if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function (result) {
                    if (isEmployeeLogin != undefined && isEmployeeLogin.toString().toLowerCase() == "true") {
                        {
                            window.location.href = "/Employee/" + issuerCode;
                        }
                    }
                    else {
                        var urlLocation = "/";
                        window.location.href = urlLocation;
                    }

                    //var urlLocation = "/";
                    //window.location.href = urlLocation;
                },
                error: function (err) {
                    $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
        $.modal.close();
    },
};IC.Public.investorAddressDetails = function () {

};
IC.Public.investorAddressDetails.prototype = {
    init: function (isEdit) {
        var self = this;
        self.registerEvents(isEdit);
        self.adjustModalSizeAndPosition();
        IC.Public.hideLoading();
        //this.IntialEvents = $("#btnIntial");
        //this.IntialEvents.unbind('click');
        //this.IntialEvents.bind("click", { obj: this }, this.validateNextButtonAndControlSelection);
        //$('#btnIntial').trigger('click');

        // Call Google Api for Address suggestion
        InvokeAddressPopupGooglePlaceAPI();
        $(".simplemodal-close").hide();
    },

    showModal: function (urlValue, isedit, isRegisterandStreetSame) {
        IC.Public.showLoading("");
        var self = this;
        var data = {
            url: urlValue,
            isRegisterandStreetSame: isRegisterandStreetSame,
            isEdit: isedit
        };
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "EditInvestorAdress");
        OC.MVC.util.showModalByUrl(url, data,
                { maxHeight: 300, maxWidth: 500, hideCloseButton: false }, function () { self.init(isedit == undefined ? false : isedit); });
    },
    showModalView: function (urlValue) {
        IC.Public.showLoading("");
        var self = this;
        var data = {
            url: urlValue,
        };
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "ViewInvestorAdress");
        OC.MVC.util.showModalByUrl(url, data,
                { maxHeight: 300, maxWidth: 500, hideCloseButton: false }, function () { self.init(false); });
    },
    showModalViewSeqIndex: function (seq) {
        IC.Public.showLoading("");
        var self = this;
        var data = {
            seqIndex: seq,
        };
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "ViewInvestorAdressIndex");
        OC.MVC.util.showModalByUrl(url, data,
                { maxHeight: 300, maxWidth: 500, hideCloseButton: false }, function () { self.init(false); });
    },
    showModalIndex: function (urlValue, indexValue, isValid, isRegisterandStreetSame, isedit) {
        IC.Public.showLoading("");
        var self = this;
        var data = {
            url: urlValue,
            index: indexValue,
            isOthersValid: isValid,
            isRegisterandStreetSame: isRegisterandStreetSame,
            isEdit: isedit
        };
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "EditInvestorAddressIndex");
        OC.MVC.util.showModalByUrl(url, data,
                { maxHeight: 300, maxWidth: 500, hideCloseButton: false }, function () { self.init(isedit == undefined ? false : isedit); });
    },
    registerEvents: function (isEditAddressClick) {

        this.btnSaveAddress = $('#btnSaveAddress');
        this.btnSaveAddress.unbind('click');
        this.btnSaveAddress.bind('click', { obj: this }, this.onSaveAddressClick);

        this.btnCancelAddress = $('#btnCancel');
        this.btnCancelAddress.unbind('click');
        this.btnCancelAddress.bind('click', { obj: this, isEditClick: isEditAddressClick }, this.onCancelAddressEditClick);

        var addressTypeChange = $("[name='AddressType']");
        addressTypeChange.unbind('change');
        addressTypeChange.bind('change', { obj: this }, this.onChangeAction);

        this.AddressLine1 = $("#AddressLine1");
        this.AddressLine1.unbind('keyup');
        this.AddressLine1.bind("keyup", { obj: this }, this.onChangeAction);

        //this.AddressLine2 = $("#IndividualInvestor_EditedInvestorAddress_AddressLine2");
        //this.AddressLine2.unbind('keyup');
        //this.AddressLine2.bind("keyup", { obj: this }, this.onChangeAction);

        this.Address_Town = $("#Town");
        this.Address_Town.unbind('keyup');
        this.Address_Town.bind("keyup", { obj: this }, this.onChangeAction);

        this.Address_PostCode = $("#PostalCode");
        this.Address_PostCode.unbind('keyup');
        this.Address_PostCode.bind("keyup", { obj: this }, this.onChangeAction);

        this.Address_State = $("#State");
        this.Address_State.unbind('keyup');
        this.Address_State.bind("keyup", { obj: this }, this.onChangeAction);

        this.Address_PhoneValue = $("#Phone_Value");
        this.Address_PhoneValue.unbind('keyup');
        this.Address_PhoneValue.bind("keyup", { obj: this }, this.onChangeAction);

        this.Address_PhoneCode = $("#Phone_Code");
        this.Address_PhoneCode.unbind('keyup');
        this.Address_PhoneCode.bind("keyup", { obj: this }, this.onChangeAction);

        var address_CountryChange = $("#TaxResidenceCountry");
        address_CountryChange.unbind('change');
        address_CountryChange.bind('change', { obj: this }, this.onChangeAction);


        this.IntialEvents = $("#btnIntial");
        this.IntialEvents.unbind('click');
        this.IntialEvents.bind("click", { obj: this }, this.onChangeAction);
        $('#btnIntial').trigger('click');

    },
    onChangeAction: function (events) {
        var _obj = events.data.obj;
        _obj.validateAddressUpdate(_obj);
    },
    onCancelAddressEditClick: function (events) {
        $.modal.close();
        // If not edit link click then revert the yes or no selection 
        var _obj = events.data.isEditClick;
        if (_obj != undefined && _obj == false) {
            $("#btnNext").parent().addClass('disabled');//.attr("disabled", "disabled");
            var index = $(this).attr("index");
            // remove check in address radio button
            if (index != undefined && index != "") {
                $("td[index='" + index + "'] input[type='radio']").prop('checked', false)
                $("#streetAddress_" + index).hide();
            }
            else { // Individual investor
                $("input[name='IndividualInvestor.ActionStreetAddress']").prop('checked', false)
                $("input[name='EntityOrganizationDetails.NonFinancialEntities.ActionStreetAddress']").prop('checked', false);
                $("#streetAddress").hide();
            }
        }
    },
    onEditCancelAddressEditClick: function (events) {
        $.modal.close();
    },
    onSaveAddressClick: function (events) {
        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        var form = $("#investorAddressForm");
        var data = form.serializeObject();

        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorNextAddress");
        form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function (result) {
                    $(".IsSameAsResidencial[index='" + data.RowSequenceIndex + "'][value='0']").attr("checked", "checked");
                    if ($("input[name='IndividualInvestor.ActionStreetAddress']").length != 0)
                    {
                        $("input[name='IndividualInvestor.ActionStreetAddress'][value='0']").attr("checked", "checked");
                    }
                    if ($("input[name='EntityOrganizationDetails.NonFinancialEntities.ActionStreetAddress']").length != 0)
                    {
                        $("input[name='EntityOrganizationDetails.NonFinancialEntities.ActionStreetAddress'][value='0']").attr("checked", "checked");
                    }
                    var addVal = _obj.addressDisplay(data);
                    if (result == "IndividualInvestorCntrl") {
                        if (data.RowSequenceIndex && data.RowSequenceIndex != '') {

                            $("#streetAddress_" + data.RowSequenceIndex + " .address-1").html(data.AddressLine1);
                            $("#streetAddress_" + data.RowSequenceIndex + " .address-2").html(data.AddressLine2);
                            $("#streetAddress_" + data.RowSequenceIndex + " .address-3").html(data.AddressLine3);
                            $("#streetAddress_" + data.RowSequenceIndex + " .address-4").html(data.AddressLine4);
                            $("#streetAddress_" + data.RowSequenceIndex + " .town").html(data.Town);

                            if (data.TaxResidenceCountry == "AUS") {
                                $("#streetAddress_" + data.RowSequenceIndex + " .state").html(data.State);
                            }
                            else {
                                $("#streetAddress_" + data.RowSequenceIndex + " .state").html(data.OptionalState);
                            }

                            $("#streetAddress_" + data.RowSequenceIndex + " .postcode").html(data.PostalCode);
                            $("#streetAddress_" + data.RowSequenceIndex + " .country").html(data.TaxResidenceCountry);

                            $("#streetAddress_" + data.RowSequenceIndex).show();

                            $("#addseq" + data.RowSequenceIndex).text(addVal);
                            $("#addseq" + data.RowSequenceIndex).attr("validate", "true");
                            $("#btnEditAddress" + data.RowSequenceIndex).text("Edit Address");


                        }
                    }
                    else if (result == "NonFinancialEntitiesPassive") {
                        if (data.RowSequenceIndex && data.RowSequenceIndex != '') {

                            //if ($("input[name=EntityOrganizationDetails.NonFinancialEntities.ActivePassiveNFE]:checked").lenght > 0 && $("input[name=EntityOrganizationDetails.NonFinancialEntities.ActivePassiveNFE]:checked").val() == "1")
                            $("#streetAddress_"+ data.RowSequenceIndex +" .address-1").html(data.AddressLine1);
                            $("#streetAddress_" + data.RowSequenceIndex + " .address-2").html(data.AddressLine2);
                            $("#streetAddress_" + data.RowSequenceIndex + " .address-3").html(data.AddressLine3);
                            $("#streetAddress_" + data.RowSequenceIndex + " .address-4").html(data.AddressLine4);
                            $("#streetAddress_" + data.RowSequenceIndex + " .town").html(data.Town);
                            if (data.TaxResidenceCountry == "AUS") {
                                $("#streetAddress_" + data.RowSequenceIndex + " .state").html(data.State);
                            }
                            else {
                                $("#streetAddress_" + data.RowSequenceIndex + " .state").html(data.OptionalState);
                            }
                            $("#streetAddress_" + data.RowSequenceIndex + " .postcode").html(data.PostalCode);
                            $("#streetAddress_" + data.RowSequenceIndex + " .country").html(data.TaxResidenceCountry);

                            $("#streetAddress_" + data.RowSequenceIndex).show();

                            $("#addseq" + data.RowSequenceIndex).text(addVal);
                            $("#addseq" + data.RowSequenceIndex).attr("validate", "true");
                            $("#btnEditAddress" + data.RowSequenceIndex).text("Edit Address");



                        }
                    }
                    else if (result == "NonFinancialEntitiesActive") {
                      //  $("#entityAddress").text(addVal);
                        //  $("#entityAddress").attr("validate", "true");
                        $("#streetAddress .address-1").html(data.AddressLine1);
                        $("#streetAddress .address-2").html(data.AddressLine2);
                        $("#streetAddress .address-3").html(data.AddressLine3);
                        $("#streetAddress .address-4").html(data.AddressLine4);
                        $("#streetAddress .town").html(data.Town);
                        if (data.TaxResidenceCountry == "AUS") {
                            $("#streetAddress .state").html(data.State);
                        }
                        else {
                            $("#streetAddress .state").html(data.OptionalState);
                        }
                        $("#streetAddress .postcode").html(data.PostalCode);
                        $("#streetAddress .country").html(data.TaxResidenceCountry);

                        $("#streetAddress").show();
                    }
                    else if (result == "EntityOrganizationDetails") {
                        $("#entityAddress").text(addVal);
                        //  $("#entityAddress").attr("validate", "true");
                    }
                    else if (result == "IndividualInvestor") {
                        //$("#individualAddress").text(addVal);
                        //$("#individualAddress").attr("validate", "true");

                        //update the Street address with the new value and display Street Address

                        // handle based on the index

                        $("#streetAddress .address-1").html(data.AddressLine1);
                        $("#streetAddress .address-2").html(data.AddressLine2);
                        $("#streetAddress .address-3").html(data.AddressLine3);
                        $("#streetAddress .address-4").html(data.AddressLine4);
                        $("#streetAddress .town").html(data.Town);
                        if (data.TaxResidenceCountry == "AUS") {
                            $("#streetAddress .state").html(data.State);
                        }
                        else {
                            $("#streetAddress .state").html(data.OptionalState);
                        }
                        $("#streetAddress .postcode").html(data.PostalCode);
                        $("#streetAddress .country").html(data.TaxResidenceCountry);

                        $("#streetAddress").show();
                    }
                    var value = $("#IsOthersValid").val();
                    //alert(value)
                    $.modal.close();
                    if (value == "true" || value == "True") {
                        $("#btnNext").parent().removeClass('disabled');
                    }
                    //$("#" + OC.MVC.constants.mainContainer).html(result);
                    IC.Public.hideLoading();
                },
                error: function (err) {
                    $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },

    addressDisplay: function (data) {
        //var addVal = data.AddressLine1;
        //if (data.AddressLine2) {
        //    addVal += data.AddressLine2;
        //}
        //addVal = addVal + ' ' + data.State + ' ' + data.TaxResidenceCountry;
        //return addVal;

        var line2 = data.AddressLine1;
        if (data.AddressLine2 && data.AddressLine2 != '') {
            line2 = line2 + ' ' + data.AddressLine2;
        }
        //line2 = if ()(data.AddressLine2 !=) ? line2 : line2 + ' ' + data.AddressLine2;
        var line4 = data.Town + ' ' + data.State + ' ' + data.TaxResidenceCountry + ' ' + data.PostalCode;
        var finalAddressDisplay = line2 + ' ' + line4;
        return finalAddressDisplay;
    },
    displaySuccessMessage: function () {
        // $('#messageDiv').hide();
    },
    displayErrorMessage: function (errorMessage) {
        //  $('#mainErrorDiv').html("<ol><li>" + errorMessage + "</li></ol>");
    },
    adjustModalSizeAndPosition: function () {
        $('#simplemodal-container').css('height', 'auto');
        $('#simplemodal-container').css('width', '820px');
        $.modal.setPosition();
    },
    validateAddressUpdate: function (_obj) {
        if ($("#TaxResidenceCountry").val() == "AUS"){
            $(".au-star").show();
            $("#OptionalState").hide();
            $("#State").show();
        }      
        else
        {
            $(".au-star").hide();
            $("#State").hide();
            $("#OptionalState").show();
        }
        var isValid = false;
        if ($("#AddressType").val() != '' && $("#AddressLine1").val() != '' && $("#TaxResidenceCountry").val() != '')
        {
            if ($("#TaxResidenceCountry").val() != "AUS" || ($("#TaxResidenceCountry").val() == "AUS" && $("#Town").val() != '' && $("#PostalCode").val() != '' && $("#State").val() != ''))
                isValid = true;

            //if ($("#Phone_Code").val().trim().length > 0 || $("#Phone_Value").val().trim().length > 0) {
            //    if ($("#Phone_Value").val().trim().length > 0 && ($("#Phone_Code").val().trim().length == 3))
            //        isValid = true;
            //    else
            //        isValid = false;
            //}
        }


        //if ($("#TaxResidenceCountry").val() != 'AUS') {
        //    $("#SSuburb").hide(); $("#SPostCode").hide(); $("#SState").hide();
        //}
        //else {
        //    $("#SSuburb").show(); $("#SPostCode").show(); $("#SState").show();
        //}
        if (isValid && $("#IsViewMode").val() != "T")
            $("#btnSaveAddress").parent().removeClass('disabled');
        else
            $("#btnSaveAddress").parent().addClass('disabled');
    }
};


// Google API integration
var placeSearch, autocomplete;
var componentForm = {
    sublocality_level_1: 'short_name', // Street address
    route: 'long_name', // Route
    locality: 'long_name',//city
    administrative_area_level_1: 'short_name',//state
    country: 'long_name',
    postal_code: 'short_name',
    street_number: 'short_name',
    route: "long_name",
    administrative_area3: "long_name",
    premise: "long_name",
};


function InvokeAddressPopupGooglePlaceAPI() {
    autocomplete = new google.maps.places.Autocomplete(
        (document.getElementById('AddressLine1')),
        { types: ['geocode'] });

    // When the user selects an address from the dropdown, populate the address
    // fields in the form.
    autocomplete.addListener('place_changed', fillAddressPopup);
}

function fillAddressPopup() {
    // Get the place details from the autocomplete object.
    var place = autocomplete.getPlace();
    // If google api address not available then give street name as whole
    if (place == null || place == undefined || place.address_components.length == 0)
        $("#AddressLine1").val($("#AddressLine1").val());
    else
        $("#AddressLine1").val("");
    place.address_components.forEach(function (e, v) { console.log(e.types[0] + ' - ' + e.long_name) })
    // Bind the address to respecive Address popup forms
    for (var i = 0; i < place.address_components.length; i++) {
        var addressType = place.address_components[i].types[0];// get google parameter
        if (componentForm[addressType]) {
            var val = place.address_components[i][componentForm[addressType]]; // respective address type values and binding it
            var addressLine1 = $("#AddressLine1").val();
            switch (addressType) {
                case "street_number":
                case "route":
                case "premise":
                case "sublocality_level_1":
                    val != "" ? $("#AddressLine1").val(addressLine1 == "" ? val : addressLine1 + ' ' + val) : "";
                    break;
                case "country":
                    $("#TaxResidenceCountry option").each(function () {
                        if ($(this).text().toUpperCase() == val.toUpperCase()) {
                            $(this).attr("selected", "selected");
                        }
                    });
                    //$("#TaxResidenceCountry option:contains('" + val.toUpperCase() + "')").attr("selected", "selected");
                    break;
                case "locality":
                case "administrative_area3":
                    $("#Town").val(val);
                    break;
                case "administrative_area_level_1":
                    $("#State option:contains('" + val.toUpperCase() + "')").attr("selected", "selected");
                    $("#OptionalState").val(val);
                    break;
                case "postal_code":
                    $("#PostalCode").val(val);
                    break;
                default:
                    break;
            }

        }
    }
    // Do validation
    var _obj = IC.Public.investorAddressDetails.prototype;
    _obj.validateAddressUpdate(_obj);
};/// <reference path="ic.public.foreignTaxResidency.investorCertificationSummary.js" />
/// <reference path="~/Scripts/common.js" />
IC.Public.individualInvestor = function () {
    this.init();
}

IC.Public.individualInvestor.prototype = {
    init: function () {
        var isCampaign = $("#IsCampaignEnabled");
        if (isCampaign != undefined && isCampaign.length != 0 && isCampaign.val().toLowerCase() == "true") {
            IC.Public.HideMenu();
        }
        //var divContent = '';
        //$("[rowcount='true']").each(function () {
        //    divContent += $(this).html();
        //    $(this).remove();
        //});
        var checkTaxValidity = this.checkTaxValidation(this);
        if ($("ul [disPlay='block']").length == 1) {
            if (checkTaxValidity) {
                $("[name='IndividualInvestor.ActionMoreJurisdiction'][value='0']").attr('checked', true);
            }
            else {
                $("input[name='IndividualInvestor.ActionMoreJurisdiction']").attr('checked', false);
            }
        }
      //  $('#divAddMoreJurisdiction').after(divContent);

        $("#validateTIN").hide();
        this.btnNext = $("#btnNext");
        this.btnNext.bind('click', { obj: this }, this.onNextClick);
        this.btnSkip = $("#btnSkip");
        this.btnSkip.bind('click', { obj: this }, this.onSkipClick);
        this.btnBack = $("#btnBack");
        this.btnBack.bind('click', { obj: this }, this.onBackClick);
        $("#btnOk").bind('click', { obj: this }, this.onOkClick);
        this.btnAddMore = $("#btnAddMore");
        this.btnAddMore.bind('click', { obj: this }, this.onAddMoreClick);
        this.btnEditAddress = $("#btnEditAddress");
        this.btnEditAddress.bind('click', { obj: this }, this.onEditAddressClick);

        this.RadioActionMoreJurisdiction = $("[name='IndividualInvestor.ActionMoreJurisdiction']");
        this.RadioActionMoreJurisdiction.unbind("change");
        this.RadioActionMoreJurisdiction.bind("change", { obj: this }, this.validateNextButtonAndControlSelection);


        this.txtFirstGivenName = $("#IndividualInvestor_FirstGivenName");
        this.txtFirstGivenName.unbind("keyup");
        this.txtFirstGivenName.bind("keyup", { obj: this }, this.validateNextButtonAndControlSelection);

        //this.txtSecondGivenName = $("#IndividualInvestor_SecondGivenName");
        //this.txtSecondGivenName.unbind("keyup");
        //this.txtSecondGivenName.bind("keyup", { obj: this }, this.validateNextButtonAndControlSelection);

        this.txtSurName = $("#IndividualInvestor_SurName");
        this.txtSurName.unbind("keyup");
        this.txtSurName.bind("keyup", { obj: this }, this.validateNextButtonAndControlSelection);

        this.RadioActionMoreStreetAddress = $("[name='IndividualInvestor.ActionStreetAddress']");
        this.RadioActionMoreStreetAddress.unbind("change");
        this.RadioActionMoreStreetAddress.bind("change", { obj: this }, this.validateNextButtonAndControlSelection);

        var taxResidence = $("[typeValue='TaxRes']");
        taxResidence.unbind('change');
        taxResidence.bind('change', { obj: this }, this.onChangeActionTaxResidence);
        //taxResidence.each(function () {
        //    $(this).trigger('change');
        //});

        var taxIdentificationType = $("[typeValue='TaxTyp']");
        taxIdentificationType.unbind('change');
        taxIdentificationType.bind('change', { obj: this }, this.onChangeActionTaxType);
        //taxIdentificationType.each(function () {
        //    $(this).trigger('change');
        //});

        var placeOB = $("[typeValue='POB']");
        placeOB.unbind('change');
        placeOB.bind('change', { obj: this }, this.onChangeAction);
        //placeOB.each(function () {
        //    $(this).trigger('change');
        //});
        var self = this;
        //self.ids = [];
        //$("[typeValue='TaxRes']").each(function () {
        //    if ($(this).val() != '') {
        //        self.ids.push($(this).val());
        //    }
        //});
        this.tinNumber = $("[typeValue='TFNTxt']");
        this.tinNumber.unbind('keyup');
        this.tinNumber.bind("keyup", { obj: this }, this.onChangeAction);


        this.IntialEvents = $("#btnIntial");
        this.IntialEvents.unbind('click');
        this.IntialEvents.bind("click", { obj: this }, this.validateNextButtonAndControlSelection);
        $('#btnIntial').trigger('click');


        this.dateOfBirth = $("[name='DateOfBirth']");
        // this.dateOfBirth.datepicker('option', { maxDate: new Date() });
        this.dateOfBirth.datepicker('option', { maxDate: new Date() });
        //this.dateOfBirth.datepicker('option', { dateFormat: 'dd/mm/yy', maxDate: new Date()});
        // this.dateOfBirth.datepicker({ dateFormat: 'dd-mm-yy' }).datepicker("setDate", new Date());
        this.dateOfBirth.unbind('change');
        this.dateOfBirth.bind("change", { obj: this }, this.onChangeActionDOB);
        this.dateOfBirth.unbind('focusout');
        this.dateOfBirth.bind("focusout", { obj: this }, this.onChangeActionDOB);

        var value = $("[name='DateOfBirth']").val();
        $("[name='IndividualInvestor.DateOfBirthValue']").val(value);
        //self.DateFrom.datepicker('option', { onSelect: function () { self.resetPlanYear(self); } });
        //this.dateOfBirth.datepicker({ onSelect: function (val) { } });


        var removeCntrlPerson = $("[typeValue='removeCntrlPerson']");
        removeCntrlPerson.unbind('click');
        removeCntrlPerson.bind("click", { obj: this }, this.onRemoveControllingPersonClick);

        if ($("ul [disPlay='block']").length == 1) {
            $("ul [typeValue='removeCntrlPerson']").children().first().addClass('disabledLinkButton');
        }

        // 
        this.editStreetAddress = $("input[name='IndividualInvestor.ActionStreetAddress']");
        this.editStreetAddress.unbind('change');
        this.editStreetAddress.bind("change", { obj: this }, this.editStreeePopup);

        // Onload control street address view
        if ($("input[name='IndividualInvestor.ActionStreetAddress']:checked").length != 0)
            $("#streetAddress").show();
        else
            $("#streetAddress").hide();
        taxResidenceOnPageLoad();
    },
    editStreeePopup: function (events) {
        var _obj = events.data.obj;
        if ($(this).val() == "1") {// yes
            $("#streetAddress .address-1").html($("#divRegistered .address-1").html());
            $("#streetAddress .address-2").html($("#divRegistered .address-2").html());
            $("#streetAddress .address-3").html($("#divRegistered .address-3").html());
            $("#streetAddress .address-4").html($("#divRegistered .address-4").html());
            $("#streetAddress .state").html($("#divRegistered .state").html());
            $("#streetAddress .town").html($("#divRegistered .town").html());
            $("#streetAddress .postcode").html($("#divRegistered .postcode").html());
            $("#streetAddress .country").html($("#divRegistered .country").html());

            $("#streetAddress").show();
            _obj.validateNextButtonAndControlSelection(events);
        }
        else {// If no then display the popup
            var urlConfirm = OC.MVC.util.getLink("Holding", "Index");
            var modal = new IC.Public.investorAddressDetails();
            modal.showModal(urlConfirm, false, false);
        }
    },
    //checkTaxDuplication:function (events)
    //{
    //    $("[name='Foo'][value='All']")
    //},
    onRemoveControllingPersonClick: function (events) {
        if ($(this).children().first().hasClass('disabledLinkButton')) {
            events.preventDefault();
            return;
        }
        var sequence = $(this).attr('countVal');
        $("[name='IndexActioned']").val(sequence);
        //$(this).parents('li').hide();
        var form = $("#individualInvestorForm");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "RemoveInvestorDetails");

        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            // cache:false,
            success: function (result) {
                //$("#" + OC.MVC.constants.mainContainer).html('');
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    onSkipClick: function (events) {
        var title = 'Individual Investor';
        var displayResource = 'ForeignTax_Residency_InvestorSummary_SkipMessage';
        var formName = "#individualInvestorForm";
        var modal = new IC.Public.foreignTaxResidencyMessage();
        modal.showModal(title, displayResource, formName);
    },
    disPlayDuplicateMessage: function (value) {

        var title = 'Individual Investor';
        var displayResource = value;
        var formName = "GeneralMessage";
        var modal = new IC.Public.foreignTaxResidencyMessage();
        modal.showModal(title, displayResource, formName);
    },
    onNextClick: function (events) {
        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled') || !_obj.checkDuplicationTaxDetails(_obj)) {
            events.preventDefault();
            return;
        }
        var form = $("#individualInvestorForm");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorNext");
        //form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        //}
    },
    onBackClick: function (events) {
        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        var form = $("#individualInvestorForm");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorBack");
        //form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        //}
    },
    onChangeActionTaxResidence: function (events) {
        var _obj = events.data.obj;
        // fetch the selected country value
        var selectedValue = $(this).val();
        // if tax residence select then no need to act else satisfy the requirement
        if (selectedValue != "") {
            $(this).closest("div").next().find("select[name$='TaxIdentificationType']")[0].selectedIndex = 0;
            // hide TIN Type - USA TIN,NZL IRD,UK UTR,UK NINO
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option").each(function () {
                var val = $(this).val();
                 if (val == "NZL_IRD" || val == "USA_TIN" || val == "UK_UTR" || val == "UK_NIN") {
                    if ($(this).parent()[0].nodeName == "SPAN")
                        $(this).parent().hide();
                    else
                        $(this).wrap((navigator.appName == 'Microsoft Internet Explorer' || navigator.appName == 'Netscape') ? '<span>' : null).hide();
                }

            });
            var spanHandler = (navigator.appName == 'Microsoft Internet Explorer' || navigator.appName == 'Netscape') ? "<span></span>" : " ";
            //based on the residence set the default USA - USA TIN ; NZL - NZL IRD; UK - UK UTR & UK NINO. 
            if (selectedValue == "USA") {
                // $("select[name$='TaxResidence']")
                $(this).closest("div").next().find("option[value='USA_TIN']").unwrap().show();
                $(this).closest("div").next().find("select[name$='TaxIdentificationType']").val('USA_TIN');
            }                                                                             
            else if (selectedValue == "NZL") {                                            
                $(this).closest("div").next().find("option[value='NZL_IRD']").unwrap().show();
                $(this).closest("div").next().find("select[name$='TaxIdentificationType']").val('NZL_IRD');
            }                                                                             
            else if (selectedValue == "GBR") {                                            
                $(this).closest("div").next().find("option[value='UK_UTR']").unwrap().show();
                $(this).closest("div").next().find("option[value='UK_NIN']").unwrap().show();//.attr("selected", true)
                $(this).closest("div").next().find("select[name$='TaxIdentificationType']").val('UK_UTR');
            }
            else {
                $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option[value='']").attr("selected", true);
            }
        }
        else {
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option").show();
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option[value='']").attr("selected", true);
        }
        //what we need to act for other country?

        _obj.validateNextButtonAndControlSelection(events);
    },
    ValidateDate: function (dtValue) {
        var dtRegex = new RegExp(/\b\d{1,2}[\/-]\d{1,2}[\/-]\d{4}\b/);
        return dtRegex.test(dtValue);
    },
    validateDateAndMonth: function () {
        try {
            $.datepicker.parseDate('dd/mm/yy', $("[name='DateOfBirth']").val());
        } catch (e) {
            //alert(e);
            return false;
        };
        return true;
    },
    onChangeActionDOB: function (events) {
        var _obj = events.data.obj;
        var date = $("[name=DateOfBirth]").val();
        if (_obj.ValidateDate(date) && _obj.validateDateAndMonth()) {

            var dateValue = $.datepicker.parseDate("dd/mm/yy", $("[name='DateOfBirth']").val());
            var newDateValue = new Date();
            //var dateCurrentValue = $.datepicker.parseDate("dd/mm/yy", newDateValue.getDate());

            if (dateValue > newDateValue) {
                $("[name=DateOfBirth]").val($("[name='IndividualInvestor.DateOfBirthValue']").val());
            }
            else {
                var value = $("[name='DateOfBirth']").val();
                $("[name='IndividualInvestor.DateOfBirthValue']").val(value);
            }

            _obj.validateNextButtonAndControlSelection(events);
        }
        else {
            $("[name='DateOfBirth']").val('');
            $("[name='IndividualInvestor.DateOfBirthValue']").val('');
            _obj.validateNextButtonAndControlSelection(events);
        }
    },
    onChangeActionTaxType: function (events) {

        if ($(this).val() == 'DNI_TIN' ||
            $(this).val() == 'DNR_TIN' || $(this).val() == 'NO_TIN') {
            $(this).parent().parent().next().hide();
            $(this).parent().parent().next().find('[typevalue="TFNTxt"]').val('');
        }
        else { $(this).parent().parent().next().show(); }
        var _obj = events.data.obj;
        _obj.validateNextButtonAndControlSelection(events);
    },
    onChangeAction: function (events) {
        var _obj = events.data.obj;
        _obj.validateNextButtonAndControlSelection(events);
    },
    validateNextButtonAndControlSelection: function (events) {
        var _obj = events.data.obj;
        var isValid = false;

        if ($("input[name='IndividualInvestor.ActionStreetAddress']:checked").is(':checked') &&
            $("#IndividualInvestor_FirstGivenName").val() != '' &&
            $("#IndividualInvestor_SurName").val() != ''
            ) {
            isValid = true;
        }
        if ($("[name='DateOfBirth']").val() == '' || $("[name='DateOfBirth']").val() == undefined) {
            isValid = false && isValid;
        }
        else {
            isValid = true && isValid;
        }
        var isTaxValid = _obj.checkTaxValidation(_obj);
        isValid = isValid && isTaxValid;

        if ($("input[name='IndividualInvestor.ActionMoreJurisdiction']:checked").is(':checked')
            && isValid) {
            isValid = true;
        } else {           
            isValid = false;
        }
        if (!($("input[name='IndividualInvestor.ActionMoreJurisdiction']:checked").is(':checked'))) {
            $("#btnAddMore").attr("disabled", "disabled");
        }

        if (($("[name='IndividualInvestor.ActionMoreJurisdiction'][value='1']").is(':checked')) && $("ul [disPlay='block']").length == 1) {
            $("#btnAddMore").removeAttr('disabled');
            isValid = isValid && false;
        } else if (($("[name='IndividualInvestor.ActionMoreJurisdiction'][value='0']").is(':checked'))) {
            $("#btnAddMore").attr("disabled", "disabled");
            isValid = isValid && true;
        }
       
        if (($("[name='IndividualInvestor.ActionMoreJurisdiction'][value='1']").is(':checked') && $("ul [disPlay='block']").length > 1) && isTaxValid) {
            isValid = true && isValid;
        }
        if (isValid)
            _obj.btnNext.parent().removeClass('disabled');
        else
            _obj.btnNext.parent().addClass('disabled');
    },
    checkDuplicationTaxDetails: function (_obj) {

        var arraycombinations = [];
        var taxComb = ''; var duplicated = false;

        $("[typeValue='TaxTyp']").each(function () {
            taxComb = '';

            var countVal = $(this).attr("idVal");
            taxComb = $("[typeValue='TaxRes'][idVal='" + countVal + "']").val();
            taxComb = taxComb + $(this).val();
            if ($(this).val() == 'DNI_TIN' ||
                     $(this).val() == 'DNR_TIN' || $(this).val() == 'NO_TIN') {
                //$(this).parent().parent().next().hide();
            }
            else {
                var val = $("[typeValue='TFNTxt'][idVal='" + countVal + "']").val();
                taxComb = taxComb + $.trim(val);
            }
            arraycombinations.push(taxComb);
        });
        var temp = [];
        $.each(arraycombinations, function (key, value) {
            if ($.inArray(value, temp) === -1) {
                temp.push(value);
            } else {
                duplicated = true;
                //_obj.btnNext.parent().addClass('disabled');
                // alert("Tax details duplicated");
                _obj.disPlayDuplicateMessage("ForeignTaxResidency_TaxDetails_Duplicate_Message");
            }
        });
        return (duplicated == false);
    },

    checkTaxValidation: function (_obj) {
        var isValidTaxResidence = true, isValidTFNTxt = true, isValidTFNType = true, isValidPOB = true;
        isValidDOB = true;
        //var isDisplayUsCitizen = false;
        $("[typeValue='TaxTyp']").each(function () {
            if ($(this).val() == 'DNI_TIN' ||
                     $(this).val() == 'DNR_TIN' || $(this).val() == 'NO_TIN') {
                $(this).parent().parent().next().hide();
                $(this).parent().parent().next().find('[typevalue="TFNTxt"]').val('');
            }
            else { $(this).parent().parent().next().show(); }

            if ($(this).val() == '') {
                isValidTFNType = false;
            }
        });
        var residencyCount = 0;
        $("[typeValue='TaxRes']").each(function () {
            residencyCount += 1;
            if ($(this).val() == '') {
                isValidTaxResidence = false;
            }
            //if ($(this).val() == 'USA') {
            //    isDisplayUsCitizen = true;
            //}
        });
        if ($("ul [disPlay='block']").length > 1) {
            $("[name='IndividualInvestor.ActionMoreJurisdiction'][value='1']").attr('checked', true);
            $("[name='IndividualInvestor.ActionMoreJurisdiction']").attr('disabled', 'disabled');
            $("#btnAddMore").removeAttr('disabled');
        }
        else
            $("[name='IndividualInvestor.ActionMoreJurisdiction']").removeAttr('disabled');
        //if (residencyCount > 1) {
        //    $("[name='IndividualInvestor.ActionMoreJurisdiction']").attr('disabled', 'disabled');
        //} else
        //    $("[name='IndividualInvestor.ActionMoreJurisdiction']").removeAttr('disabled');

        $("[typeValue='TFNTxt']").each(function () {
            if (!$(this).parent().parent().is(':visible')) {
                $(this).val() == '';
            }
            if ($(this).val().trim() == "" && $(this).parent().parent().is(':visible')) {
                isValidTFNTxt = false;
            }
        });

        //if (isDisplayUsCitizen) {
        //    $("#UlHideUs").hide();
        //}
        //else {
        //    $("#UlHideUs").show();
        //    $("[typeValue='POB']").each(function () {
        //        if ($(this).val() == '') {
        //            isValidPOB = false;
        //        }
        //    });
        //    if ($("[name=DateOfBirth]").val() == '') { isValidDOB = false; }
        //}
        // var valid = (isValidTaxResidence && isValidTFNTxt && isValidTFNType);
        //if (valid) {
        //    valid = _obj.checkDuplicationTaxDetails();
        //}
        return (isValidTaxResidence && isValidTFNTxt && isValidTFNType); //&& isValidPOB && isValidDOB);
    },
    onAddMoreClick: function (events) {

        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled') || !_obj.checkTaxValidation(_obj) || !_obj.checkDuplicationTaxDetails(_obj)) {
            events.preventDefault();
            return;
        }
        $("#ScreenType").val("IndividualInvestor");
        var form = $("#individualInvestorForm");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "AddMoreFatcaTaxDetails");
        form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        //}
    },

    onEditAddressClick: function (events) {
        var urlConfirm = OC.MVC.util.getLink("Holding", "Index");
        var modal = new IC.Public.investorAddressDetails();
        var isRegisterandStreetSame = $("input[name='IndividualInvestor.ActionStreetAddress']:checked").val() == "1";
        modal.showModal(urlConfirm, true, isRegisterandStreetSame);
    },
    onOkClick: function (events) {
        $.modal.close();
    }
};

function validateTINForIndividual(textValue) {
    var id = $(textValue).attr("idval");
    var taxResidence = $("#IndividualInvestor_InvestorTaxDetails_" + id + "__TaxResidence").val();
    var taxIdType = $("#IndividualInvestor_InvestorTaxDetails_" + id + "__TaxIdentificationType").val();
    validateTIN(textValue, taxResidence, taxIdType);

}

function taxResidenceOnPageLoad() {
    // hide NZL/ UK and USA options
    $("select[name$='TaxResidence']").closest("div").next().find("select[name$='TaxIdentificationType'] option").each(function () {
        var val = $(this).val();
        if (val == "NZL_IRD" || val == "USA_TIN" || val == "UK_UTR" || val == "UK_NIN") {
            //$(this).hide(); IE fix
            //$(this).unwrap('<span>').wrap((navigator.appName == 'Microsoft Internet Explorer' || navigator.appName == 'Netscape') ? '<span>' : null).hide();

            if ($(this).parent()[0].nodeName== "SPAN")
                $(this).parent().hide();
            else
                $(this).wrap((navigator.appName == 'Microsoft Internet Explorer' || navigator.appName == 'Netscape') ? '<span>' : null).hide();
        }

    });
    var spanHandler = (navigator.appName == 'Microsoft Internet Explorer' || navigator.appName == 'Netscape') ? '<span>' :null;
    // based on the selection make availablity to the user selection
    $("select[name$='TaxResidence']").each(function () {
        // fetch the selected country value
        var selectedValue = $(this).val();
        if (selectedValue == "USA") {
            // $("select[name$='TaxResidence']")
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option[value='USA_TIN']").unwrap(spanHandler).show();
        }                                                                             
        else if (selectedValue == "NZL") {                                            
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option[value='NZL_IRD']").unwrap(spanHandler).show();
        }                                                                             
        else if (selectedValue == "GBR") {                                            
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option[value='UK_UTR']").unwrap(spanHandler).show();
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option[value='UK_NIN']").unwrap(spanHandler).show();
        }
    });
};/// <reference path="ic.public.foreignTaxResidency.investorCertificationSummary.js" />
/// <reference path="~/Scripts/common.js" />
IC.Public.entityorganization = function () {
    this.init();
};

IC.Public.entityorganization.prototype = {
    init: function () {
        var isCampaign = $("#IsCampaignEnabled");
        if (isCampaign != undefined && isCampaign.length != 0 && isCampaign.val().toLowerCase() == "true") {
            IC.Public.HideMenu();
        }
        $("#validateTIN").hide();
        this.btnNext = $("#btnNext");
        this.btnNext.bind('click', { obj: this }, this.onNextClick);
        this.btnSkip = $("#btnSkip");
        
        $("#btnOk").bind('click', { obj: this }, this.onOkClick);
        
        this.btnSkip.bind('click', { obj: this }, this.onSkipClick);
        this.btnBack = $("#btnBack");
        this.btnBack.bind('click', { obj: this }, this.onBackClick);

        this.btnEditAddress = $(".editstreetaddress");
        this.btnEditAddress.bind('click', { obj: this }, this.onEditAddressClick);
        var taxCountry = $("[name='EntityOrganizationDetails.TaxCountries']");
        taxCountry.unbind('change');
        taxCountry.bind('change', { obj: this }, this.onChangeAction);

        this.RadioActionActionEntity = $("[name='EntityOrganizationDetails.ActionTaxPurpose']");
        this.RadioActionActionEntity.unbind("change");
        this.RadioActionActionEntity.bind("change", { obj: this }, this.onRadioChangeActionEntity);

        this.RadioActionExemptBeneficialOwners = $("[name='EntityOrganizationDetails.ExemptBeneficialOwnersSelect']");
        this.RadioActionExemptBeneficialOwners.unbind("change");
        this.RadioActionExemptBeneficialOwners.bind("change", { obj: this }, this.onChangeAction);

        this.RadioActionFinancialInstitutions = $("[name='EntityOrganizationDetails.FinancialInstitutionsSelect']");
        this.RadioActionFinancialInstitutions.unbind("change");
        this.RadioActionFinancialInstitutions.bind("change", { obj: this }, this.onChangeAction);

        this.RadioActionNonFinancialAccount = $("[name='EntityOrganizationDetails.NonFinancialAccountSelect']");
        this.RadioActionNonFinancialAccount.unbind("change");
        this.RadioActionNonFinancialAccount.bind("change", { obj: this }, this.onChangeAction);

        this.RadioActionNonFinancialEntity = $("[name='EntityOrganizationDetails.NonFinancialEntitySelect']");
        this.RadioActionNonFinancialEntity.unbind("change");
        this.RadioActionNonFinancialEntity.bind("change", { obj: this }, this.onChangeAction);

        this.RadioActionEntityAccountSelected = $("[name='EntityOrganizationDetails.EntityAccountSelected']");
        this.RadioActionEntityAccountSelected.unbind("change");
        this.RadioActionEntityAccountSelected.bind("change", { obj: this }, this.onChangeAction);

        this.TxtControlExemptOthers = $("#EntityOrganizationDetails_ExemptBeneficialOthers");
        this.TxtControlExemptOthers.unbind('keyup');
        this.TxtControlExemptOthers.bind("keyup", { obj: this }, this.onChangeAction);

        this.TxtControlForeignEntityTIN = $("#EntityOrganizationDetails_ForeignEntityTIN");
        this.TxtControlForeignEntityTIN.unbind('keyup');
        this.TxtControlForeignEntityTIN.bind("keyup", { obj: this }, this.onChangeAction);

        this.TxtControlLocalEntityABN = $("#EntityOrganizationDetails_CRSABN");
        this.TxtControlLocalEntityABN.unbind('keyup');
        this.TxtControlLocalEntityABN.bind("keyup", { obj: this }, this.onChangeAction);
        this.TxtControlLocalEntityABN.unbind('mouseup');
        this.TxtControlLocalEntityABN.bind("mouseup", { obj: this }, this.onCloseLinkIeABN);

        this.TxtControlMarketExchange = $("#EntityOrganizationDetails_MarketExchange");
        this.TxtControlMarketExchange.unbind('keyup');
        this.TxtControlMarketExchange.bind("keyup", { obj: this }, this.onChangeAction);
       
        this.TxtControlMarketExchange.unbind('mouseup');
        this.TxtControlMarketExchange.bind("mouseup", { obj: this }, this.onCloseLinkMarketExchange);

        this.TxtControlUniqueExchangeCode = $("#EntityOrganizationDetails_UniqueExchangeCode");
        this.TxtControlUniqueExchangeCode.unbind('keyup');
        this.TxtControlUniqueExchangeCode.bind("keyup", { obj: this }, this.onChangeAction);

        this.TxtControlUniqueExchangeCode.unbind('mouseup');
        this.TxtControlUniqueExchangeCode.bind("mouseup", { obj: this }, this.onCloseLinkUniqueExchangeCode);


        this.TxtControlNonFinancialOthers = $("#EntityOrganizationDetails_NonFinancialAccountOthers");
        this.TxtControlNonFinancialOthers.unbind('keyup');
        this.TxtControlNonFinancialOthers.bind("keyup", { obj: this }, this.onChangeAction);

        this.TxtControlNFETrust = $("#EntityOrganizationDetails_NFETrust");
        this.TxtControlNFETrust.unbind('keyup');
        this.TxtControlNFETrust.bind("keyup", { obj: this }, this.onChangeAction);

        this.TxtControlNFECooperative = $("#EntityOrganizationDetails_NFECooperative");
        this.TxtControlNFECooperative.unbind('keyup');
        this.TxtControlNFECooperative.bind("keyup", { obj: this }, this.onChangeAction);

        this.TxtControlNFERegisteredCharitable = $("#EntityOrganizationDetails_NFERegisteredCharitable");
        this.TxtControlNFERegisteredCharitable.unbind('keyup');
        this.TxtControlNFERegisteredCharitable.bind("keyup", { obj: this }, this.onChangeAction);

        this.TxtControlNFENonRegisteredCharitable = $("#EntityOrganizationDetails_NFENonRegisteredCharitable");
        this.TxtControlNFENonRegisteredCharitable.unbind('keyup');
        this.TxtControlNFENonRegisteredCharitable.bind("keyup", { obj: this }, this.onChangeAction);

        this.TxtControlNFEAssociation = $("#EntityOrganizationDetails_NFEAssociation");
        this.TxtControlNFEAssociation.unbind('keyup');
        this.TxtControlNFEAssociation.bind("keyup", { obj: this }, this.onChangeAction);

        this.IntialEvents = $("#btnIntial");
        this.IntialEvents.unbind('click');
        this.IntialEvents.bind("click", { obj: this }, this.validateNextButtonAndControlSelection);
        $('#btnIntial').trigger('click');

        if ($("input[name='EntityOrganizationDetails.EntityAccountSelected']:checked") != undefined && $("input[name='EntityOrganizationDetails.EntityAccountSelected']:checked").val() == "SAF")
            $(".starABN").show();
        else {
            $(".starABN").hide();
        }
        if ($("input[name='EntityOrganizationDetails.EntityAccountSelected']:checked") != undefined && $("input[name='EntityOrganizationDetails.EntityAccountSelected']:checked").val() == "PLC")
            $(".divPublicListedCompany").show();
        else {
            $(".divPublicListedCompany").hide();
        }

    },
    onCloseLinkIeABN: function (events) {
        //ABN issue closing....
        if ($("input[name='EntityOrganizationDetails.EntityAccountSelected']:checked").val() == "SAF") {
            var $input = $(this),
              oldValue = $input.val();
            // alert(oldValue)
            if (oldValue == "") return;

            //// When this event is fired after clicking on the clear button
            //// the value is not cleared yet. We have to wait for it.
            setTimeout(function () {
                var newValue = $input.val();
                //     alert(oldValue)

                if (newValue == "") {
                    var _obj = events.data.obj;
                    _obj.TxtControlLocalEntityABN.trigger("keyup");
                }
            }, 1);
        }
    },
    onCloseLinkMarketExchange: function (events) {
        //ABN issue closing....
        if ($("input[name=EntityOrganizationDetails.EntityAccountSelected]:checked").val() == "PLC") {
            var $input = $(this),
              oldValue = $input.val();
            // alert(oldValue)
            if (oldValue == "") return;

            //// When this event is fired after clicking on the clear button
            //// the value is not cleared yet. We have to wait for it.
            setTimeout(function () {
                var newValue = $input.val();
                //     alert(oldValue)

                if (newValue == "") {
                    var _obj = events.data.obj;
                    _obj.TxtControlMarketExchange.trigger("keyup");
                }
            }, 1);
        }
    },

    onCloseLinkUniqueExchangeCode: function (events) {
        //ABN issue closing....
        if ($("input[name=EntityOrganizationDetails.EntityAccountSelected]:checked").val() == "PLC") {
            var $input = $(this),
              oldValue = $input.val();
            // alert(oldValue)
            if (oldValue == "") return;

            //// When this event is fired after clicking on the clear button
            //// the value is not cleared yet. We have to wait for it.
            setTimeout(function () {
                var newValue = $input.val();
                //     alert(oldValue)

                if (newValue == "") {
                    var _obj = events.data.obj;
                    _obj.TxtControlUniqueExchangeCode.trigger("keyup");
                }
            }, 1);
        }
    },
    //checkDeceased: function (obj) {
    //    if ($("[name='InvestorSummaryDetails.ActionDeceased'][value='0']").is(':checked')) {
    //        if (_obj.checkDeceased(_obj)) {
    //            // alert('Not provided the required documents.');
    //            _obj.disPlayMessage("ForeignTaxResidency_Investorsummary_Deceased_Message");
    //        }
    //    }
    //},
    onSkipClick: function (events) {
        var title = 'Entity / Organization Details';
        var displayResource = 'ForeignTax_Residency_InvestorSummary_SkipMessage';
        var formName = "#entityOrganizationForm";
        var modal = new IC.Public.foreignTaxResidencyMessage();
        modal.showModal(title, displayResource, formName);
    },
    onBackClick: function (events) {
        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        $("#EntityOrganizationDetails_CheckValidation").val('false');
        var form = $("#entityOrganizationForm");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorBack");
        //form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        //}
    },
    onEditAddressClick: function (events) {
        var urlConfirm = OC.MVC.util.getLink("Holding", "Index");
        var modal = new IC.Public.investorAddressDetails();
        modal.showModal(urlConfirm, true);
    },
    onRadioChangeActionEntity: function (events) {
        if (($("[name='EntityOrganizationDetails.ActionTaxPurpose'][value='1']").is(':checked'))) {
        }
        var _obj = events.data.obj;
        _obj.validateNextButtonAndControlSelection(events);
    },
    onChangeAction: function (events) {
        var _obj = events.data.obj;
        
        if ($("input[name='EntityOrganizationDetails.EntityAccountSelected']:checked") != undefined && $("input[name='EntityOrganizationDetails.EntityAccountSelected']:checked").val() == "SAF")
            $(".starABN").show();
        else {
            $(".starABN").hide();
        }
        if ($("input[name='EntityOrganizationDetails.EntityAccountSelected']:checked") != undefined && $("input[name='EntityOrganizationDetails.EntityAccountSelected']:checked").val() == "PLC")
            $(".divPublicListedCompany").show();
        else {
            $(".divPublicListedCompany").hide();
        }

        if ($(this).attr("type") == "radio") {
            // hide all instruction, if only selected heading instruction
            /* $("ul[name=radioMainAction]").hide();
            $(this).parent().parent().find("ul").show();
            if ($(this).val() == "SAF")
                $("#divLocalABNACN").show();
            else {
                $("#divLocalABNACN").hide();
                $("input[name=EntityOrganizationDetails.LocalEntityABN]").val("");
            }
             */
            if ($(this).val() == "DEC") {
                //if ($("#IsDeceasedOscar").val() == "true" || $("#IsDeceasedOscar").val() == "True") {
                    // alert('Not provided the required documents.');
                    //_obj.disPlayMessage("ForeignTaxResidency_Investorsummary_Deceased_Message");
                    _obj.disPlayMessage("ForeignTaxResidency_Investorsummary_Deceased_Message_YES_WARNING");
                //}
            }
        }

        _obj.validateNextButtonAndControlSelection(events);
    },
    disPlayMessage: function (value) {

        var title = 'Investor Summary';
        var displayResource = value;
        var formName = "GeneralMessage";
        var modal = new IC.Public.foreignTaxResidencyMessage();
        modal.showModal(title, displayResource, formName);
    },
    validateNextButtonAndControlSelection: function (events) {
        var _obj = events.data.obj;
        var isActionValid = false;
        //, isEntityAccountValid = false;
        isActionValid = _obj.checkActionEntityRelatedValidation();
        //isEntityAccountValid = _obj.checkValidateEntityOrganizationAccounts();
        //if (($("[name='EntityOrganizationDetails.TaxCountries']").val() != '')
        //    && ($("[name='EntityOrganizationDetails.ForeignEntityTIN']").val() != '')
        //    && ($("[name='EntityOrganizationDetails.LocalEntityABN']").val() != '')
        //    && _obj.checkActionEntityRelatedValidation()
        //    && _obj.checkValidateEntityOrganizationAccounts()
        //    ) {
        //    isValid = true;
        //}
        if (isActionValid)// && isEntityAccountValid)
            _obj.btnNext.parent().removeClass('disabled');
        else
            _obj.btnNext.parent().addClass('disabled');
    },
    checkActionEntityRelatedValidation: function () {
        // Should select any one of the option
        var temp = true;
        if ($("input[name='EntityOrganizationDetails.EntityAccountSelected']:checked").length != 0) {
            // If superannuation fund then Local ABN is mandatory
            if ($("input[name='EntityOrganizationDetails.EntityAccountSelected']:checked").val() == "SAF" && $("input[name='EntityOrganizationDetails.CRSABN']").val().trim() == "")
            {
                temp = temp && false;
            }
            if ($("input[name='EntityOrganizationDetails.EntityAccountSelected']:checked").val() == "PLC" && ($("input[name='EntityOrganizationDetails.MarketExchange']").val().trim() == "" || $("input[name='EntityOrganizationDetails.UniqueExchangeCode']").val().trim() == "")) {
                temp = temp && false;
            }
            return temp;
        }
        
        return false;
    },
    checkValidateEntityOrganizationAccounts: function () {

        var isValid = false;
        $("[name='radioMainAction']").show();
        $("[radioGrp='radioEntity']").each(function () {
            var subChildValue = $(this).attr('childGrp');
            //var txtControl = $("[displaychildgrp=" + subChildValue + "]").parent().children().last();

            if ($(this).is(':checked')) {
                $("[displaychildgrp='" + subChildValue + "']").show();
                $("[name='" + subChildValue + "']").each(function () {
                    var txtControl = $(this).parent().children().last();
                    $(txtControl).show();
                    if ($(this).is(':checked')) {
                        if ($(txtControl).attr('type') == 'text') {
                            $(txtControl).show();
                            if ($(txtControl).val() != '') {
                                isValid = true;
                            }
                        } else {
                            isValid = true;
                        }
                    } else {
                        if ($(txtControl).attr('type') == 'text') {
                            $(txtControl).val('');
                            $(txtControl).hide();
                        }
                    }
                });
            } else {
                $("[name='" + subChildValue + "']").attr('checked', false);
                $("[displaychildgrp='" + subChildValue + "']").hide();
            }
        });
        return isValid;

    },
    //onChangeRadioEntity: function (events) {
    //    //$("[radioGrp=radioEntity]").each(function () {
    //    //    $(this).attr('checked', false);
    //    //    $(this).attr('value', "False")
    //    //});
    //    //$(this).attr('checked', true);
    //    //$(this).attr('value', "True")
    //    var _obj = events.data.obj;
    //    _obj.validateNextButtonAndControlSelection(events);
    //},
    onNextClick: function (events) {
        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        var form = $("#entityOrganizationForm");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorNext");
        //form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
        if ($("#EntityOrganizationDetails_EntityAccountSelected:checked").val().trim() == "SAF") {
            if ($("#EntityOrganizationDetails_CRSABN").val().trim() == "") {
                $("#EntityOrganizationDetails_CRSABN").addClass("input-validation-error");
                return false;
            }
            else {
                $("#EntityOrganizationDetails_CRSABN").removeClass("input-validation-error");
            }
        }
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
           
        });
        //}
    },
    onOkClick: function (events) {
        $.modal.close();
    }
};

function validateTINForEntity(textValue) {
    var taxResidence = $("#EntityOrganizationDetails_TaxCountries").val();
    var taxIdType = "TIN";
    validateTIN(textValue, taxResidence, taxIdType);
       
    }
;/// <reference path="ic.public.foreignTaxResidency.investorCertificationSummary.js" />
/// <reference path="~/Scripts/common.js" />
IC.Public.financialinstitutions = function () {
    this.init();

    for (var property in inputMask) {
        if (inputMask.hasOwnProperty(property)) {
            InputMask.prototype[property] = inputMask[property];
        }
    }

    //  Declaritive initalization
    new InputMask();
}

IC.Public.financialinstitutions.prototype = {
    init: function () {
        var isCampaign = $("#IsCampaignEnabled");
        if (isCampaign != undefined && isCampaign.length != 0 && isCampaign.val().toLowerCase() == "true") {
            IC.Public.HideMenu();
        }
        this.ddfiStatus = $("select#EntityOrganizationDetails_FinancialInstitutions_FinancialInstitutionStatus");
        this.ddfiStatus.bind("change", { obj: this }, this.fiStausChange);

        this.rdbGIINQuiz = $("input[name='EntityOrganizationDetails.FinancialInstitutions.GINOption']");
        this.rdbGIINQuiz.bind("change", { obj: this }, this.GINOptionChange);

        var rdbNonCRS = $("input[name='EntityOrganizationDetails.FinancialInstitutions.FINonCRS']");
        rdbNonCRS.bind('change', { obj: this }, this.onChange);

        var txtOthers = $("#EntityOrganizationDetails_FinancialInstitutions_FISOthers");
        txtOthers.bind('keyup', { obj: this }, this.onChange);

        var rdbRegisteredHolder = $("input[name='EntityOrganizationDetails.FinancialInstitutions.IsRegisteredHolders']");
        rdbRegisteredHolder.bind('change', { obj: this }, this.onChange);

        var txtGIIN= $("#EntityOrganizationDetails_FinancialInstitutions_GIIN");
        txtGIIN.bind('keypress', { obj: this }, this.txtGIINKeyPress);

        $("#validateTIN").hide();
        // Onload - invoke page validation
        IC.Public.financialinstitutions.prototype.onChange(this);
        
        this.btnNext = $("#btnNext");
        this.btnNext.bind('click', { obj: this }, this.onNextClick);

        this.btnSkip = $("#btnSkip");
        this.btnSkip.bind('click', { obj: this }, this.onSkipClick);

        this.btnBack = $("#btnBack");
        this.btnBack.bind('click', { obj: this }, this.onBackClick);

        $("#btnOk").bind('click', { obj: this }, this.onOkClick);


        //If “Yes”, enable GIIN control to capture GIIN; clear & hide the FFI status controls (if visible)
        if ($("input[name='EntityOrganizationDetails.FinancialInstitutions.GINOption']:checked").val() == "1") {
            $(".giin-row").show();
            $(".EntityOrganizationDetails_FinancialInstitutions_FISOthers").val("");

            $(".giin-fistatus").hide();
        }
        else if ($("input[name='EntityOrganizationDetails.FinancialInstitutions.GINOption']:checked").val() == "0") {//If “No”, clear and hide GIIN controls; enable the Foreign Financial Institution (FFI) status controls;
            $("#EntityOrganizationDetails_FinancialInstitutions_GIIN").val("");
            $(".giin-row").hide();
            $(".giin-fistatus").show();
        }

        if ($("#EntityOrganizationDetails_FinancialInstitutions_FinancialInstitutionStatus").val() == "OTH") {
            $("input#EntityOrganizationDetails_FinancialInstitutions_FISOthers").show();
        }
        else { $("input#EntityOrganizationDetails_FinancialInstitutions_FISOthers").hide(); }

        /* this.btnAddMore = $("#btnAddMore");
         this.btnAddMore.bind('click', { obj: this }, this.onAddMoreClick);
 
         
         this.RadioActionActionFinancialInstitutionTax = $("[name='EntityOrganizationDetails.FinancialInstitutions.TaxID']");
         this.RadioActionActionFinancialInstitutionTax.unbind("change");
         this.RadioActionActionFinancialInstitutionTax.bind("change", { obj: this }, this.onRadioTaxChange);
 
         this.TxtControlFFIStatus = $("[name=EntityOrganizationDetails.FinancialInstitutions.FFIStatus]");
         this.TxtControlFFIStatus.unbind('keyup');
         this.TxtControlFFIStatus.bind("keyup", { obj: this }, this.onChange);
 
         this.RadioActionActionFFI = $("[name=EntityOrganizationDetails.FinancialInstitutions.FFI]");
         this.RadioActionActionFFI.unbind("change");
         this.RadioActionActionFFI.bind("change", { obj: this }, this.onChange);
 
         var taxResidence = $("[typeValue=TaxRes]");
         taxResidence.unbind('change');
         taxResidence.bind('change', { obj: this }, this.onChange);
         //taxResidence.each(function () {
         //    $(this).trigger('change');
         //});
 
         var removeCntrlPerson = $("[typeValue=removeCntrlPerson]");
         removeCntrlPerson.unbind('click');
         removeCntrlPerson.bind("click", { obj: this }, this.onRemoveControllingPersonClick);
 
         if ($("ul [disPlay='block']").length <= 1) {
             $("ul [typeValue='removeCntrlPerson']").children().first().addClass('disabledLinkButton');
         }
 
         var taxIdentificationType = $("[typeValue=TaxTyp]");
         taxIdentificationType.unbind('change');
         taxIdentificationType.bind('change', { obj: this }, this.onChange);
 
         this.TFNTxtControl = $("[typeValue=TFNTxt]");
         this.TFNTxtControl.unbind('keyup');
         this.TFNTxtControl.bind("keyup", { obj: this }, this.onABNKeyUpClick);
 
         //$("[typeValue=TFNTxt]").focus(function () {
         //    var that = this;
         //    setTimeout(function () { that.selectionStart = that.selectionEnd = 10000; }, 0);
         //});
 
         this.IntialEvents = $("#btnIntial");
         this.IntialEvents.unbind('click');
         this.IntialEvents.bind("click", { obj: this }, this.validateNextButtonAndControlSelection);
         $('#btnIntial').trigger('click');
         */
    },
    txtGIINKeyPress: function (e) { IC.Public.financialinstitutions.prototype.onChange(e); inputMask.handleValueChange.call(this, e); },
    onChange: function (events) {
        if (events != undefined) {
            var _obj = (events.data == undefined || events.data == "" ? this : events.data.obj);// events.data.obj;
        _obj.validateNextButtonAndControlSelection(events);
        }
    },
    validateNextButtonAndControlSelection: function (events) {
        //var _obj = (events.data == undefined ? this : events.data.obj);//events.data.obj;
        var isValid = true;
        //If GIIN option isi “Yes”,  GIIN control is mandatory
        if ($("input[name='EntityOrganizationDetails.FinancialInstitutions.GINOption']:checked").val() == "1") {
            if ($("#EntityOrganizationDetails_FinancialInstitutions_GIIN").val() == "")
                isValid = false;
            else if($("#EntityOrganizationDetails_FinancialInstitutions_GIIN").val().length<19)
                isValid = false;
        }
        else if ($("input[name='EntityOrganizationDetails.FinancialInstitutions.GINOption']:checked").val() == "0") {
            if ($("select#EntityOrganizationDetails_FinancialInstitutions_FinancialInstitutionStatus").val() == "") {
                isValid = false;
            }
            if ($("select#EntityOrganizationDetails_FinancialInstitutions_FinancialInstitutionStatus").val() == "OTH") {
                if ($("input#EntityOrganizationDetails_FinancialInstitutions_FISOthers").val() == "")
                    isValid = false;
            }
        }
        else {
            isValid = false;
        }
        if ($("input[name='EntityOrganizationDetails.FinancialInstitutions.FINonCRS']:checked").length == 0)
            isValid = false;
        // IF DA Account then controll should be display and mandatoryly get option
        if ($("input[name='EntityOrganizationDetails.FinancialInstitutions.IsRegisteredHolders']").length > 0 && $('input[name="EntityOrganizationDetails.FinancialInstitutions.IsRegisteredHolders"]:checked').length == 0)
            isValid = false;


        if (isValid) {
            $("#btnNext").parent().removeClass('disabled');
        }
        else
            $("#btnNext").parent().addClass('disabled');
    },
    GINOptionChange: function (events) {
        //If “Yes”, enable GIIN control to capture GIIN; clear & hide the FFI status controls (if visible)
        if ($(this).val() == "1") {
            $(".giin-row").show();
            $(".EntityOrganizationDetails_FinancialInstitutions_FISOthers").val("");

            $(".giin-fistatus").hide();
        }
        else {//If “No”, clear and hide GIIN controls; enable the Foreign Financial Institution (FFI) status controls;
            $("#EntityOrganizationDetails_FinancialInstitutions_GIIN").val("");
            $(".giin-row").hide();
            $(".giin-fistatus").show();
        }
        IC.Public.financialinstitutions.prototype.onChange(events);
    },
    fiStausChange: function (events) {
        if ($(this).val() == "OTH") {
            $("input#EntityOrganizationDetails_FinancialInstitutions_FISOthers").show();
        }
        else { $("input#EntityOrganizationDetails_FinancialInstitutions_FISOthers").hide(); }
        IC.Public.financialinstitutions.prototype.onChange(events);
    }
    ,
    onABNKeyUpClick: function (events) {
        var _obj = events.data.obj;
        _obj.validateNextButtonAndControlSelection(events);
        var value = $(this).val();
        $(this).focus().val(value);
    },
    onRemoveControllingPersonClick: function (events) {
        if ($(this).children().first().hasClass('disabledLinkButton')) {
            events.preventDefault();
            return;
        }
        var sequence = $(this).attr('countVal');
        $("[name='IndexActioned']").val(sequence);
        //$(this).parents('li').hide();
        var form = $("#financialInstitution");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "RemoveInvestorDetails");


        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            // cache:false,
            success: function (result) {
                //$("#" + OC.MVC.constants.mainContainer).html('');
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    onRadioTaxChange: function (events) {
        $("[typeValue='TaxTyp']").each(function () {
            $(this).val('');
        });
        var residencyCount = 0;
        $("[typeValue='TaxRes']").each(function () {
            $(this).val('');
        });
        $("[typeValue='TFNTxt']").each(function () {
            $(this).val('');
        });
        var _obj = events.data.obj;
        _obj.validateNextButtonAndControlSelection(events);
    },
    processTrueTaxID: function () {
        $("[name='EntityOrganizationDetails.FinancialInstitutions.FFI']").attr('disabled', 'disabled');
        $("[name='EntityOrganizationDetails.FinancialInstitutions.FFIStatus']").hide();
        $("[name='EntityOrganizationDetails.FinancialInstitutions.FFIStatus']").val('');
    },
    processFalseTaxID: function () {
        var isValid = false;
        if ($("[name='EntityOrganizationDetails.FinancialInstitutions.FFI']").val() == 'OTH') {
            $("[name='EntityOrganizationDetails.FinancialInstitutions.FFIStatus']").show();
            if ($("[name='EntityOrganizationDetails.FinancialInstitutions.FFIStatus']").val() != '') {
                isValid = true;
            }
        } else {
            if ($("[name='EntityOrganizationDetails.FinancialInstitutions.FFI']").val() != '') {
                $("[name='EntityOrganizationDetails.FinancialInstitutions.FFIStatus']").hide();
                $("[name='EntityOrganizationDetails.FinancialInstitutions.FFIStatus']").val('');
                isValid = true;
            }
        }
        return isValid;
    },
    RemoveDisableTaxDetails: function () {
        //$("[typeValue='TaxTyp']").val('');
        //  $("[typeValue='TaxRes']").val('');
        //  $("[typeValue='TFNTxt']").val('');

        $("[typeValue='TaxTyp']").removeAttr('disabled');
        $("[typeValue='TaxRes']").removeAttr('disabled');
        $("[typeValue='TFNTxt']").removeAttr('disabled');
    },
    checkTaxValidation: function () {
        var isValidTaxResidence = true, isValidTFNTxt = true, isValidTFNType = true, isValidPOB = true;
        isValidDOB = true;
        //var isDisplayUsCitizen = false;

        $("[typeValue='TaxTyp']").each(function () {
            if ($(this).val() == '') {
                isValidTFNType = false;
            }
        });

        var residencyCount = 0;
        $("[typeValue='TaxRes']").each(function () {
            residencyCount += 1;
            if ($(this).val() == '') {
                isValidTaxResidence = false;
            }
        });

        if (residencyCount > 1) {
            $("[name='EntityOrganizationDetails.FinancialInstitutions.TaxID']").attr('disabled', 'disabled');
        } else
            $("[name='EntityOrganizationDetails.FinancialInstitutions.TaxID']").removeAttr('disabled');

        $("[typeValue='TFNTxt']").each(function () {
            if ($(this).val() == '') {
                isValidTFNTxt = false;
            }
        });
        return (isValidTaxResidence && isValidTFNTxt && isValidTFNType); //&& isValidPOB && isValidDOB);
    },
    onNextClick: function (events) {
        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        var form = $("#financialInstitution");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorNext");
        //form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        //}
    },
    onSkipClick: function (events) {
        var title = 'Financial Institutions';
        var displayResource = 'ForeignTax_Residency_InvestorSummary_SkipMessage';
        var formName = "#financialInstitution";
        var modal = new IC.Public.foreignTaxResidencyMessage();
        modal.showModal(title, displayResource, formName);
    },
    onBackClick: function (events) {
        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        var form = $("#financialInstitution");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorBack");
        //form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        //}
    },
    onAddMoreClick: function (events) {

        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled') || !_obj.checkTaxValidation()) {
            events.preventDefault();
            return;
        }
        var form = $("#financialInstitution");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "AddMoreFatcaTaxDetails");
        form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        //}
    },
    onOkClick: function (events) {
        $.modal.close();
    }
};

function validateTINForFinancialEntity(textValue) {
    var id = $(textValue).attr("idval");
    var taxResidence = $("#EntityOrganizationDetails_FinancialInstitutions_InvestorTaxDetails_" + id + "__TaxResidence").val();
    var taxIdType = $("#EntityOrganizationDetails_FinancialInstitutions_InvestorTaxDetails_" + id + "__TaxIdentificationType").val();
    validateTIN(textValue, taxResidence, taxIdType);

}




// Mask input

var InputMask = function (opts) {
    if (opts && opts.masked) {
        // Make it easy to wrap this plugin and pass elements instead of a selector
        opts.masked = typeof opts.masked === string ? document.querySelectorAll(opts.masked) : opts.masked;
    }

    if (opts) {
        this.opts = {
            masked: opts.masked || document.querySelectorAll(this.d.masked),
            mNum: opts.mNum || this.d.mNum,
            mChar: opts.mChar || this.d.mChar,
            mAlpha: opt.mAlpha || this.d.mAlpha,
            error: opts.onError || this.d.onError
        }
    } else {
        this.opts = this.d;
        this.opts.masked = $(".masked");// document.querySelectorAll(this.opts.masked);
    }

    this.refresh(true);
};

var inputMask = {
    
    // Default Values
    d: {
        masked: '.masked',
        mNum: 'XdDmMyY9',
        mChar: '_',
        mAlpha: 'Y',
        onError: function () { }
    },

    refresh: function (init) {
        var t, parentClass;

        if (!init) {
            this.opts.masked = document.querySelectorAll(this.opts.masked);
        }

        for (i = 0; i < this.opts.masked.length; i++) {
            t = this.opts.masked[i]
            parentClass = t.parentNode.getAttribute('class');
            
            if (!parentClass || (parentClass && parentClass.indexOf('shell') === -1)) {
                this.createShell(t);
                this.activateMasking(t);
            }
        }
    },

    // replaces each masked t with a shall containing the t and it's mask.
    createShell: function (t) {
        var wrap = document.createElement('span'),
            mask = document.createElement('span'),
            emphasis = document.createElement('i'),
            tClass = t.getAttribute('class'),
            pTxt = t.getAttribute('placeholder'),
            placeholder = document.createTextNode(pTxt);

        t.setAttribute('maxlength', placeholder.length);
        t.setAttribute('data-placeholder', pTxt);
        t.removeAttribute('placeholder');


        if (!tClass || (tClass && tClass.indexOf('masked') === -1)) {
            t.setAttribute('class', tClass + ' masked');
        }

        mask.setAttribute('aria-hidden', 'true');
        mask.setAttribute('id', t.getAttribute('id') + 'Mask');
        mask.appendChild(emphasis);
        mask.appendChild(placeholder);

        wrap.setAttribute('class', 'shell');
        wrap.appendChild(mask);
        t.parentNode.insertBefore(wrap, t);
        wrap.appendChild(t);
    },

    setValueOfMask: function (e) {
        var value = e.target.value,
            placeholder = e.target.getAttribute('data-placeholder');

        return "<i>" + value + "</i>" + placeholder.substr(value.length);
    },

    // add event listeners
    activateMasking: function (t) {
        var that = this;
        if (t.addEventListener) { // remove "if" after death of IE 8
            t.addEventListener('keyup', function (e) {
                that.handleValueChange.call(that, e);
                IC.Public.financialinstitutions.prototype.onChange(e);
            }, false);
        } else if (t.attachEvent) { // For IE 8
            t.attachEvent('onkeyup', function (e) {
                e.target = e.srcElement;
                that.handleValueChange.call(that, e);
                IC.Public.financialinstitutions.prototype.onChange(e);
            });
        }
    },

    handleValueChange: function (e) {
        var id = "EntityOrganizationDetails_FinancialInstitutions_GIIN";// e.target.getAttribute('id');

        if (e.target.value == document.querySelector('#' + id + 'Mask i').innerHTML) {
            return; // Continue only if value hasn't changed
        }

        document.getElementById(id).value = this.handleCurrentValue(e);
        document.getElementById(id + 'Mask').innerHTML = this.setValueOfMask(e);
        
        // Check GIIN validation and disaply the warning Popup
        if ($("#EntityOrganizationDetails_FinancialInstitutions_GIIN").val().length == 19) {
            IC.Public.showLoading("");
            // CRS verification
            var crsValidationURL = OC.MVC.util.getLink("ForeignTaxResidency", "ValidateABNorGIIN");
            $.ajax({
                url: crsValidationURL,//CRSValidation
                type: 'POST',
                data: { validationNumber: $("#EntityOrganizationDetails_FinancialInstitutions_GIIN").val().toUpperCase(), validationType: $("#ValidationType").val() },
                success: function (result) {
                    if (result != undefined && !result.Valid) {
                        // Show popup
                        var title = 'GIIN Validation';
                        var displayResource = 'ForeignTax_Residency_GIIN_InvalidWarning';
                        var formName = "GeneralMessage";
                        var modal = new IC.Public.foreignTaxResidencyMessage();
                        IC.Public.hideLoading();
                        modal.showModal(title, displayResource, formName);
                    }
                    else {
                        IC.Public.hideLoading();
                    }
                    
                }
            });
        }

    },

    handleCurrentValue: function (e) {
        var isCharsetPresent = e.target.getAttribute('data-charset'),
            placeholder = isCharsetPresent || e.target.getAttribute('data-placeholder'),
            value = e.target.value, l = placeholder.length, newValue = '',
            i, j, isInt, isLetter, strippedValue, isAlpha;

        // strip special characters
        strippedValue = isCharsetPresent ? value.replace(/\W/g, "") : value.replace(/\D/g, "");

        for (i = 0, j = 0; i < l; i++) {
            isInt = !isNaN(parseInt(strippedValue[j]));
            isLetter = strippedValue[j] ? strippedValue[j].match(/[A-NP-Z0-9]/i) : false;
            isAlpha = strippedValue[j] ? strippedValue[j].match(/[A-NP-Z0-9]/i) : false;
            matchesNumber = this.opts.mNum.indexOf(placeholder[i]) >= 0;
            matchesLetter = this.opts.mChar.indexOf(placeholder[i]) >= 0;
            matchesAlpha = this.opts.mChar.indexOf(placeholder[i]) >= 0;

            if ((matchesNumber && isInt) || (isCharsetPresent && matchesLetter && isLetter)) {
                newValue += strippedValue[j++];
            } else if ((!isCharsetPresent && !isInt && matchesNumber) || (isCharsetPresent && ((matchesLetter && !isLetter) || (matchesNumber && !isInt)))) {
                //this.opts.onError( e ); // write your own error handling function
                return newValue;
            } else {
                newValue += placeholder[i];
            }
            // break if no characters left and the pattern is non-special character
            if (strippedValue[j] == undefined) {
                break;
            }
        }
        return newValue;
    }
};
;/// <reference path="ic.public.foreignTaxResidency.investorCertificationSummary.js" />
/// <reference path="~/Scripts/common.js" />
IC.Public.nonfinancialentities = function () {
    this.init();
}

IC.Public.nonfinancialentities.prototype = {
    init: function () {
        var isCampaign = $("#IsCampaignEnabled");
        if (isCampaign != undefined && isCampaign.length != 0 && isCampaign.val().toLowerCase() == "true") {
            IC.Public.HideMenu();
        }
        taxResidenceOnPageLoad();
        //var divContent = '';
        //$("[rowcount='true']").each(function () {
        //    divContent += $(this).html();
        //    $(this).remove();
        //});
        var checkTaxValidity = this.checkTaxValidation(this);
        if ($("[disPlay='ForeignTax']").length == 1) {
            if (checkTaxValidity) {
                $("[name='EntityOrganizationDetails.NonFinancialEntities.ActionMoreJurisdiction'][value='0']").attr('checked', true);
            }
            else {
                $("[name='EntityOrganizationDetails.NonFinancialEntities.ActionMoreJurisdiction']").attr('checked', false);
            }
        }
        //$('#divAddMoreJurisdiction').after(divContent);

        $("#validateTIN").hide();

        this.btnNext = $("#btnNext");
        this.btnNext.bind('click', { obj: this }, this.onNextClick);

        this.btnSkip = $("#btnSkip");
        this.btnSkip.bind('click', { obj: this }, this.onSkipClick);

        this.btnAddMore = $("#btnAddMore");
        this.btnAddMore.bind('click', { obj: this }, this.onAddMorePassiveClick);

        $("#btnOk").bind('click', { obj: this }, this.onOkClick);

        this.btnBack = $("#btnBack");
        this.btnBack.bind('click', { obj: this }, this.onBackClick);

        this.pagessiveControls = $(".firstName,.lastName,#dateOfBirth,.placeOfBirth");
        this.pagessiveControls.unbind("keyup");
        this.pagessiveControls.bind("keyup", { obj: this }, this.onChange);

        this.RadioActionActionPassiveNFE = $("[name='EntityOrganizationDetails.NonFinancialEntities.ActivePassiveNFE']");
        this.RadioActionActionPassiveNFE.unbind("change");
        this.RadioActionActionPassiveNFE.bind("change", { obj: this }, this.onChange);

        this.RadioActionActionPassiveNFEBeneficialOwners = $("[name='EntityOrganizationDetails.NonFinancialEntities.ActionPassiveNFEBeneficialOwners']");
        this.RadioActionActionPassiveNFEBeneficialOwners.unbind("change");
        this.RadioActionActionPassiveNFEBeneficialOwners.bind("change", { obj: this }, this.onChange);
        this.RadioActionActionPassiveNFEBeneficialOwners.attr("disabled");

        this.RadioActionMoreStreetAddress = $("[name='EntityOrganizationDetails.NonFinancialEntities.ActionStreetAddress']");
        this.RadioActionMoreStreetAddress.unbind("change");
        this.RadioActionMoreStreetAddress.bind("change", { obj: this }, this.validateNextButtonAndControlSelection);

        this.txtLegalEntityName = $("#EntityOrganizationDetails_NonFinancialEntities_LegalEntityName");
        this.txtLegalEntityName.unbind("keyup");
        this.txtLegalEntityName.bind("keyup", { obj: this }, this.validateNextButtonAndControlSelection);


        this.editStreetAddress = $(".IsSameAsResidencial");//$("input[name=EntityOrganizationDetails.NonFinancialEntities.ActionStreetAddress]");
        this.editStreetAddress.unbind('change');
        this.editStreetAddress.bind("change", { obj: this }, this.editStreeePopup);

        this.RadioActionMoreJurisdiction = $("[name='EntityOrganizationDetails.NonFinancialEntities.ActionMoreJurisdiction']");
        this.RadioActionMoreJurisdiction.unbind("change");
        this.RadioActionMoreJurisdiction.bind("change", { obj: this }, this.validateNextButtonAndControlSelection);
        /**************************************added tax details*****************************************/

        var taxResidence = $("[typeValue='TaxRes']");
        taxResidence.unbind('change');
        taxResidence.bind('change', { obj: this }, this.onTaxResidenceChange);

        this.btnEditAddress = $(".editstreetaddress");
        this.btnEditAddress.bind('click', { obj: this }, this.onEditAddressClick);


        var taxJurisdiction = $("[typeValue='Jurisdiction']");
        taxJurisdiction.unbind('change');
        taxJurisdiction.bind('change', { obj: this }, this.validateNextButtonAndControlSelection);
        //taxResidence.each(function () {
        //    $(this).trigger('change');
        //});

        var taxIdentificationType = $("[typeValue='TaxTyp']");
        taxIdentificationType.unbind('change');
        taxIdentificationType.bind('change', { obj: this }, this.onChangeActionActiveTaxType);

        this.tinNumber = $("[typeValue='TFNTxt']");
        this.tinNumber.unbind('keyup');
        this.tinNumber.bind("keyup", { obj: this }, this.validateNextButtonAndControlSelection);

        this.btnAddMoreTaxDetails = $("#btnAddMoreTaxDetails");
        this.btnAddMoreTaxDetails.unbind('click');
        this.btnAddMoreTaxDetails.bind('click', { obj: this }, this.onAddMoreTaxDetailsClick);

        var removeCntrlPerson = $("[typeValue='removeTax']");
        removeCntrlPerson.unbind('click');
        removeCntrlPerson.bind("click", { obj: this }, this.onRemoveTaxDetailsClick);

        if ($("[disPlay='ForeignTax']").length == 1) {
            $("ul [typeValue='removeTax']").children().first().addClass('disabledLinkButton');
        }

        //$("[typeValue='AddMoreTaxPassive']").each(function () {
        //    $(this).attr('tempDate', $(this).val())
        //});
        var taxValidationPassive = this.checkTaxValidationPassive(this);
        $("[typeValue='AddMoreTaxPassive']").each(function () {
            var taxCount = parseInt($(this).attr("taxCount"));
            var finalCount = $(this).attr("finalCount");
            if (taxCount == 1) {
                $("#btnDeleteTaxDetails" + "" + (taxCount - 1) + "" + finalCount).addClass('disabledLinkButton');
                $(this).addClass('disabledLinkButton');
                
                if (taxValidationPassive) {
                    $("[name='EntityOrganizationDetails.NonFinancialEntities.ControllingPersonDetails[" + finalCount + "].ActionMoreJurisdiction'][value='0']").attr("checked", "true");
                }
                else {
                    if ($("[name='EntityOrganizationDetails.NonFinancialEntities.ControllingPersonDetails[" + finalCount + "].InvestorTaxDetails[" + (taxCount - 1) + "].TaxResidence']").val() == '' &&
                       $("[name='EntityOrganizationDetails.NonFinancialEntities.ControllingPersonDetails[" + finalCount + "].InvestorTaxDetails[" + (taxCount - 1) + "].TaxIdentificationType']").val() == '' &&
                       $("[name='EntityOrganizationDetails.NonFinancialEntities.ControllingPersonDetails[" + finalCount + "].InvestorTaxDetails[" + (taxCount - 1) + "].TIN']").val() == '') {
                        $("[name='EntityOrganizationDetails.NonFinancialEntities.ControllingPersonDetails[" + finalCount + "].ActionMoreJurisdiction']").attr('checked', false);
                    }
                    else {
                        $("[name='EntityOrganizationDetails.NonFinancialEntities.ControllingPersonDetails[" + finalCount + "].ActionMoreJurisdiction'][value='0']").attr("checked", "true");
                    }
                }
                // It is by default it was selecting the option
                //$("[name='EntityOrganizationDetails.NonFinancialEntities.ControllingPersonDetails[" + finalCount + "].ActionMoreJurisdiction'][value='0']").attr("checked", "true");
            }
        });

        //temp need to remove
        //$("[name=EntityOrganizationDetails.NonFinancialEntities.ActivePassiveNFE][value='0']").attr("disabled", "disabled");
        /**************************************added tax details*****************************************/

        /**************************************added controlling persons*****************************************/
        if ($("ul [disPlay='remCntrl']").length == 1) {
            $("ul [typeValue='removeCntrlPerson']").children().first().addClass('disabledLinkButton');
        }

        var AddMoreTaxPassive = $("[typeValue='AddMoreTaxPassive']");
        AddMoreTaxPassive.unbind('click');
        AddMoreTaxPassive.bind('click', { obj: this }, this.onAddMoreTaxDetailsPassiveClick);

        var taxResidencePassive = $("[typeValue='TaxResPassive']");
        taxResidencePassive.unbind('change');
        taxResidencePassive.bind('change', { obj: this }, this.onTaxResidenceChange);//validateNextButtonAndControlSelection


        var taxIdentificationTypePassive = $("[typeValue='TaxTypPassive']");
        taxIdentificationTypePassive.unbind('change');
        taxIdentificationTypePassive.bind('change', { obj: this }, this.onChangeActionActiveTaxType);

        this.tinNumberPassive = $("[typeValue='TFNTxtPassive']");
        this.tinNumberPassive.unbind('keyup');
        this.tinNumberPassive.bind("keyup", { obj: this }, this.validateNextButtonAndControlSelection);

        var removeCntrlPersonPassive = $("[typeValue='removeTaxPassive']");
        removeCntrlPersonPassive.unbind('click');
        removeCntrlPersonPassive.bind("click", { obj: this }, this.onRemoveTaxDetailsClick);

        var removeCntrlPerson = $("[typeValue='removeCntrlPerson']");
        removeCntrlPerson.unbind('click');
        removeCntrlPerson.bind("click", { obj: this }, this.onRemoveControllingPersonClick);
        /**************************************added controlling persons*****************************************/
        this.IntialEvents = $("#btnIntial");
        this.IntialEvents.unbind('click');
        this.IntialEvents.bind("click", { obj: this }, this.validateNextButtonAndControlSelection);
        $('#btnIntial').trigger('click');

        //this.viewLoadHandler();



        this.dateOfBirth = $("[name='DOB']");
        this.dateOfBirth.datepicker('option', { maxDate: new Date() });
        this.dateOfBirth.unbind('change');
        this.dateOfBirth.bind("change", { obj: this }, this.onChangeDOB);

        this.dateOfBirth.unbind('focusout');
        this.dateOfBirth.bind("focusout", { obj: this }, this.onChangeDOB);

        $("ul [typeValue='DOB']").each(function () {
            $(this).attr('tempDate', $(this).val())
        });

        this.IsOthersValid = false;
    },
    onTaxResidenceChange: function (events) {
        var _obj = events.data.obj;

        // fetch the selected country value
        var selectedValue = $(this).val();

        // if tax residence select then no need to act else satisfy the requirement
        if (selectedValue != "") {
            $(this).closest("div").next().find("select[name$='TaxIdentificationType']")[0].selectedIndex = 0;
            // hide TIN Type - USA TIN,NZL IRD,UK UTR,UK NINO
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option").each(function () {
                var val = $(this).val();
                if (val == "NZL_IRD" || val == "USA_TIN" || val == "UK_UTR" || val == "UK_NIN") {
                    if ($(this).parent()[0].nodeName == "SPAN")
                        $(this).parent().hide();
                    else
                        $(this).wrap((navigator.appName == 'Microsoft Internet Explorer' || navigator.appName == 'Netscape') ? '<span>' : null).hide();
                }

            });
            var spanHandler = (navigator.appName == 'Microsoft Internet Explorer' || navigator.appName == 'Netscape') ? '<span>' : " ";
            //based on the residence set the default USA - USA TIN ; NZL - NZL IRD; UK - UK UTR & UK NINO. 
            if (selectedValue == "USA") {
                // $("select[name$='TaxResidence']")
                $(this).closest("div").next().find("option[value='USA_TIN']").unwrap().show();
                $(this).closest("div").next().find("select[name$='TaxIdentificationType']").val('USA_TIN');
            }
            else if (selectedValue == "NZL") {
                $(this).closest("div").next().find("option[value='NZL_IRD']").unwrap().show();
                $(this).closest("div").next().find("select[name$='TaxIdentificationType']").val('NZL_IRD');
            }
            else if (selectedValue == "GBR") {
                $(this).closest("div").next().find("option[value='UK_UTR']").unwrap().show();
                $(this).closest("div").next().find("option[value='UK_NIN']").unwrap().show();//.attr("selected", true)
                $(this).closest("div").next().find("select[name$='TaxIdentificationType']").val('UK_UTR');
            }
            else {
                $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option[value='']").attr("selected", true);
            }
        }
        else {
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option").show();
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option[value='']").attr("selected", true);
        }
        _obj.validateNextButtonAndControlSelection(events);
    },
    viewStateHandler: function () {

        // If user comes from FI and selected answered YES to ‘a non-CRS Participating Jurisdiction...’ default to 'PASSIVE’ and disable radio buttons from editing the value.
        if ($("#IsDisableActivePassive").val() == "0") {
            $("input[name='EntityOrganizationDetails.NonFinancialEntities.ActivePassiveNFE']").attr('disabled', 'disabled');
        }
        else {
            $("input[name='EntityOrganizationDetails.NonFinancialEntities.ActivePassiveNFE']").removeAttr('disabled');
        }

        if ($("#IsForeign").val() == "1") {// If foreign holding then display address

            $("#active-container").show();
            // Onload control street address view
            if ($("input[name='EntityOrganizationDetails.NonFinancialEntities.ActionStreetAddress']:checked").length != 0)
                $("#streetAddress").show();
            else
                $("#streetAddress").hide();
        }
        else {
            $("#active-container").hide();
        }

        if ($("input[name='EntityOrganizationDetails.NonFinancialEntities.ActivePassiveNFE']:checked").length > 0) {
            if ($("input[name='EntityOrganizationDetails.NonFinancialEntities.ActivePassiveNFE']:checked").val() == "1") {//Active
                $("#passive-container").hide();
            }
            else { // passive
                $("#passive-container").show();
                //$("#active-container").hide();
            }
        }
        $("#active-container .IsSameAsResidencial:checked").each(function () {
            if ($(this).val() == "1") {// yes
                $("#streetAddress .address-1").html($("#divRegistered .address-1").html());
                $("#streetAddress .address-2").html($("#divRegistered .address-2").html());
                $("#streetAddress .address-3").html($("#divRegistered .address-3").html());
                $("#streetAddress .address-4").html($("#divRegistered .address-4").html());
                $("#streetAddress .state").html($("#divRegistered .state").html());
                $("#streetAddress .town").html($("#divRegistered .town").html());
                $("#streetAddress .postcode").html($("#divRegistered .postcode").html());
                $("#streetAddress .country").html($("#divRegistered .country").html());
                $("#streetAddress").show();
            }
        });
        $("#ulNFETaxDetails .IsSameAsResidencial").each(function () {
            var index = '';
            if ($(this).attr("index") != undefined && $(this).attr("index") != "") { // multiple
                index = $(this).attr("index");
            }
            var radioGroupName = $(this).attr("groupName");
            if ($("#ulNFETaxDetails input[name='" + radioGroupName + "']:checked").length != 0) {

                $("#streetAddress_" + index).show();

                if ($("#ulNFETaxDetails input[name='" + radioGroupName + "']:checked").val() == "1") {
                    $("#streetAddress_" + index + " .address-1").html($("#divRegistered_" + index + " .address-1").html());
                    $("#streetAddress_" + index + " .address-2").html($("#divRegistered_" + index + " .address-2").html());
                    $("#streetAddress_" + index + " .address-3").html($("#divRegistered_" + index + " .address-3").html());
                    $("#streetAddress_" + index + " .address-4").html($("#divRegistered_" + index + " .address-4").html());
                    $("#streetAddress_" + index + " .state").html($("#divRegistered_" + index + "  .state").html());
                    $("#streetAddress_" + index + " .town").html($("#divRegistered_" + index + "  .town").html());
                    $("#streetAddress_" + index + " .postcode").html($("#divRegistered_" + index + "  .postcode").html());
                    $("#streetAddress_" + index + " .country").html($("#divRegistered_" + index + "  .country").html());

                    $("#streetAddress_" + index).show();
                }
            }
        });
        /*
        var TaxFirName = $("[typeValue=FirstNameTxt]");
        TaxFirName.unbind('keyup');
        TaxFirName.bind('keyup', { obj: this }, this.onChange);
    
        var TaxMidName = $("[typeValue=MiddleNameTxt]");
        TaxMidName.unbind('keyup');
        TaxMidName.bind('keyup', { obj: this }, this.onChange);
    
        var TaxLastName = $("[typeValue=LastNameTxt]");
        TaxLastName.unbind('keyup');
        TaxLastName.bind('keyup', { obj: this }, this.onChange);
    
        var taxResidence = $("[typeValue=TaxRes]");
        taxResidence.unbind('change');
        taxResidence.bind('change', { obj: this }, this.onChange);
    
        var TaxIdentificationType = $("[typeValue=TaxTyp]");
        TaxIdentificationType.unbind('change');
        TaxIdentificationType.bind('change', { obj: this }, this.onChange);
    
        var TFNNumber = $("[typeValue=TFNTxt]");
        TFNNumber.unbind('keyup');
        TFNNumber.bind('keyup', { obj: this }, this.onChange);
    
    
        var taxAddress = $("[typeValue=address]");
        taxAddress.unbind('click');
        taxAddress.bind("click", { obj: this }, this.onEditAddressClick);
    
        var removeCntrlPerson = $("[typeValue=removeCntrlPerson]");
    
        //removeCntrlPerson.each(function () {
        //    $(this).unbind('click');
        //    $(this).bind("click", { obj: this }, this.onRemoveControllingPersonClick);
        //});
        // var taxAddress = $("[typeValue=address]");
        removeCntrlPerson.unbind('click');
        removeCntrlPerson.bind("click", { obj: this }, this.onRemoveControllingPersonClick);
    
        //taxDOB.unbind('change');
        //taxDOB.bind('change', { obj: this }, this.onChange);
    
    
        this.IntialEvents = $("#btnIntial");
        this.IntialEvents.unbind('click');
        this.IntialEvents.bind("click", { obj: this }, this.validateNextButtonAndControlSelection);
        $('#btnIntial').trigger('click');
    
    

    */


        //if ($("ul [disPlay='block']").length == 1) {
        //    $("ul [typeValue='removeCntrlPerson']").children().first().addClass('disabledLinkButton');
        //}

    },
    onChangeActionActiveTaxType: function (events) {

        if ($(this).val() == 'DNI_TIN' ||
            $(this).val() == 'DNR_TIN' || $(this).val() == 'NO_TIN') {
            $(this).parent().parent().next().hide();
            $(this).parent().parent().next().find('[typevalue="TFNTxtPassive"]').val('');
            $(this).parent().parent().next().find('[typevalue="TFNTxt"]').val('');
        }
        else { $(this).parent().parent().next().show(); }
        var _obj = events.data.obj;
        _obj.validateNextButtonAndControlSelection(events);
    },
    checkTaxValidation: function (_obj) {
        var isValidTaxResidence = true, isValidTFNTxt = true, isValidTFNType = true, isValidPOB = true;
        isValidDOB = true;
        //var isDisplayUsCitizen = false;
        $("[typeValue='TaxTyp']").each(function () {
            if ($(this).val() == 'DNI_TIN' ||
                     $(this).val() == 'DNR_TIN' || $(this).val() == 'NO_TIN') {
                $(this).parent().parent().next().hide();
                $(this).parent().parent().next().find('[typevalue="TFNTxtPassive"]').val('');
                $(this).parent().parent().next().find('[typevalue="TFNTxt"]').val('');
            }
            else { $(this).parent().parent().next().show(); }

            if ($(this).val() == '') {
                isValidTFNType = false;
            }
        });
        var residencyCount = 0;
        $("[typeValue='TaxRes']").each(function () {
            residencyCount += 1;
            if ($(this).val() == '') {
                isValidTaxResidence = false;
            }

        });
        if ($("[disPlay='ForeignTax']").length > 1) {
            $("[name='EntityOrganizationDetails.NonFinancialEntities.ActionMoreJurisdiction']").attr('disabled', 'disabled');
            $("[name='EntityOrganizationDetails.NonFinancialEntities.ActionMoreJurisdiction'][value='1']").attr('checked', 'checked');
            $("#btnAddMoreTaxDetails").removeAttr('disabled');
        }
        else
            $("[name='EntityOrganizationDetails.NonFinancialEntities.ActionMoreJurisdiction']").removeAttr('disabled');


        $("[typeValue='TFNTxt']").each(function () {
            if (!$(this).parent().parent().is(':visible')) {
                $(this).val() == '';
            }
            if ($(this).val().trim() == '' && $(this).parent().parent().is(':visible')) {
                isValidTFNTxt = false;
            }
        });

        return (isValidTaxResidence && isValidTFNTxt && isValidTFNType); //&& isValidPOB && isValidDOB);
    },
    checkTaxValidationPassive: function (_obj) {
        var isValidTaxResidence = true, isValidTFNTxt = true, isValidTFNType = true, isValidPOB = true;
        isValidDOB = true;
        //var isDisplayUsCitizen = false;
        $("[typeValue='TaxTypPassive']").each(function () {
            if ($(this).val() == 'DNI_TIN' ||
                     $(this).val() == 'DNR_TIN' || $(this).val() == 'NO_TIN') {
                $(this).parent().parent().next().hide();
                $(this).parent().parent().next().find('[typevalue="TFNTxtPassive"]').val('');
                $(this).parent().parent().next().find('[typevalue="TFNTxt"]').val('');
            }
            else { $(this).parent().parent().next().show(); }

            if ($(this).val() == '') {
                isValidTFNType = false;
            }
        });
        var residencyCount = 0;
        $("[typeValue='TaxResPassive']").each(function () {
            residencyCount += 1;
            if ($(this).val() == '') {
                isValidTaxResidence = false;
            }

        });

        $("[typeValue='AddMoreTaxPassive']").each(function () {
            var count = parseInt($(this).attr("taxCount"));
            var controllingPersonIndex = parseInt($(this).attr("finalCount"));
            // var index = finalCount - 1;
            if (count == 1) {
                var id = "#btnDeleteTaxDetails" + (count - 1) + "" + controllingPersonIndex;
                $(id).children().first().addClass('disabledLinkButton');
            }
            else {
                var id = "#btnDeleteTaxDetails" + (count - 1) + "" + controllingPersonIndex;
                $(id).children().first().removeClass('disabledLinkButton');
            }

            if (count > 1) {
                $("[name='EntityOrganizationDetails.NonFinancialEntities.ControllingPersonDetails[" + controllingPersonIndex + "].ActionMoreJurisdiction']").attr('disabled', 'disabled');
                $("[name='EntityOrganizationDetails.NonFinancialEntities.ControllingPersonDetails[" + controllingPersonIndex + "].ActionMoreJurisdiction'][value='1']").attr('checked', true);
            }
            else
                $("[name='EntityOrganizationDetails.NonFinancialEntities.ControllingPersonDetails[" + controllingPersonIndex + "].ActionMoreJurisdiction']").removeAttr('disabled');
        });
        //if ($("ul [disPlay='block']").length > 1) {
        //    $("[name='EntityOrganizationDetails.NonFinancialEntities.ActionMoreJurisdiction']").attr('disabled', 'disabled');
        //}
        //else
        //    $("[name='EntityOrganizationDetails.NonFinancialEntities.ActionMoreJurisdiction']").removeAttr('disabled');


        $("[typeValue='TFNTxtPassive']").each(function () {
            if (!$(this).parent().parent().is(':visible')) {
                $(this).val() == '';
            }
            if ($(this).val().trim() == '' && $(this).parent().parent().is(':visible')) {
                isValidTFNTxt = false;
            }
        });

        return (isValidTaxResidence && isValidTFNTxt && isValidTFNType); //&& isValidPOB && isValidDOB);
    },
    checkDuplicationTaxDetails: function (_obj) {
        
        var arraycominations = [];
        var taxComb = ''; var duplicated = false;

        $("[typeValue='TaxTyp']").each(function () {
            taxComb = '';

            var countVal = $(this).attr("idVal");
            taxComb = $("[typeValue='TaxRes'][idVal='" + countVal + "']").val();
            taxComb = taxComb + $(this).val();
            if ($(this).val() == 'DNI_TIN' ||
                     $(this).val() == 'DNR_TIN' || $(this).val() == 'NO_TIN') {
                //$(this).parent().parent().next().hide();
            }
            else {
                var val = $("[typeValue='TFNTxt'][idVal='" + countVal + "']").val();
                taxComb = taxComb + $.trim(val);
            }
            arraycominations.push(taxComb);
        });
        var temp = [];
        $.each(arraycominations, function (key, value) {
            if ($.inArray(value, temp) === -1) {
                temp.push(value);
            } else {
                duplicated = true;

                _obj.disPlayDuplicateMessage("ForeignTaxResidency_TaxDetails_Duplicate_Message");
            }
        });
        return (duplicated == false);
    },
    checkDuplicationTaxDetailsPassive: function (_obj) {
        var arraycominations = [];
        var taxComb = ''; var duplicated = false;

        $("[typeValue='TaxTypPassive']").each(function () {
            taxComb = '';
            var controllingPersonIndex = $(this).attr("idValue");
            var countVal = $(this).attr("selectedTaxDetails");
            taxComb = $("[typeValue='TaxResPassive'][selectedTaxDetails='" + countVal + "']").val();
            taxComb = controllingPersonIndex + taxComb + $(this).val();
            if ($(this).val() == 'DNI_TIN' ||
                     $(this).val() == 'DNR_TIN' || $(this).val() == 'NO_TIN') {
                //$(this).parent().parent().next().hide();
            }
            else {
                var val = $("[typeValue='TFNTxtPassive'][selectedTaxDetails='" + countVal + "']").val();
                taxComb = taxComb + $.trim(val);
            }
            arraycominations.push(taxComb);
        });
        var temp = [];
        $.each(arraycominations, function (key, value) {
            if ($.inArray(value, temp) === -1) {
                temp.push(value);
            } else {
                duplicated = true;
                _obj.disPlayDuplicateMessage("ForeignTaxResidency_TaxDetails_Duplicate_Message");
            }
        });
        return (duplicated == false);
    },
    disPlayDuplicateMessage: function (value) {

        var title = 'Individual Investor';
        var displayResource = value;
        var formName = "GeneralMessage";
        var modal = new IC.Public.foreignTaxResidencyMessage();
        modal.showModal(title, displayResource, formName);
    },
    onRemoveTaxDetailsClick: function (events) {
        if ($(this).children().first().hasClass('disabledLinkButton')) {
            events.preventDefault();
            return;
        }
        var sequence = $(this).attr('countVal');
        var IndexSequenceValue = $(this).attr('taxCountValue');

        if ($(this).attr('typeValue') == "removeTaxPassive") {
            $("#AddRemoveFlowType").val("false");
        } else {
            $("#AddRemoveFlowType").val("true");
        }

        $("[name='IndexActioned']").val(sequence);
        $("[name='IndexSequenceValue']").val(IndexSequenceValue);
        //$(this).parents('li').hide();
        var form = $("#nonFinancialEntities");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "RemoveInvestorDetails");

        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            // cache:false,
            success: function (result) {
                //$("#" + OC.MVC.constants.mainContainer).html('');
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    onAddMorePassiveClick: function (events) {

        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled') || !_obj.checkTaxValidationPassive(_obj)) {// || !_obj.checkDuplicationTaxDetails(_obj)) {
            events.preventDefault();
            return;
        }
        $("#AddRemoveFlowType").val("cntrlPerson");

        var sequence = $(this).attr('countVal');
        $("[name='IndexActioned']").val(sequence);

        $("#ScreenType").val("NonFinancialEntities");
        var form = $("#nonFinancialEntities");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "AddMoreFatcaTaxDetails");
        form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        //}
    },
    onAddMoreTaxDetailsPassiveClick: function (events) {

        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled') || !_obj.checkTaxValidationPassive(_obj)) {// || !_obj.checkDuplicationTaxDetails(_obj)) {
            events.preventDefault();
            return;
        }
        $("#AddRemoveFlowType").val("false");

        var sequence = $(this).attr('countVal');
        $("[name='IndexActioned']").val(sequence);

        $("#ScreenType").val("NonFinancialEntities");
        var form = $("#nonFinancialEntities");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "AddMoreFatcaTaxDetails");
        form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        //}
    },
    onAddMoreTaxDetailsClick: function (events) {
        
        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled') || !_obj.checkTaxValidation(_obj) || !_obj.checkDuplicationTaxDetails(_obj)) {
            events.preventDefault();
            return;
        }

        $("#ScreenType").val("NonFinancialEntities");
        $("#AddRemoveFlowType").val("true");

        var form = $("#nonFinancialEntities");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "AddMoreFatcaTaxDetails");
        form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                taxResidenceOnPageLoad();
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        //}
    },
    editStreeePopup: function (events) {
        var index = '';
        if ($(this).attr("index") != undefined && $(this).attr("index") != "") { // multiple
            index = $(this).attr("index");
        }
        if (index == '') {
            if ($(this).val() == "1") {// yes
                $("#streetAddress .address-1").html($("#divRegistered .address-1").html());
                $("#streetAddress .address-2").html($("#divRegistered .address-2").html());
                $("#streetAddress .address-3").html($("#divRegistered .address-3").html());
                $("#streetAddress .address-4").html($("#divRegistered .address-4").html());
                $("#streetAddress .state").html($("#divRegistered .state").html());
                $("#streetAddress .town").html($("#divRegistered .town").html());
                $("#streetAddress .postcode").html($("#divRegistered .postcode").html());
                $("#streetAddress .country").html($("#divRegistered .country").html());

                $("#streetAddress").show();

                // validate at selection
                var _obj = events.data.obj;
                _obj.validateNextButtonAndControlSelection(events);
            }
            else {// If no then display the popup
                var urlConfirm = OC.MVC.util.getLink("Holding", "Index");
                var modal = new IC.Public.investorAddressDetails();
                modal.showModal(urlConfirm,false,false);
            }
        }
        else {
            if ($(this).val() == "1") {// yes
                $("#streetAddress_" + index + " .address-1").html($("#divRegistered_" + index + " .address-1").html());
                $("#streetAddress_" + index + " .address-2").html($("#divRegistered_" + index + " .address-2").html());
                $("#streetAddress_" + index + " .address-3").html($("#divRegistered_" + index + " .address-3").html());
                $("#streetAddress_" + index + " .address-4").html($("#divRegistered_" + index + " .address-4").html());
                $("#streetAddress_" + index + " .state").html($("#divRegistered_" + index + "  .state").html());
                $("#streetAddress_" + index + " .town").html($("#divRegistered_" + index + "  .town").html());
                $("#streetAddress_" + index + " .postcode").html($("#divRegistered_" + index + "  .postcode").html());
                $("#streetAddress_" + index + " .country").html($("#divRegistered_" + index + "  .country").html());

                $("#streetAddress_" + index).show();

                // validate at selection
                var _obj = events.data.obj;
                _obj.validateNextButtonAndControlSelection(events);
            }
            else {// If no then display the popup
                //var urlConfirm = OC.MVC.util.getLink("Holding", "Index");
                //var modal = new IC.Public.investorAddressDetails();
                //modal.showModal(urlConfirm);

                var _obj = events.data.obj;
                var urlConfirm = OC.MVC.util.getLink("Holding", "Index");
                var modal = new IC.Public.investorAddressDetails();
                modal.showModalIndex(urlConfirm, index, _obj.IsOthersValid,false,false);
            }
        }

    },
    onSkipClick: function (events) {
        var title = 'Financial Institutions';
        var displayResource = 'ForeignTax_Residency_InvestorSummary_SkipMessage';
        var formName = "#nonFinancialEntities";
        var modal = new IC.Public.foreignTaxResidencyMessage();
        modal.showModal(title, displayResource, formName);
    },
    onBackClick: function (events) {
        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        var form = $("#nonFinancialEntities");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorBack");
        //form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        //}
    },
    onAddMoreClick: function (events) {

        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled')) {
            //|| !_obj.checkTaxValidation(_obj)) {
            events.preventDefault();
            return;
        }
        var form = $("#nonFinancialEntities");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "AddMoreFatcaTaxDetails");
        form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        //}
    },
    ValidateDate: function (dtValue) {
        var dtRegex = new RegExp(/\b\d{1,2}[\/-]\d{1,2}[\/-]\d{4}\b/);
        return dtRegex.test(dtValue);
    },
    validateDateAndMonth: function (val) {
        try {
            $.datepicker.parseDate('dd/mm/yy', val);
        } catch (e) {
            //alert(e);
            return false;
        };
        return true;
    },
    onChangeDOB: function (events) {
        var _obj = events.data.obj;
        var date = $(this).val();
        if (_obj.ValidateDate(date) && _obj.validateDateAndMonth($(this).val())) {
            var dateValue = $.datepicker.parseDate("dd/mm/yy", $(this).val());
            var newDateValue = new Date();
            if ($(this).val() != '') {
                if (dateValue > newDateValue) {
                    var currValue = $(this).attr('tempDate');
                    $(this).val(currValue);
                    $(this).parent().children().last().val(currValue);
                }
                else {
                    var value = $(this).val();
                    $(this).attr('tempDate', value);
                    $(this).parent().children().last().val(value);
                }
            }
            _obj.validateNextButtonAndControlSelection(events);
        }
        else {
            $(this).val('');
            $(this).attr('tempDate', '');
            $(this).parent().children().last().val('');
            _obj.validateNextButtonAndControlSelection(events);
        }

    },
    onDOBFocusOut: function (events) {

        var dateValue = $.datepicker.parseDate("dd/mm/yy", $(this).val());
        var newDateValue = new Date();

        if (dateValue > newDateValue) {
            var currValue = $(this).attr('tempDate');
            $(this).val(currValue);
            $(this).parent().children().last().val(currValue);
        }
        else {
            var value = $(this).val();
            $(this).attr('tempDate', value);
            $(this).parent().children().last().val(value);
        }
    },
    onChange: function (events) {
        var _obj = events.data.obj;
        _obj.validateNextButtonAndControlSelection(events);
    },
    validateNextButtonAndControlSelection: function (events) {
        var _obj = events.data.obj;
        var isValid = true;
        _obj.viewStateHandler();
        //added
        if (($("[name='EntityOrganizationDetails.NonFinancialEntities.ActionMoreJurisdiction'][value='1']").is(':checked'))) {
            $("#btnAddMoreTaxDetails").removeAttr('disabled');
            isValid = isValid && false;
        } else if ($("[name='EntityOrganizationDetails.NonFinancialEntities.ActionMoreJurisdiction'][value='0']").is(':checked')) {
            $("#btnAddMoreTaxDetails").attr("disabled", "disabled");
            isValid = isValid && true;
        }


        /////////////////////////////////////////////////////////////
        //$("[name='EntityOrganizationDetails.NonFinancialEntities.ActivePassiveNFE']").removeAttr('disabled');
        $("[name='EntityOrganizationDetails.NonFinancialEntities.ActionPassiveNFEBeneficialOwners']").removeAttr('disabled');

        $("#btnAddMore").removeAttr('disabled');

        if (!($("input[name='EntityOrganizationDetails.NonFinancialEntities.ActionMoreJurisdiction']:checked").is(':checked'))) {
                $("#btnAddMoreTaxDetails").attr("disabled", "disabled");
        }

        //- disable Next and ensure to capture foreign tax details for FOREIGN NFE
        if ($("#IsForeign").val() == "1") {
            //Common validation:- legal name of the Entity is Mandatory
            var isTaxValid = _obj.checkTaxValidation(_obj);
            isValid = isValid && isTaxValid;
            if (($("[name='EntityOrganizationDetails.NonFinancialEntities.ActionMoreJurisdiction'][value='1']").is(':checked')) && $("[disPlay='ForeignTax']").length > 1 && isTaxValid) {
                isValid = true;
            }
            if ($("#EntityOrganizationDetails_NonFinancialEntities_LegalEntityName").val() == '') {
                isValid = false;
            }

            // If residentital not same street then need values
            if (!$("input[name='EntityOrganizationDetails.NonFinancialEntities.ActionStreetAddress']:checked").is(':checked')) {
                isValid = false;
            }

            // validate foreign tax details
            
            if ($("input[name='EntityOrganizationDetails.NonFinancialEntities.ActionMoreJurisdiction']:checked").is(':checked')
                && isValid) {
                isValid = true;
            }
            else {
                isValid = false;
            }           
            if (($("[name='EntityOrganizationDetails.NonFinancialEntities.ActionMoreJurisdiction'][value='1']").is(':checked')
            && $("#divTaxDetails ul [disPlay='block']").length == 1)) {
                $("#btnAddMoreTaxDetails").removeAttr('disabled');
                isValid = isValid && false;
            }
            else if ($("[name='EntityOrganizationDetails.NonFinancialEntities.ActionMoreJurisdiction'][value='0']").is(':checked')) {
                $("#btnAddMoreTaxDetails").attr("disabled", "disabled");
                isValid = isValid && true;
            }
        }

        if (($("[name='EntityOrganizationDetails.NonFinancialEntities.ActivePassiveNFE'][value='1']").is(':checked'))) {
            var isValidActive = _obj.validateAndProcessActiveNFE(_obj);
            isValid = isValid && isValidActive;
        }
        else if (($("[name='EntityOrganizationDetails.NonFinancialEntities.ActivePassiveNFE'][value='0']").is(':checked'))) {
            var isValidPassive = _obj.validateAndProcessPassiveNFE(_obj);
            isValid = isValid && isValidPassive;
        }
        else {
            //_obj.processTrueTaxID();
            $("#radioActivePassiveBeneficial").hide();
            $("#ulNFETaxDetails").hide();
            $("#btnAddMore").attr("disabled", "disabled");
            isValid = false;
        }



        if (isValid)
            _obj.btnNext.parent().removeClass('disabled');
        else
            _obj.btnNext.parent().addClass('disabled');
    },
    validateAndProcessActiveNFE: function (_obj) {
        $("#radioActivePassiveBeneficial").hide();
        $("[name='EntityOrganizationDetails.NonFinancialEntities.ActionPassiveNFEBeneficialOwners']").attr('checked', false);
        $("#ulNFETaxDetails").hide();
        //_obj.clearControllingDetails();
        //$("#btnAddMore").attr("disabled", "disabled");
        return true;
    },
    validateAndProcessPassiveNFE: function (_obj) {
        $("#radioActivePassiveBeneficial").show();
        var isValid = true;
        if ($("input[name='EntityOrganizationDetails.NonFinancialEntities.ActionPassiveNFEBeneficialOwners']:checked").length > 0
            && $("input[name='EntityOrganizationDetails.NonFinancialEntities.ActionPassiveNFEBeneficialOwners']:checked").val() == "0") {
            // No- Clear and hide the controlling person data capture controls data & Ensure Next button is disabled
            $("#ulNFETaxDetails").hide();
            $("#btnAddMore").attr("disabled", "disabled");
            _obj.clearControllingDetails();
            return isValid;
        }
        else if ($("input[name='EntityOrganizationDetails.NonFinancialEntities.ActionPassiveNFEBeneficialOwners']:checked").length > 0
            && $("input[name='EntityOrganizationDetails.NonFinancialEntities.ActionPassiveNFEBeneficialOwners']:checked").val() == "1") {
            // Yes - Clear and enable the controlling persons data capture controls & Ensure Next button is enabled
            $("#ulNFETaxDetails").show();
            $("#btnAddMore").removeAttr('disabled');
        }


        $(".firstName").each(function () {
            if ($(this).val() == "") {
                isValid = isValid && false;
            }
        });

        $(".lastName").each(function () {
            if ($(this).val() == "") {
                isValid = isValid && false;
            }
        });

        $("[name=DOB]").each(function () {
            if ($(this).val() == "") {
                isValid = isValid && false;
            }
        });
        var isTaxValid = _obj.checkTaxValidationPassive(_obj);
        isValid = isTaxValid && isValid;
        //var jurisValid = false;
        $("[typeValue='Jurisdiction']").each(function () {
            var count = $(this).attr("finalCount");
            var taxCount = $("#btnAddMoreTaxDetails" + count).attr("taxCount");
            if (!($("[name='EntityOrganizationDetails.NonFinancialEntities.ControllingPersonDetails[" + count + "].ActionMoreJurisdiction']").is(':checked')))
            {
                isValid = isValid && false;
                $("#btnAddMoreTaxDetails" + count).attr("disabled", "disabled");
            }
            else {
                if ($("[name='EntityOrganizationDetails.NonFinancialEntities.ControllingPersonDetails[" + count + "].ActionMoreJurisdiction'][value='1']").is(':checked')) {
                    $("#btnAddMoreTaxDetails" + count).removeAttr('disabled');
                    $("#btnAddMoreTaxDetails" + count).removeClass('disabledLinkButton');
                    var controlCount = $(this).attr("countval");
                    if ($("#passive-container ul [disPlay='block'][countval='" + controlCount + "']").length == 1) // Clicked yes but not added more jurisdiction
                    isValid = isValid && false;
                    else
                        isValid = isValid && true;
                }
                else {
                    $("#btnAddMoreTaxDetails" + count).attr("disabled", "disabled");
                    isValid = isValid && true;
                }
            }
            if (taxCount > 1) {
                $("#btnAddMoreTaxDetails" + count).removeAttr('disabled');
                $("#btnAddMoreTaxDetails" + count).removeClass('disabledLinkButton');
            }
            if ($("[name='EntityOrganizationDetails.NonFinancialEntities.ControllingPersonDetails[" + count + "].ActionMoreJurisdiction'][value='1']").is(':checked') && (taxCount > 1) && isTaxValid) {
                isValid = isValid && true;
            }
        });

        _obj.IsOthersValid = isValid;
        $("#ulNFETaxDetails .IsSameAsResidencial").each(function () {
            var radioGroupName = $(this).attr("groupName");
            if ($("#ulNFETaxDetails input[name='" + radioGroupName + "']:checked").length == 0) {
                isValid = isValid && false;
            }
        });
        return isValid;

        //  isValid = _obj.checkTaxValidation(_obj);

    },
    clearControllingDetails: function () {

        //$("[typeValue='TaxTyp']").each(function () {
        //    $(this).val() == '';
        //});
        //$("[typeValue='TaxTyp']").val('');
        ////var residencyCount = 0;
        //$("[typeValue='TaxRes']").val('');

        //$("[typeValue='TFNTxt']").val('');

        //$("[typeValue='DOB']").val('');

        //$("[typeValue='dateOfBirthDisplay']").val('');

        //$("[typeValue='LastNameTxt']").val('');
        //$("[typeValue='MiddleNameTxt']").val('');
        //$("[typeValue='FirstNameTxt']").val('');
        //$("[typeValue='title']").val('');
        //$("[typeValue='BirthPlace']").val('');
        //$("[typeValue='address']").val('');
    },
    checkControllingPersonValidation: function (_obj) {
        var isValidAddress = true, isValidTaxResidence = true, isValidTFNTxt = true, isValidTFNType = true, isValidFirName = true, isValidMidName = true, isValidLastName = true, isValidDOB = true;
        //var isDisplayUsCitizen = false;
        _obj.IsOthersValid = false;
        //$("ul [typeValue='TaxTyp']").each(function () {
        //    if ($(this).val() == '') {
        //        isValidTFNType = false;
        //    }
        //});

        var residencyCount = 0;
        //$("ul [typeValue='TaxRes']").each(function () {
        //    residencyCount += 1;
        //    if ($(this).val() == '') {
        //        isValidTaxResidence = false;
        //    }
        //});

        //$("ul [typeValue='TFNTxt']").each(function () {
        //    if ($(this).val() == '') {
        //        isValidTFNTxt = false;
        //    }
        //});

        $("ul [typeValue='DOB']").each(function () {
            //  var dobSeqId = $(this).attr('seqID');
            //var dobDisPlay = 'dob' + dobSeqId;
            var value = $(this).val();
            //  $("[displayId=" + dobDisPlay + "]").val(value);
            $(this).parent().children().last().val(value);
            if ($(this).val() == '') {
                isValidDOB = false;
            }
        });

        $("ul [typeValue='LastNameTxt']").each(function () {
            if ($(this).val() == '') {
                isValidLastName = false;
            }
        });

        $("ul [typeValue='MiddleNameTxt']").each(function () {
            if ($(this).val() == '') {
                isValidMidName = false;
            }
        });

        $("ul [typeValue='FirstNameTxt']").each(function () {
            if ($(this).val() == '') {
                isValidFirName = false;
            }
        });
        $("ul [address='loop']").each(function () {
            if ($(this).attr("validate") != 'true') {
                isValidAddress = false;
            }
        });



        //if (residencyCount > 1) {
        //    $("[name='EntityOrganizationDetails.NonFinancialEntities.ActivePassiveNFE']").attr('disabled', 'disabled');
        //    $("[name='EntityOrganizationDetails.NonFinancialEntities.ActionPassiveNFEBeneficialOwners']").attr('disabled', 'disabled');
        //} else {
        //    $("[name='EntityOrganizationDetails.NonFinancialEntities.ActivePassiveNFE']").removeAttr('disabled');
        //    $("[name='EntityOrganizationDetails.NonFinancialEntities.ActionPassiveNFEBeneficialOwners']").removeAttr('disabled');

        //}
        _obj.IsOthersValid = (isValidTaxResidence && isValidTFNTxt && isValidTFNType && isValidFirName && isValidMidName && isValidLastName && isValidDOB);
        return (isValidAddress && isValidTaxResidence && isValidTFNTxt && isValidTFNType && isValidFirName && isValidMidName && isValidLastName && isValidDOB); //&& isValidPOB && isValidDOB);
    },

    onNextClick: function (events) {
        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled') || !_obj.checkDuplicationTaxDetails(_obj) || !_obj.checkDuplicationTaxDetailsPassive(_obj)) {
            events.preventDefault();
            return;
        }
        var form = $("#nonFinancialEntities");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorNext");
        //form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        //}
    },

    onRemoveControllingPersonClick: function (events) {
        if ($(this).children().first().hasClass('disabledLinkButton')) {
            events.preventDefault();
            return;
        }
        var sequence = $(this).attr('countVal');
        $("[name='IndexActioned']").val(sequence);
        $("#AddRemoveFlowType").val("cntrlPerson");
        //$(this).parents('li').hide();
        var form = $("#nonFinancialEntities");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "RemoveInvestorDetails");
        //////



        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            // cache:false,
            success: function (result) {
                //$("#" + OC.MVC.constants.mainContainer).html('');
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    onEditAddressClick: function (events) {
        var _obj = events.data.obj;
        var self = this;
        var index = $(this).attr("editindex");
        if (index == undefined || index == "") {
            var isRegisterandStreetSame = $("input[name='EntityOrganizationDetails.NonFinancialEntities.ActionStreetAddress']:checked").val() == "1";
            var urlConfirm = OC.MVC.util.getLink("Holding", "Index");
            var modal = new IC.Public.investorAddressDetails();
            modal.showModal(urlConfirm, true, isRegisterandStreetSame);
        }
        else {
            var isRegisterandStreetSame = $(".IsSameAsResidencial[index=" + index + "]:checked").val() == "1";
            var urlConfirm = OC.MVC.util.getLink("Holding", "Index");
            var modal = new IC.Public.investorAddressDetails();
            modal.showModalIndex(urlConfirm, index, _obj.IsOthersValid, isRegisterandStreetSame, true);
        }
        self.init();

        //var _obj = events.data.obj;
        //var urlConfirm = OC.MVC.util.getLink("Holding", "Index");
        //var modal = new IC.Public.investorAddressDetails();
        //$(this).attr("countVal")
        //modal.showModalIndex(urlConfirm, $(this).attr('countVal'), _obj.IsOthersValid);
    },
    onOkClick: function (events) {
        $.modal.close();
    }
};

function validateTINForNonFinancialEntity(textValue) {
    var id = $(textValue).attr("idval");
    var taxResidence = $("#EntityOrganizationDetails_NonFinancialEntities_InvestorTaxDetails_" + id + "__TaxResidence").val();
    var taxIdType = $("#EntityOrganizationDetails_NonFinancialEntities_InvestorTaxDetails_0_" + id + "__TaxIdentificationType").val();
    validateTIN(textValue, taxResidence, taxIdType);

}
function taxResidenceOnPageLoad() {
    // hide NZL/ UK and USA options
    $("select[name$='TaxResidence']").closest("div").next().find("select[name$='TaxIdentificationType'] option").each(function () {
        var val = $(this).val();
        if (val == "NZL_IRD" || val == "USA_TIN" || val == "UK_UTR" || val == "UK_NIN") {
            //$(this).hide(); IE fix
            //$(this).unwrap('<span>').wrap((navigator.appName == 'Microsoft Internet Explorer' || navigator.appName == 'Netscape') ? '<span>' : null).hide();

            if ($(this).parent()[0].nodeName == "SPAN")
                $(this).parent().hide();
            else
                $(this).wrap((navigator.appName == 'Microsoft Internet Explorer' || navigator.appName == 'Netscape') ? '<span>' : null).hide();
        }

    });
    var spanHandler = (navigator.appName == 'Microsoft Internet Explorer' || navigator.appName == 'Netscape') ? '<span>' : null;
    // based on the selection make availablity to the user selection
    $("select[name$='TaxResidence']").each(function () {
        // fetch the selected country value
        var selectedValue = $(this).val();
        if (selectedValue == "USA") {
            // $("select[name$='TaxResidence']")
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option[value='USA_TIN']").unwrap().show();
        }
        else if (selectedValue == "NZL") {
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option[value='NZL_IRD']").unwrap().show();
        }
        else if (selectedValue == "GBR") {
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option[value='UK_UTR']").unwrap().show();
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option[value='UK_NIN']").unwrap().show();
        }
    });
};IC.Public.individualConfirmation = function () {
    this.init();
}
IC.Public.individualConfirmation.prototype = {

    init: function () {
        var isCampaign = $("#IsCampaignEnabled");
        if (isCampaign != undefined && isCampaign.length != 0 && isCampaign.val().toLowerCase() == "true") {
            IC.Public.HideMenu();
        }
        this.btnNext = $("#btnNext");
        this.btnNext.bind('click', { obj: this }, this.onNextClick);
        this.btnNext.parent().addClass('disabled');
        this.btnSkip = $("#btnSkip");
        this.btnSkip.bind('click', { obj: this }, this.onSkipClick);


        this.btnBack = $("#btnBack");
        this.btnBack.bind('click', { obj: this }, this.onBackClick);

        //this.btnEditSummary = $("#btnEditSummary");
        //this.btnEditSummary.bind('click', { obj: this }, this.onEditSummaryClick);

        //this.btnEditIndividualInvestor = $("#btnEditIndividualInvestor");
        //this.btnEditIndividualInvestor.bind('click', { obj: this }, this.onEditIndividualInvestorTaxClick);

        this.TermsAndConditionsAccepted = $("#TermsAndConditionAccepted");
        this.TermsAndConditionsAccepted.bind('change', { obj: this }, this.validateData);
    },
    validateData: function (events) {
        var _obj = events.data.obj;
        if (_obj.TermsAndConditionsAccepted.is(':checked'))
            _obj.btnNext.parent().removeClass('disabled');
        else
            _obj.btnNext.parent().addClass('disabled');
    },
    //onEditSummaryClick: function (events) {
    //    var url = OC.MVC.util.getLink("ForeignTaxResidency", "ForiegnTaxResidencyCertification");
    //    OC.MVC.util.loadMainContainerView(url, null);
    //},
    //onEditIndividualInvestorTaxClick: function (events) {
    //    var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorEdit", "screenType=" + $("#EditIndividualInvestor").val());
    //    OC.MVC.util.loadMainContainerView(url, null);
    //},
    onSkipClick: function (events) {
        var title = 'Investor Individual';
        var displayResource = 'ForeignTax_Residency_InvestorSummary_SkipMessage';
        var formName = "#confirmationIndividual";
        var modal = new IC.Public.foreignTaxResidencyMessage();
        modal.showModal(title, displayResource, formName);
    },
    onBackClick: function (events) {
        var form = $("#confirmationIndividual");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorBack");
        form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function (result) {
                    $("#" + OC.MVC.constants.mainContainer).html(result);
                    IC.Public.hideLoading();
                },
                error: function (err) {
                    $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },
    onNextClick: function (events) {
        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        var form = $("#confirmationIndividual");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorNext");
        form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function (result) {
                    if (result == "1") {
                        var urlLocation = "/";
                        window.location.href = urlLocation;
                    }
                    else {
                        $("#" + OC.MVC.constants.mainContainer).html(result);
                        IC.Public.hideLoading();
                    }
                },
                error: function (err) {
                    $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },


};IC.Public.confirmationEntity = function () {
    this.init();
}

IC.Public.confirmationEntity.prototype = {

    init: function () {
        this.btnNext = $("#btnNext");
        this.btnNext.bind('click', { obj: this }, this.onNextClick);

        this.btnSkip = $("#btnSkip");
        this.btnSkip.bind('click', { obj: this }, this.onSkipClick);


        this.btnBack = $("#btnBack");
        this.btnBack.bind('click', { obj: this }, this.onBackClick);

        this.btnEditSummary = $("#btnEditSummary");
        this.btnEditSummary.bind('click', { obj: this }, this.onEditSummaryClick);

        this.btnEditEntityOrganization = $("#btnEditEntityDetails");
        this.btnEditEntityOrganization.bind('click', { obj: this }, this.onEditEntityOrganizationClick);

        this.btnEditFinancialInstitution = $("#btnEditFinancialInstitutions");
        this.btnEditFinancialInstitution.bind('click', { obj: this }, this.onEditFinancialClick);

        this.btnEditNonFinancialEntity = $("#btnEditNonFinancialEntities");
        this.btnEditNonFinancialEntity.bind('click', { obj: this }, this.onEditNonFianancialClick);

        this.btnEditAddress = $("#btnViewEntityAddress");
        this.btnEditAddress.bind('click', { obj: this }, this.onViewAddressClick);

        this.TermsAndConditionsAccepted = $("#TermsAndConditionAccepted");
        this.TermsAndConditionsAccepted.bind('change', { obj: this }, this.validateData);

        this.entityNonFinancialGrid = $("#EntityNonFinancialGrid");
        this.entityNonFinancialGrid.bind("grid_gridComplete", { obj: this }, function (events, gridContext) {
            rowCount = gridContext;
            var _obj = events.data.obj;
            $(this).find("a.addressLink").each(function (index) {
                $(this).bind("click", { obj: _obj }, _obj.onViewAddressGridLinkClick);
            });
            $("#divbottomPanel").hide();
        });
    },
    onViewAddressClick: function (events) {
        var urlConfirm = OC.MVC.util.getLink("Holding", "Index");
        var modal = new IC.Public.investorAddressDetails();
        modal.showModalView(urlConfirm);
    },
    onViewAddressGridLinkClick: function (events) {
        var seqNo = $(this).attr('seqno');
        var modal = new IC.Public.investorAddressDetails();
        modal.showModalViewSeqIndex(seqNo);
    },
    onSkipClick: function (events) {
        var title = 'Investor Individual';
        var displayResource = 'ForeignTax_Residency_InvestorSummary_SkipMessage';
        var formName = "#confirmationEntity";
        var modal = new IC.Public.foreignTaxResidencyMessage();
        modal.showModal(title, displayResource, formName);
    },
    onBackClick: function (events) {
        var form = $("#confirmationEntity");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorBack");
        form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function (result) {
                    $("#" + OC.MVC.constants.mainContainer).html(result);
                    IC.Public.hideLoading();
                },
                error: function (err) {
                    $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },
    validateData: function (events) {
        var _obj = events.data.obj;
        if (_obj.TermsAndConditionsAccepted.is(':checked'))
            _obj.btnNext.parent().removeClass('disabled');
        else
            _obj.btnNext.parent().addClass('disabled');
    },
    onEditSummaryClick: function (events) {
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "ForeignTaxResidencyCertification");
        OC.MVC.util.loadMainContainerView(url, null);
    },
    onEditEntityOrganizationClick: function (events) {
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorEdit", "screenType=" + $("#editEntity").val());
        OC.MVC.util.loadMainContainerView(url, null);
    },
    onEditFinancialClick: function (events) {
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorEdit", "screenType=" + $("#editFinancial").val());
        OC.MVC.util.loadMainContainerView(url, null);
    },
    onEditNonFianancialClick: function (events) {
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorEdit", "screenType=" + $("#editNonFinancial").val());
        OC.MVC.util.loadMainContainerView(url, null);
    },
    onNextClick: function (events) {
    var _obj = events.data.obj;
    if ($(this).parent().hasClass('disabled')) {
        events.preventDefault();
        return;
    }
    var form = $("#confirmationEntity");
    var data = form.serializeObject();
    var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorNext");
        //form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
    IC.Public.showLoading("");
    $.ajax({
        url: url,
        type: 'POST',
        data: data,
        success: function (result) {
            $("#" + OC.MVC.constants.mainContainer).html(result);
            IC.Public.hideLoading();
        },
        error: function (err) {
            $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
            IC.Public.hideLoading();
        }
    });
        //}
},
};/// <reference path="ic.public.foreignTaxResidency.investorCertificationSummary.js" />
/// <reference path="~/Scripts/common.js" />
IC.Public.individualInvestorInfo = function () {
    this.init();
}

IC.Public.individualInvestorInfo.prototype = {
    onChangeDOB: function (events) {
        var _obj = events.data.obj;
        var date = $(this).val();
        if (_obj.ValidateDate(date) && _obj.validateDateAndMonth($(this).val())) {
            var dateValue = $.datepicker.parseDate("dd/mm/yy", $(this).val());
            var newDateValue = new Date();
            if ($(this).val() != '') {
                if (dateValue > newDateValue) {
                    var currValue = $(this).attr('tempDate');
                    $(this).val(currValue);
                    $(this).parent().children().last().val(currValue);
                }
                else {
                    var value = $(this).val();
                    $(this).attr('tempDate', value);
                    $(this).parent().children().last().val(value);
                }
            }
            _obj.validateNextButtonAndControlSelection(events);
        }
        else {
            $(this).val('');
            $(this).attr('tempDate', '');
            $(this).parent().children().last().val('');
            _obj.validateNextButtonAndControlSelection(events);
        }

    },
    init: function () {
        var isCampaign = $("#IsCampaignEnabled");
        if (isCampaign != undefined && isCampaign.length != 0 && isCampaign.val().toLowerCase() == "true") {
            IC.Public.HideMenu();
        }
        this.dateOfBirth = $("[name='DOB']");
        this.dateOfBirth.datepicker('option', { maxDate: new Date() });
        this.dateOfBirth.unbind('change');
        this.dateOfBirth.bind("change", { obj: this }, this.onChangeDOB);

        this.dateOfBirth.unbind('focusout');
        this.dateOfBirth.bind("focusout", { obj: this }, this.onChangeDOB);

        $("[typeValue='dateOfBirthDisplay']").each(function () {
            $(this).attr('tempDate', $(this).val())
        });

        this.btnEditAddress = $(".editstreetaddress");
        this.btnEditAddress.bind('click', { obj: this }, this.onEditAddressClick);

        taxResidenceOnPageLoad();

        //var divContent = '';
        //$("[rowcount='true']").each(function () {
        //    divContent += $(this).html();
        //    $(this).remove();
        //});
        //var checkTaxValidity = this.checkTaxValidation(this);
        //if (divContent == '' && checkTaxValidity) {
        //    $("[name='IndividualInvestor.ActionMoreJurisdiction'][value='0']").attr('checked', true);
        //}
        //$('#divAddMoreJurisdiction').after(divContent);

        $("#validateTIN").hide();
        this.btnNext = $("#btnNext");
        this.btnNext.bind('click', { obj: this }, this.onNextClick);
        this.btnSkip = $("#btnSkip");
        this.btnSkip.bind('click', { obj: this }, this.onSkipClick);
        this.btnBack = $("#btnBack");
        this.btnBack.bind('click', { obj: this }, this.onBackClick);
        $("#btnOk").bind('click', { obj: this }, this.onOkClick);

        this.btnAddMore = $("#btnAddMore");
        this.btnAddMore.bind('click', { obj: this }, this.onAddMorePassiveClick);

        this.pagessiveControls = $(".firstName,.lastName,#dateOfBirth,.placeOfBirth");
        this.pagessiveControls.unbind("keyup");
        this.pagessiveControls.bind("keyup", { obj: this }, this.onChange);

        this.editStreetAddress = $(".IsSameAsResidencial");//$("input[name=EntityOrganizationDetails.NonFinancialEntities.ActionStreetAddress]");
        this.editStreetAddress.unbind('change');
        this.editStreetAddress.bind("change", { obj: this }, this.editStreeePopup);

        /**************************************added tax details*****************************************/


        var taxJurisdiction = $("[typeValue='Jurisdiction']");
        taxJurisdiction.unbind('change');
        taxJurisdiction.bind('change', { obj: this }, this.validateNextButtonAndControlSelection);
        //taxResidence.each(function () {
        //    $(this).trigger('change');
        //});



        //this.btnAddMoreTaxDetails = $("#btnAddMoreTaxDetails");
        //this.btnAddMoreTaxDetails.unbind('click');
        //this.btnAddMoreTaxDetails.bind('click', { obj: this }, this.onAddMoreTaxDetailsClick);

        //var removeCntrlPerson = $("[typeValue=removeTax]");
        //removeCntrlPerson.unbind('click');
        //removeCntrlPerson.bind("click", { obj: this }, this.onRemoveTaxDetailsClick);

        //if ($("[disPlay='ForeignTax']").length == 1) {
        //    $("ul [typeValue='removeTax']").children().first().addClass('disabledLinkButton');
        //}

        //$("[typeValue='AddMoreTaxPassive']").each(function () {
        //    $(this).attr('tempDate', $(this).val())
        //});
        var taxValidationPassive = this.checkTaxValidationPassive(this);
        $("[typeValue='AddMoreTaxPassive']").each(function () {
            var taxCount = parseInt($(this).attr("taxCount"));
            var finalCount = $(this).attr("finalCount");
            if (taxCount == 1) {
                $("#btnDeleteTaxDetails" + "" + (taxCount - 1) + "" + finalCount).addClass('disabledLinkButton');
                $(this).addClass('disabledLinkButton');
                if (taxValidationPassive) {
                    $("[name='IndividualInvestor.ControllingPersonDetails[" + finalCount + "].ActionMoreJurisdiction'][value='0']").attr("checked", "true");
                }
                else {
                    if ($("[name='IndividualInvestor.ControllingPersonDetails[" + finalCount + "].InvestorTaxDetails[" + (taxCount - 1) + "].TaxResidence']").val() == '' &&
                        $("[name='IndividualInvestor.ControllingPersonDetails[" + finalCount + "].InvestorTaxDetails[" + (taxCount - 1) + "].TaxIdentificationType']").val() == '' &&
                        $("[name='IndividualInvestor.ControllingPersonDetails[" + finalCount + "].InvestorTaxDetails[" + (taxCount - 1) + "].TIN']").val() == '') {
                        $("[name='IndividualInvestor.ControllingPersonDetails[" + finalCount + "].ActionMoreJurisdiction']").attr('checked', false);
                    }
                    else {
                        $("[name='IndividualInvestor.ControllingPersonDetails[" + finalCount + "].ActionMoreJurisdiction'][value='0']").attr("checked", "true");
                    }
                }
                //$("[name='IndividualInvestor.ControllingPersonDetails[" + finalCount + "].ActionMoreJurisdiction'][value='0']").attr("checked", "true");
            }
        });

        /**************************************added tax details*****************************************/

        /**************************************added controlling persons*****************************************/
        if ($("ul [disPlay='remCntrl']").length == 1) {
            $("ul [typeValue='removeCntrlPerson']").children().first().addClass('disabledLinkButton');
            $("ul [typeValue='removeCntrlPerson']").attr("disabled", "disabled");
        }

        var AddMoreTaxPassive = $("[typeValue='AddMoreTaxPassive']");
        AddMoreTaxPassive.unbind('click');
        AddMoreTaxPassive.bind('click', { obj: this }, this.onAddMoreTaxDetailsPassiveClick);

        var taxResidencePassive = $("[typeValue='TaxResPassive']");
        taxResidencePassive.unbind('change');
        taxResidencePassive.bind('change', { obj: this }, this.onTaxResidenceChange);


        var taxIdentificationTypePassive = $("[typeValue='TaxTypPassive']");
        taxIdentificationTypePassive.unbind('change');
        taxIdentificationTypePassive.bind('change', { obj: this }, this.onChangeActionTaxType);

        this.tinNumberPassive = $("[typeValue='TFNTxtPassive']");
        this.tinNumberPassive.unbind('keyup');
        this.tinNumberPassive.bind("keyup", { obj: this }, this.validateNextButtonAndControlSelection);

        var removeCntrlPersonPassive = $("[typeValue='removeTaxPassive']");
        removeCntrlPersonPassive.unbind('click');
        removeCntrlPersonPassive.bind("click", { obj: this }, this.onRemoveTaxDetailsClick);

        var removeCntrlPerson = $("[typeValue='removeCntrlPerson']");
        removeCntrlPerson.unbind('click');
        removeCntrlPerson.bind("click", { obj: this }, this.onRemoveControllingPersonClick);
        /**************************************added controlling persons*****************************************/



        this.IsOthersValid = false;

        this.IntialEvents = $("#btnIntial");
        this.IntialEvents.unbind('click');
        this.IntialEvents.bind("click", { obj: this }, this.validateNextButtonAndControlSelection);
        $('#btnIntial').trigger('click');

    },
    onChange: function (events) {
        var _obj = events.data.obj;
        _obj.validateNextButtonAndControlSelection(events);
    },
    onTaxResidenceChange: function (events) {
        var _obj = events.data.obj;
        // fetch the selected country value
        var selectedValue = $(this).val();

        // if tax residence select then no need to act else satisfy the requirement
        if (selectedValue != "") {
            $(this).closest("div").next().find("select[name$='TaxIdentificationType']")[0].selectedIndex = 0;
            // hide TIN Type - USA TIN,NZL IRD,UK UTR,UK NINO
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option").each(function () {
                var val = $(this).val();
                if (val == "NZL_IRD" || val == "USA_TIN" || val == "UK_UTR" || val == "UK_NIN") {
                    if ($(this).parent()[0].nodeName == "SPAN")
                        $(this).parent().hide();
                    else
                        $(this).wrap((navigator.appName == 'Microsoft Internet Explorer' || navigator.appName == 'Netscape') ? '<span>' : null).hide();
                }

            });
            var spanHandler = (navigator.appName == 'Microsoft Internet Explorer' || navigator.appName == 'Netscape') ? '<span>' : " ";
            //based on the residence set the default USA - USA TIN ; NZL - NZL IRD; UK - UK UTR & UK NINO. 
            if (selectedValue == "USA") {
                // $("select[name$='TaxResidence']")
                $(this).closest("div").next().find("option[value='USA_TIN']").unwrap().show();
                $(this).closest("div").next().find("select[name$='TaxIdentificationType']").val('USA_TIN');
            }
            else if (selectedValue == "NZL") {
                $(this).closest("div").next().find("option[value='NZL_IRD']").unwrap().show();
                $(this).closest("div").next().find("select[name$='TaxIdentificationType']").val('NZL_IRD');
            }
            else if (selectedValue == "GBR") {
                $(this).closest("div").next().find("option[value='UK_UTR']").unwrap().show();
                $(this).closest("div").next().find("option[value='UK_NIN']").unwrap().show();//.attr("selected", true)
                $(this).closest("div").next().find("select[name$='TaxIdentificationType']").val('UK_UTR');
            }
            else {
                $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option[value='']").attr("selected", true);
            }
        }
        else {
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option").show();
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option[value='']").attr("selected", true);
        }
        _obj.validateNextButtonAndControlSelection(events);
    },
    onEditAddressClick: function (events) {
        var self = this;
        var index = $(this).attr("editindex");
        var isRegisterandStreetSame = $(".IsSameAsResidencial[index='" + index + "']:checked").val() == "1";
        var urlConfirm = OC.MVC.util.getLink("Holding", "Index");
        var modal = new IC.Public.investorAddressDetails();
        modal.showModalIndex(urlConfirm, index, false, isRegisterandStreetSame, true);
        self.init();
    },

    editStreeePopup: function (events) {
        var index = '';
        if ($(this).attr("index") != undefined && $(this).attr("index") != "") { // multiple
            index = $(this).attr("index");
        }
        if (index == '') {
            if ($(this).val() == "1") {// yes
                $("#streetAddress .address-1").html($("#divRegistered .address-1").html());
                $("#streetAddress .address-2").html($("#divRegistered .address-2").html());
                $("#streetAddress .address-3").html($("#divRegistered .address-3").html());
                $("#streetAddress .address-4").html($("#divRegistered .address-4").html());
                $("#streetAddress .state").html($("#divRegistered .state").html());
                $("#streetAddress .town").html($("#divRegistered .town").html());
                $("#streetAddress .postcode").html($("#divRegistered .postcode").html());
                $("#streetAddress .country").html($("#divRegistered .country").html());

                $("#streetAddress").show();

                // validate at selection
                var _obj = events.data.obj;
                _obj.validateNextButtonAndControlSelection(events);
            }
            else {// If no then display the popup
                var urlConfirm = OC.MVC.util.getLink("Holding", "Index");
                var modal = new IC.Public.investorAddressDetails();
                modal.showModal(urlConfirm, false, false);
            }
        }
        else {
            if ($(this).val() == "1") {// yes
                $("#streetAddress_" + index + " .address-1").html($("#divRegistered_" + index + " .address-1").html());
                $("#streetAddress_" + index + " .address-2").html($("#divRegistered_" + index + " .address-2").html());
                $("#streetAddress_" + index + " .address-3").html($("#divRegistered_" + index + " .address-3").html());
                $("#streetAddress_" + index + " .address-4").html($("#divRegistered_" + index + " .address-4").html());
                $("#streetAddress_" + index + " .state").html($("#divRegistered_" + index + "  .state").html());
                $("#streetAddress_" + index + " .town").html($("#divRegistered_" + index + "  .town").html());
                $("#streetAddress_" + index + " .postcode").html($("#divRegistered_" + index + "  .postcode").html());
                $("#streetAddress_" + index + " .country").html($("#divRegistered_" + index + "  .country").html());

                $("#streetAddress_" + index).show();

                // validate at selection
                var _obj = events.data.obj;
                _obj.validateNextButtonAndControlSelection(events);
            }
            else {// If no then display the popup
                //var urlConfirm = OC.MVC.util.getLink("Holding", "Index");
                //var modal = new IC.Public.investorAddressDetails();
                //modal.showModal(urlConfirm);

                var _obj = events.data.obj;
                var urlConfirm = OC.MVC.util.getLink("Holding", "Index");
                var modal = new IC.Public.investorAddressDetails();
                modal.showModalIndex(urlConfirm, index, _obj.IsOthersValid, false, false);
            }
        }

    },
    //checkTaxDuplication:function (events)
    //{
    //    $("[name='Foo'][value='All']")
    //},
    onRemoveTaxDetailsClick: function (events) {
        if ($(this).children().first().hasClass('disabledLinkButton')) {
            events.preventDefault();
            return;
        }
        var sequence = $(this).attr('countVal');
        var IndexSequenceValue = $(this).attr('taxCountValue');
        $("#AddRemoveFlowType").val("taxCntrlInd");
        //if ($(this).attr('typeValue') == "removeTaxPassive") {
        //    $("#AddRemoveFlowType").val("false");
        //} else {
        //    $("#AddRemoveFlowType").val("true");
        //}

        $("[name='IndexActioned']").val(sequence);
        $("[name='IndexSequenceValue']").val(IndexSequenceValue);
        //$(this).parents('li').hide();
        var form = $("#individualInvestorForm");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "RemoveInvestorDetails");

        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            // cache:false,
            success: function (result) {
                //$("#" + OC.MVC.constants.mainContainer).html('');
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    onRemoveControllingPersonClick: function (events) {
        if ($(this).children().first().hasClass('disabledLinkButton')) {
            events.preventDefault();
            return;
        }
        var sequence = $(this).attr('countVal');
        $("[name='IndexActioned']").val(sequence);
        $("#AddRemoveFlowType").val("cntrlPersonInd");
        //$(this).parents('li').hide();
        var form = $("#individualInvestorForm");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "RemoveInvestorDetails");
        //////



        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            // cache:false,
            success: function (result) {
                //$("#" + OC.MVC.constants.mainContainer).html('');
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    onSkipClick: function (events) {
        var title = 'Individual Investor';
        var displayResource = 'ForeignTax_Residency_InvestorSummary_SkipMessage';
        var formName = "#individualInvestorForm";
        var modal = new IC.Public.foreignTaxResidencyMessage();
        modal.showModal(title, displayResource, formName);
    },
    disPlayDuplicateMessage: function (value) {

        var title = 'Individual Investor';
        var displayResource = value;
        var formName = "GeneralMessage";
        var modal = new IC.Public.foreignTaxResidencyMessage();
        modal.showModal(title, displayResource, formName);
    },
    onNextClick: function (events) {
        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled') || !_obj.checkDuplicationTaxDetails(_obj)) {
            events.preventDefault();
            return;
        }
        var form = $("#individualInvestorForm");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorNext");
        //form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        //}
    },
    onBackClick: function (events) {
        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        var form = $("#individualInvestorForm");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorBack");
        //form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        //}
    },
    onChangeActionTaxResidence: function (events) {
        var _obj = events.data.obj;

        //if ($.inArray($(this).val(), _obj.ids) > -1) {
        //    alert('Tax Residency cannot be duplicated.');
        //    $(this).val('');
        //    events.preventDefault();
        //    return;
        //}
        //if ($(this).val() != '') {
        //    _obj.ids.push($(this).val());
        //}

        //if ($(this).val() == 'USA') {
        //    if ($("#UlHideUs").is(':visible')) {
        //        $("#IndividualInvestor_DateOfBirth").val('')
        //        $("#IndividualInvestor_PlaceOfBirth").val('')
        //    }
        //}
        _obj.validateNextButtonAndControlSelection(events);
    },
    ValidateDate: function (dtValue) {
        var dtRegex = new RegExp(/\b\d{1,2}[\/-]\d{1,2}[\/-]\d{4}\b/);
        return dtRegex.test(dtValue);
    },
    validateDateAndMonth: function (dateValue) {
        try {
            $.datepicker.parseDate('dd/mm/yy', dateValue);// $("[name=DateOfBirth]").val());
        } catch (e) {
            //alert(e);
            return false;
        };
        return true;
    },
    onChangeActionDOB: function (events) {
        var _obj = events.data.obj;
        var date = $("[name='DateOfBirth']").val();
        if (_obj.ValidateDate(date) && _obj.validateDateAndMonth()) {

            var dateValue = $.datepicker.parseDate("dd/mm/yy", $("[name='DateOfBirth']").val());
            var newDateValue = new Date();
            //var dateCurrentValue = $.datepicker.parseDate("dd/mm/yy", newDateValue.getDate());

            if (dateValue > newDateValue) {
                $("[name='DateOfBirth']").val($("[name='IndividualInvestor.DateOfBirthValue']").val());
            }
            else {
                var value = $("[name='DateOfBirth']").val();
                $("[name='IndividualInvestor.DateOfBirthValue']").val(value);
            }

            _obj.validateNextButtonAndControlSelection(events);
        }
        else {
            $("[name='DateOfBirth']").val('');
            $("[name='IndividualInvestor.DateOfBirthValue']").val('');
        }
    },
    onChangeActionTaxType: function (events) {

        if ($(this).val() == 'DNI_TIN' ||
            $(this).val() == 'DNR_TIN' || $(this).val() == 'NO_TIN') {
            $(this).parent().parent().next().hide();
            $(this).parent().parent().next().find('[typevalue="TFNTxtPassive"]').val('');
        }
        else { $(this).parent().parent().next().show(); }
        var _obj = events.data.obj;
        _obj.validateNextButtonAndControlSelection(events);
    },
    onChangeAction: function (events) {
        var _obj = events.data.obj;
        _obj.validateNextButtonAndControlSelection(events);
    },
    validateNextButtonAndControlSelection: function (events) {
        var _obj = events.data.obj;
        var isValid = true;

        $("#ulNFETaxDetails .IsSameAsResidencial").each(function () {
            var index = '';
            if ($(this).attr("index") != undefined && $(this).attr("index") != "") { // multiple
                index = $(this).attr("index");
            }
            var radioGroupName = $(this).attr("groupName");
            if ($("#ulNFETaxDetails input[name='" + radioGroupName + "']:checked").length != 0) {

                $("#streetAddress_" + index).show();

                if ($("#ulNFETaxDetails input[name='" + radioGroupName + "']:checked").val() == "1") {
                    $("#streetAddress_" + index + " .address-1").html($("#divRegistered_" + index + " .address-1").html());
                    $("#streetAddress_" + index + " .address-2").html($("#divRegistered_" + index + " .address-2").html());
                    $("#streetAddress_" + index + " .address-3").html($("#divRegistered_" + index + " .address-3").html());
                    $("#streetAddress_" + index + " .address-4").html($("#divRegistered_" + index + " .address-4").html());
                    $("#streetAddress_" + index + " .state").html($("#divRegistered_" + index + "  .state").html());
                    $("#streetAddress_" + index + " .town").html($("#divRegistered_" + index + "  .town").html());
                    $("#streetAddress_" + index + " .postcode").html($("#divRegistered_" + index + "  .postcode").html());
                    $("#streetAddress_" + index + " .country").html($("#divRegistered_" + index + "  .country").html());

                    $("#streetAddress_" + index).show();
                }
            }
        });
        //_obj.viewStateHandler();
        //added
        //if (($("[name='EntityOrganizationDetails.NonFinancialEntities.ActionMoreJurisdiction'][value='1']").is(':checked'))) {
        //    $("#btnAddMoreTaxDetails").removeAttr('disabled');
        //} else
        //    $("#btnAddMoreTaxDetails").attr("disabled", "disabled");

        /////////////////////////////////////////////////////////////
        //$("[name='EntityOrganizationDetails.NonFinancialEntities.ActivePassiveNFE']").removeAttr('disabled');
        //$("[name='EntityOrganizationDetails.NonFinancialEntities.ActionPassiveNFEBeneficialOwners']").removeAttr('disabled');

        $("#btnAddMore").removeAttr('disabled');

        isValid = _obj.validateAndProcessPassiveNFE(_obj);

        if (isValid)
            _obj.btnNext.parent().removeClass('disabled');
        else
            _obj.btnNext.parent().addClass('disabled');
    },
    validateAndProcessPassiveNFE: function (_obj) {
        //   $("#radioActivePassiveBeneficial").show();
        var isValid = true;


        $("#ulNFETaxDetails").show();
        // $("#btnAddMore").removeAttr('disabled');

        $(".firstName").each(function () {
            if ($(this).val() == "") {
                isValid = isValid && false;
            }
        });

        $(".lastName").each(function () {
            if ($(this).val() == "") {
                isValid = isValid && false;
            }
        });

        $("[name=DOB]").each(function () {
            if ($(this).val() == "") {
                isValid = isValid && false;
            }
        });
        var isTaxValid = _obj.checkTaxValidationPassive(_obj);
        isValid = isTaxValid && isValid;
        //var jurisValid = false;
        $("[typeValue='Jurisdiction']").each(function () {
            var count = $(this).attr("finalCount");
            var taxCount = $("#btnAddMoreTaxDetails" + count).attr("taxCount");
            if (!($("[name='IndividualInvestor.ControllingPersonDetails[" + count + "].ActionMoreJurisdiction']").is(':checked'))) {
                isValid = isValid && false;
                $("#btnAddMoreTaxDetails" + count).attr("disabled", "disabled");
            }
            else {
                if ($("[name='IndividualInvestor.ControllingPersonDetails[" + count + "].ActionMoreJurisdiction'][value='1']").is(':checked')) {
                    $("#btnAddMoreTaxDetails" + count).removeAttr('disabled');
                    $("#btnAddMoreTaxDetails" + count).removeClass('disabledLinkButton');
                    var controlCount = $(this).attr("countval");
                    if ($("ul [disPlay='block'][countval='" + controlCount + "']").length == 1) // Clicked yes but not added more jurisdiction
                        isValid = isValid && false;
                    else
                        isValid = isValid && true;
                }
                else if ($("[name='IndividualInvestor.ControllingPersonDetails[" + count + "].ActionMoreJurisdiction'][value='0']").is(':checked')) {
                    $("#btnAddMoreTaxDetails" + count).attr("disabled", "disabled");
                    isValid = isValid && true;
                }
            }
            if (taxCount > 1) {
                $("#btnAddMoreTaxDetails" + count).removeAttr('disabled');
                $("#btnAddMoreTaxDetails" + count).removeClass('disabledLinkButton');
            }
            if ($("[name='IndividualInvestor.ControllingPersonDetails[" + count + "].ActionMoreJurisdiction'][value='1']").is(':checked') && (taxCount > 1) && isTaxValid) {
                isValid = isValid && true;
            }
        });

        _obj.IsOthersValid = isValid;
        $("#ulNFETaxDetails .IsSameAsResidencial").each(function () {
            var radioGroupName = $(this).attr("groupName");
            if ($("#ulNFETaxDetails input[name='" + radioGroupName + "']:checked").length == 0) {
                isValid = isValid && false;
            }
        });
        return isValid;

        //  isValid = _obj.checkTaxValidation(_obj);

    },
    checkTaxValidationPassive: function (_obj) {
        var isValidTaxResidence = true, isValidTFNTxt = true, isValidTFNType = true, isValidPOB = true;
        isValidDOB = true;
        //var isDisplayUsCitizen = false;
        $("[typeValue='TaxTypPassive']").each(function () {
            if ($(this).val() == 'DNI_TIN' ||
                     $(this).val() == 'DNR_TIN' || $(this).val() == 'NO_TIN') {
                $(this).parent().parent().next().hide();
                $(this).parent().parent().next().find('[typevalue="TFNTxtPassive"]').val('');
            }
            else { $(this).parent().parent().next().show(); }

            if ($(this).val() == '') {
                isValidTFNType = false;
            }
        });
        var residencyCount = 0;
        $("[typeValue='TaxResPassive']").each(function () {
            residencyCount += 1;
            if ($(this).val() == '') {
                isValidTaxResidence = false;
            }

        });

        $("[typeValue='AddMoreTaxPassive']").each(function () {
            var count = parseInt($(this).attr("taxCount"));
            var controllingPersonIndex = parseInt($(this).attr("finalCount"));
            // var index = finalCount - 1;
            //added to disable the link button of removetaxdetails
            if (count == 1) {
                var id = "#btnDeleteTaxDetails" + (count - 1) + "" + controllingPersonIndex;
                $(id).children().first().addClass('disabledLinkButton');
            }
            else {
                var id = "#btnDeleteTaxDetails" + (count - 1) + "" + controllingPersonIndex;
                $(id).children().first().removeClass('disabledLinkButton');
            }


            if (count > 1) {
                $("[name='IndividualInvestor.ControllingPersonDetails[" + controllingPersonIndex + "].ActionMoreJurisdiction']").attr('disabled', 'disabled');
                $("[name='IndividualInvestor.ControllingPersonDetails[" + controllingPersonIndex + "].ActionMoreJurisdiction'][value='1']").attr('checked', true);
            }
            else
                $("[name='IndividualInvestor.ControllingPersonDetails[" + controllingPersonIndex + "].ActionMoreJurisdiction']").removeAttr('disabled');
        });
        //if ($("ul [disPlay='block']").length > 1) {
        //    $("[name='EntityOrganizationDetails.NonFinancialEntities.ActionMoreJurisdiction']").attr('disabled', 'disabled');
        //}
        //else
        //    $("[name='EntityOrganizationDetails.NonFinancialEntities.ActionMoreJurisdiction']").removeAttr('disabled');


        $("[typeValue='TFNTxtPassive']").each(function () {
            if (!$(this).parent().parent().is(':visible')) {
                $(this).val() == '';
            }
            if ($(this).val().trim() == '' && $(this).parent().parent().is(':visible')) {
                isValidTFNTxt = false;
            }
        });
        return (isValidTaxResidence && isValidTFNTxt && isValidTFNType); //&& isValidPOB && isValidDOB);
    },
    checkDuplicationTaxDetails: function (_obj) {

        var arraycominations = [];
        var taxComb = ''; var duplicated = false;

        $("[typeValue='TaxTypPassive']").each(function () {
            taxComb = '';
            var controllingPersonIndex = $(this).attr("idValue");
            var countVal = $(this).attr("selectedTaxDetails");
            taxComb = $("[typeValue='TaxResPassive'][selectedTaxDetails='" + countVal + "']").val();
            taxComb = taxComb + $(this).val();
            if ($(this).val() == 'DNI_TIN' ||
                     $(this).val() == 'DNR_TIN' || $(this).val() == 'NO_TIN') {
                //$(this).parent().parent().next().hide();
            }
            else {
                var val = $("[typeValue='TFNTxtPassive'][selectedTaxDetails='" + countVal + "']").val();
                taxComb = taxComb + $.trim(val);
            }
            arraycominations.push(taxComb);
        });
        var temp = [];
        $.each(arraycominations, function (key, value) {
            if ($.inArray(value, temp) === -1) {
                temp.push(value);
            } else {
                duplicated = true;
                //_obj.btnNext.parent().addClass('disabled');
                // alert("Tax details duplicated");
                _obj.disPlayDuplicateMessage("ForeignTaxResidency_TaxDetails_Duplicate_Message");
            }
        });
        return (duplicated == false);
    },
    onAddMorePassiveClick: function (events) {

        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled') || !_obj.checkTaxValidationPassive(_obj)) {// || !_obj.checkDuplicationTaxDetails(_obj)) {
            events.preventDefault();
            return;
        }
        $("#AddRemoveFlowType").val("cntrlIndPersonDA");

        var sequence = $(this).attr('countVal');
        $("[name='IndexActioned']").val(sequence);

        $("#ScreenType").val("IndividualInvestor");
        var form = $("#individualInvestorForm");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "AddMoreFatcaTaxDetails");
        form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        //}
    },
    onAddMoreTaxDetailsPassiveClick: function (events) {

        var _obj = events.data.obj;
        if ($(this).parent().hasClass('disabled') || !_obj.checkTaxValidationPassive(_obj)) {// || !_obj.checkDuplicationTaxDetails(_obj)) {
            events.preventDefault();
            return;
        }
        $("#AddRemoveFlowType").val("taxIndPersonDA");

        var sequence = $(this).attr('countVal');
        $("[name='IndexActioned']").val(sequence);

        $("#ScreenType").val("IndividualInvestor");
        var form = $("#individualInvestorForm");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "AddMoreFatcaTaxDetails");
        form.validate(IC.Public.getValidatorCfg());
        //if (form.valid()) {
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
        //}
    },

    //onEditAddressClick: function (events) {
    //    var urlConfirm = OC.MVC.util.getLink("Holding", "Index");
    //    var modal = new IC.Public.investorAddressDetails();
    //    modal.showModal(urlConfirm);
    //},
    onOkClick: function (events) {
        $.modal.close();
    }
};

function validateTINForIndividual(textValue) {
    var id = $(textValue).attr("idval");
    var taxResidence = $("#IndividualInvestor_InvestorTaxDetails_" + id + "__TaxResidence").val();
    var taxIdType = $("#IndividualInvestor_InvestorTaxDetails_" + id + "__TaxIdentificationType").val();
    validateTIN(textValue, taxResidence, taxIdType);

}
function taxResidenceOnPageLoad() {
    // hide NZL/ UK and USA options
    $("select[name$='TaxResidence']").closest("div").next().find("select[name$='TaxIdentificationType'] option").each(function () {
        var val = $(this).val();
        if (val == "NZL_IRD" || val == "USA_TIN" || val == "UK_UTR" || val == "UK_NIN") {
            //$(this).hide(); IE fix
            //$(this).unwrap('<span>').wrap((navigator.appName == 'Microsoft Internet Explorer' || navigator.appName == 'Netscape') ? '<span>' : null).hide();

            if ($(this).parent()[0].nodeName == "SPAN")
                $(this).parent().hide();
            else
                $(this).wrap((navigator.appName == 'Microsoft Internet Explorer' || navigator.appName == 'Netscape') ? '<span>' : null).hide();
        }

    });
    var spanHandler = (navigator.appName == 'Microsoft Internet Explorer' || navigator.appName == 'Netscape') ? '<span>' : null;
    // based on the selection make availablity to the user selection
    $("select[name$='TaxResidence']").each(function () {
        // fetch the selected country value
        var selectedValue = $(this).val();
        if (selectedValue == "USA") {
            // $("select[name$='TaxResidence']")
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option[value='USA_TIN']").unwrap().show();
        }
        else if (selectedValue == "NZL") {
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option[value='NZL_IRD']").unwrap().show();
        }
        else if (selectedValue == "GBR") {
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option[value='UK_UTR']").unwrap().show();
            $(this).closest("div").next().find("select[name$='TaxIdentificationType'] option[value='UK_NIN']").unwrap().show();
        }
    });
};/// <reference path="ic.public.foreignTaxResidency.investorCertificationSummary.js" />
/// <reference path="~/Scripts/common.js" />

IC.Public.updatecomplete = function () {
    this.init();
}

IC.Public.updatecomplete.prototype = {

    init: function () {
        var isCampaign = $("#IsCampaignEnabled");
        
        if (isCampaign != undefined && isCampaign.length != 0 && isCampaign.val().toLowerCase() == "true") {
            if ($("#IsMissIngHoldingExist").val().toLowerCase() == "false") {
                $(".redirect").show();
               // $("input[name=ActionUpdateComplete]").attr("disabled", "disabled");
            }
            IC.Public.HideMenu();
        }
        
        this.btnSelfCertify = $("#btnSelfCertify");
        this.btnSelfCertify.bind('click', { obj: this }, this.onSelfCertifyClick);
        this.btnSkip = $("#btnSkip");
        this.btnSkip.bind('click', { obj: this }, this.onSkipClick);
        this.btnBackToPortFolio = $("#btnBackToPortFolio");
        this.btnBackToPortFolio.bind('click', { obj: this }, this.onReturnToPortfolioClick);
        //
        this.btnBackToHoldingDetails = $("#btnBackToHoldingDetails");
        this.btnBackToHoldingDetails.bind('click', { obj: this }, this.onReturnToHoldingDetailsClick);
        //
        this.investorCertificationGrid = $("#InvestorCertificationGrid");
        this.investorCertificationGrid.bind("grid_gridComplete", { obj: this }, function (events, gridContext) {
            rowCount = gridContext;
            // $("#drpFilter").parent().remove();
            var _obj = events.data.obj;

            $(".holdingFilterForm").each(function (val) {
                if ($(this).children().first().attr("id") != "drpFilterHolding") {
                    var htm = $(this).children().first();
                    $(this).children().first().parent().remove();
                    $("#divParentHolding").html(htm);
                }
            });

            $(this).find("a.certify").each(function (index) {
                $(this).bind("click", { obj: _obj }, _obj.onSelfCertifyGridClick);
            });
        });

        this.ActionSelfCertificate = $("input[name='ActionUpdateComplete']");
        this.ActionSelfCertificate.unbind('click');
        this.ActionSelfCertificate.bind("click", { obj: this }, this.onSelfCertifyOptionClick);
        //
        //this.holdingFilter = $("form.holdingFilterForm");
        //this.holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterGrid);
        // this.holdingFilter.bind("holdingSelect", { obj: this }, this.onHoldingSelect);
    },
    onSelfCertifyOptionClick: function (events) {
        var url = "/ForeignTaxResidency/ForeignTaxResidencyCertification?isRedirectedFromUpdateComplete=" + true;
        if ($(this).val() == "0") {
            $(".redirect").show();
        }
        else {
            $(".redirect").hide();
            if ($("#IsCampaignEnabled").val().toLowerCase() == "true") {
                url = "/ForeignTaxResidency/ForeignTaxResidencyCertificationIndex?isFATCAMissing=" + true;
                window.location.href = url;
            }
            else {
                //url = "/ForeignTaxResidency/ForiegnTaxResidencyCertification?isRedirectedFromUpdateComplete=" + true;
                OC.MVC.util.loadMainContainerView(url, null);
            }

        }

    },
    getCertifySequences: function () {
        var sequenceTotal = '';
        $.each($('.certify'), function (i, item) {
            var value = $(this).attr('seqNo');
            if (value) {
                if (sequenceTotal == '') {
                    sequenceTotal = value;
                }
                else
                    sequenceTotal = sequenceTotal + ";" + value;
            }
        });
        return sequenceTotal;
    },
    onReturnToPortfolioClick: function (events) {
        //var url = OC.MVC.util.getLink("Holdings", "Index");
        //OC.MVC.util.loadMainContainerView(url);
        var isEmployeeLogin = $('#IsEmployeeLogin').val();
        var issuerCode = $('#ContextIssuerCode').val();
        if (isEmployeeLogin != undefined && isEmployeeLogin.toLowerCase() == "true") {
            {
                window.location.href = "/Employee/" + issuerCode;
            }
        }
        else {
            var urlLocation = "/";
            window.location.href = urlLocation;
        }
    },
    onReturnToHoldingDetailsClick: function (events) {
        //var url = OC.MVC.util.getLink("HoldingDetails", "Index");
        //OC.MVC.util.loadMainContainerView(url);
        var isEmployeeLogin = $('#IsEmployeeLogin').val();
        var issuerCode = $('#ContextIssuerCode').val();
        if (isEmployeeLogin != undefined && isEmployeeLogin.toLowerCase() == "true") {
            {
                window.location.href = "/Employee/" + issuerCode;
            }
        }
        else {
            var urlLocation = "/";
            window.location.href = urlLocation;
        }
    },
    onSelfCertifyGridClick: function (events) {
        var _obj = events.data.obj;
        var data = { viewKey: $("#ViewKey").val(), seqNo: $(this).attr("seqNo") };
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "InvestorSelectionOnUpdateComplete");
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },
    onSkipClick: function (events) {
        var _obj = events.data.obj;
        var values = _obj.getCertifySequences();
        $("#SkipSequenceNumbers").val(values);
        var title = 'Update Complete';
        //if($("#InvestorTypeValue").val()==Investor)
        var displayResource = 'ForeignTax_Residency_InvestorSummary_SkipMessage';
        var formName = "#updateCompleteInvestorForm";
        var modal = new IC.Public.foreignTaxResidencyMessage();
        modal.showModal(title, displayResource, formName);
    },
    onSelfCertifyClick: function (events) {
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "ForeignTaxResidencyCertification");
        OC.MVC.util.loadMainContainerView(url, null);
        //}
    },

};IC.Public.ConflictResolve = function () {
    this.init();
} 
IC.Public.ConflictResolve.prototype = {
    init: function () {//$('table[id^=Tab_]')
        this.grid = $("table[id^=FATCAConflictGrid_]");
        this.grid.unbind(OC.MVC.grid.events.loadComplete);
        this.grid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onLoadComplete);

        this.gotoSelfCertifcation = $("#btnGotoSelfCertification");
        this.gotoSelfCertifcation.unbind("click");
        this.gotoSelfCertifcation.bind("click", { obj: this }, this.gotoSelfCertifcationClick);

        // add * in heading
        $(".ui-jqgrid-htable th:last-child div").html(function () { return $(this).html().replace("Conflict Explanation", "Conflict Explanation <span class='warning-span'>*</span>"); });
        this.submitButton = $("#btnNext");
        this.submitButton.unbind(this.submitButtonClick);
        this.submitButton.bind("click", { obj: this }, this.submitButtonClick);

    },
    submitButtonClick: function ()
    {
        // If valid then allow to submit
        if (IC.Public.ForeignTaxResidency.ConflictResolve.validateAndEnableSubmit()) {
            var url = OC.MVC.util.getLink("ForeignTaxResidency", "ResolveFATCAConflict");
            var data = $("#resolveConflictForm").serializeObject();
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                data: data,
                type: 'POST',
                success: function (result) {
                    $("#" + OC.MVC.constants.mainContainer).html(result);
                    IC.Public.hideLoading();
                },
                error: function (err) {
                    $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },
    gotoSelfCertifcationClick: function () {
        var form = $("#FATCAConflictForm");
        var data = form.serializeObject();
        var url = OC.MVC.util.getLink("ForeignTaxResidency", "ForeignTaxResidencyCertificationIndex");
        form.validate(IC.Public.getValidatorCfg());
        if (form.valid()) {
            IC.Public.showLoading("");
            $.ajax({
                url: url,
                type: 'POST',
                data: data,
                success: function (result) {
                    $("#" + OC.MVC.constants.mainContainer).html(result);
                    IC.Public.hideLoading();
                },
                error: function (err) {
                    $("#" + OC.MVC.constants.mainContainer).html(err.responseText);
                    IC.Public.hideLoading();
                }
            });
        }
    },
    reasonSelectChange: function () {
        if ($(this).val() == "ACC") {
            $(this).parent().parent().next().find("textarea")
                .removeAttr("disabled")
                .val(""); // allow to manual entry
        }
        else { // resolve flow => span / td / next td and find ta
            $(this).parent().parent().next().find("textarea")
                            .attr("disabled","disabled")
                            .removeClass("error")
                            .val("Registry Data or Self-Certification Data to be amended by Investor to resolve conflict noted.");    
        }
        //IC.Public.ForeignTaxResidency.ConflictResolve.validateAndEnableSubmit();
    },
    onLoadComplete: function (events) {
        $(".fc-resolve-option").on("change", { obj: this }, IC.Public.ConflictResolve.prototype.reasonSelectChange);
        $(".ui-jqgrid-pager").hide();
        //IC.Public.ForeignTaxResidency.ConflictResolve.validateAndEnableSubmit();
        //$(".fc-explanation").on("keyup", function () {
        //    if ($(this).val() == "") { $(this).addClass("error"); } else { $(this).removeClass("error"); }
        //});
    },
}

IC.Public.ForeignTaxResidency.ConflictResolve = {
    validateAndEnableSubmit: function () {
        var isValid = true;
        $("#btnNext").parent().removeClass("disabled");
        $(".fc-explanation").each(function () {
            if ($(this).val() == "") { isValid = false; $(this).addClass("error"); } else { $(this).removeClass("error"); }
        });

        //if (!isValid)
        //    $("#btnNext").parent().addClass("disabled");
        
        return isValid;
    },
    conflictTypeTooltip: function (cellValue, options, rowObject) {
        index = options.gid.split('_')[1];
        var div = $('<div>');
        div.html(cellValue);
        if (cellValue == "Address Country") {
            $(".cluetip-addressType_" + index).detach().appendTo(div);
        }
        else if (cellValue == "Withholding Tax Code") {
            $(".cluetip-TaxCode_" + index).detach().appendTo(div);
        }
        else if (cellValue == "Payment Instruction") {
            $(".cluetip-payment_" + index).detach().appendTo(div);
        }
        else if (cellValue == "Deceased") {
            $(".cluetip-deceased_" + index).detach().appendTo(div);
        }
        else {
            div.html(cellValue);
        }

        return div.html();
    }
}
;IC.Public.onlineSaleSecuritiesTransactions = function () {
    this.init();
};
IC.Public.onlineSaleSecuritiesTransactions.prototype = {
    init: function () {
        var gridLinksInitialised = false;
        var rowCount = 0;
        this.grid = $("#OnlineSaleSecuritiesTransactionsGrid");
        this.grid.bind("grid_gridComplete", { obj: this }, function (events, gridContext) {
            rowCount = gridContext;
            var _obj = events.data.obj;
            $(this).find("a.view-confirmation").each(function (index) {
                if ($(this).attr('orderId') && $(this).attr('orderId').trim().length > 0) {
                    gridLinksInitialised = true;
                    $(this).unbind("click");
                    $(this).bind("click", { obj: _obj }, _obj.viewConfirmationClick);

                }
            });
            //$("#divbottomPanel").hide();
        });

        // Sometimes the grid is loaded before the gridComplete event can be binded, so we need to reload so that View Confirmation click event is binded
        if (rowCount > 0 && !gridLinksInitialised) {
            this.grid.trigger('reloadGrid');
        }
        var holdingFilter = $("form.holdingFilterForm");
        if (holdingFilter.length) {
            holdingFilter.unbind("applyHoldingFilter");
            holdingFilter.bind("applyHoldingFilter", { obj: this }, this.onFilterClick);
        }
        this.action = $("[name=Action]");
        this.action.unbind('click');
        this.action.bind('click', { obj: this }, this.onActionClick);
        this.viewKey = $("#ViewKey");
   
    },

    onFilterClick: function (events) {
        var url = IC.Public.urls.OnlineSale.OnlineSaleSellSecurities;
        OC.MVC.util.loadMainContainerView(url, null);
    },

    onActionClick: function (events) {
        var _obj = events.data.obj;
        var value = $(this).val();
        var url = IC.Public.urls.OnlineSale.OnlineSaleSellSecurities;
        $('#ulFatcaValidation').show();
        $('#divButtons').show();
        $('#OnlineSale_AmountToSell').show();
        $('#Sell_All').show();
        if (value == IC.Public.onlineSaleOptions.transactions) {
            url = IC.Public.urls.OnlineSale.OnlineSaleTransactionsList;
            $('#ulFatcaValidation').hide();
            $('#divErrorContainer').hide();
            $('#divButtons').hide();
            $('#OnlineSale_AmountToSell').hide();
            $('#Sell_All').hide();
        }
        //OC.MVC.util.loadView("OnlineSaleContainer", url, { viewKey: _obj.viewKey.val() });
        OC.MVC.util.loadMainContainerView(url, { viewKey: _obj.viewKey.val() });
    },

    viewConfirmationClick: function () {
        var orderId = $(this).attr('orderId');
        var url = OC.MVC.util.getLink("Securities", "OnlineSaleViewConfirmation");
        window.location = url + "?viewKey=" + $("#ViewKey").val() + "&orderId=" + orderId;
    }
};
;IC.Public.onlineSaleOptions = {
    sell: 1,
    transactions: 2
};

IC.Public.fatcaValidationOptions = {
    yes: 1,
    no: 2
};

IC.Public.onlineSaleSellSecurities = function (validateTransactionPassword) {
    this.init(validateTransactionPassword);
};

IC.Public.onlineSaleSellSecurities.prototype = {
    init: function (validateTransactionPassword) {
        this.validateTransactionPassword = validateTransactionPassword;
        this.shareSalePackEligible = $("#ShareSalePackEligible").val().toLowerCase();
        this.validRequest = $("#ValidRequest").val().toLowerCase();

        if (this.validRequest == 'false' && this.shareSalePackEligible == 'true') {
            var modal = new IC.Public.onlineSaleValidateInvestorPopUp();
            modal.showModal();
            IC.Public.hideLoading();
        }

        if (this.validRequest == 'false' && this.shareSalePackEligible == 'false') {
            $('#ulFatcaValidation').hide();
            $('#OnlineSale_AmountToSell').hide();
            $('#Sell_All').hide();
            $('#grid-container').hide();
            $('#securitiesCalculation').hide();
            $('#divButtons').hide();
        }

        this.action = $("[name=Action]");
        this.action.unbind('click');
        this.action.bind('click', { obj: this }, this.onActionClick);
        this.viewKey = $("#ViewKey");

        this.isUsCitizen = $("[name=IsUSCitizen]");
        this.isUsCitizen.bind('click', { obj: this }, this.onUSCitizenClick);
        this.viewKey = $("#ViewKey");

        this.IsAustralianIdentification = $("[name=IsAustralianIdentification]");
        this.IsAustralianIdentification.bind('click', { obj: this }, this.onAustralianIdentificationClick);
        this.viewKey = $("#ViewKey");

        this.onlineSaleSellSecuritiesContainer = $('#onlineSaleSellSecuritiesContainer');
        this.btnContinue = $("#btnOnlineSaleContinue");
        this.btnContinue.bind("click", { obj: this }, this.onContinueButtonClicked);

        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind('click');
        this.btnCancel.bind("click", { obj: this }, this.onCancelButtonClick);

        //ViewBar change event
        var self = this;
        var holdingFilter = $("form.holdingFilterForm");
        if (holdingFilter.length) {
            holdingFilter.unbind("applyHoldingFilter");
            holdingFilter.bind("applyHoldingFilter", { obj: self }, self.onFilterClick);
        }

        var self = this;
        this.EstimatedValue = $("#EstimatedValue");
        this.blockExerciseMethodProcessing = false;
        this.registerEvents(self);

        this.TotalSecuritiesSelected = $("#TotalSecuritiesSelected");

        this.TotalSecurities = $("#TotalSecurities");

        highlightMenu('sell');
        if ($("#portfolioMenuLink").attr("type") != undefined)
            $("#portfolioMenuLink").html("<small>▼</small>My portfolio");

        var isThirdPartyImpersonateUser = $("#IsThirdPartyImpersonateUser").val();
        if (isThirdPartyImpersonateUser != undefined) {
            if (isThirdPartyImpersonateUser.toLowerCase() == "true") {
                $("#portfolioMenuLink").html("<small>▼</small>Third Party Portfolios");
                $("#btnOnlineSaleContinue").attr('disabled', true);
            } else {
                $("#btnOnlineSaleContinue").removeAttr('disabled');
            }
        }

        //this.registerGridRelatedControlEvents();

    },

    onFilterClick: function (events) {
        var url = IC.Public.urls.OnlineSale.OnlineSaleSellSecurities;
        OC.MVC.util.loadMainContainerView(url, null);
    },



    onActionClick: function (events) {
        var _obj = events.data.obj;
        var value = $(this).val();
        var url = IC.Public.urls.OnlineSale.OnlineSaleSellSecurities;
        $('#ulFatcaValidation').show();
        $('#divButtons').show();
        $('#OnlineSale_AmountToSell').show();
        $('#Sell_All').show();
        if (value == IC.Public.onlineSaleOptions.transactions) {
            url = IC.Public.urls.OnlineSale.OnlineSaleTransactionsList;
            $('#ulFatcaValidation').hide();
            $('#divErrorContainer').hide();
            $('#divButtons').hide();
            $('#OnlineSale_AmountToSell').hide();
            $('#Sell_All').hide();
        }
        //OC.MVC.util.loadView("OnlineSaleContainer", url, { viewKey: _obj.viewKey.val() });
        OC.MVC.util.loadMainContainerView(url, { viewKey: _obj.viewKey.val() });
    },

    onUSCitizenClick: function (events) {
        var _obj = events.data.obj;
        var value = $(this).val();

        if (value == IC.Public.fatcaValidationOptions.yes) {
            $('#divFatcaAustraliaValidation').hide();
            $('#divErrorContainer').show();
            $('#divErrorContainer').html($('#FatcaInvalid').val());
            $("[name=IsAustralianIdentification][value=1]").attr('checked', false);
            $("[name=IsAustralianIdentification][value=2]").attr('checked', false);
        }
        else {
            $('#divFatcaAustraliaValidation').show();
            $('#divErrorContainer').hide();
        }
        _obj.validateControls(events);
    },
    validateControls: function (events) {
        var no = IC.Public.fatcaValidationOptions.no;
        var yes = IC.Public.fatcaValidationOptions.yes;
        var isThirdPartyImpersonateUser = $("#IsThirdPartyImpersonateUser").val()
        var selectedSecurities = this.getSecuritiesToSell();
        var isValid = (selectedSecurities != '');
        if (isValid && !$("[name=IsUSCitizen]").is(':checked')) {
            isValid = false;
        }
        else if (isValid && $("[name=IsUSCitizen][value=1]").is(':checked')) {
            isValid = false;
        }
        else if (isValid && $("[name=IsUSCitizen][value=2]").is(':checked')) {
            if (!$("[name=IsAustralianIdentification]").is(':checked')) {
                isValid = false;
            }
            else if ($("[name=IsAustralianIdentification][value=2]").is(':checked')) {
                isValid = false;
            }
        }
        isValid = isValid && ($("#HasUnregisteredHrns").val().toLowerCase() == "false") && (isThirdPartyImpersonateUser.toLowerCase()=="false");
        if (isValid)
            this.btnContinue.parent().removeClass('disabled');
        else
            this.btnContinue.parent().addClass('disabled');
    },

    onAustralianIdentificationClick: function (events) {
        var _obj = events.data.obj;
        var value = $(this).val();

        if (value == IC.Public.fatcaValidationOptions.no) {
            $('#divErrorContainer').show();
            $('#divErrorContainer').html($('#AustralianIdentificationInvalid').val());
        }
        else {
            $('#divErrorContainer').hide();
        }
        _obj.validateControls(events);
    },

    onContinueButtonClicked: function (events) {
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
       
        var self = events.data.obj;

        //Prepare "SecurityId|SecurityToSell" array string
        var selectedSecurities = self.getSecuritiesToSell();
        if (selectedSecurities == '')
            return;

        //Prepare the Form for submit
        var form = $("form#OnlineSaleSellSecuritiesForm");
        var formdata = form.serializeObject();
        var firstName = formdata.FirstName;
        var middleName = formdata.MiddleName;
        var lastName = formdata.LastName;
        var data = { viewKey: self.viewKey.val(), SelectedSecurities: selectedSecurities, firstName: firstName, middleName: middleName, lastName: lastName }
        var url = OC.MVC.util.getLink("Securities", "OnlineSaleTransactionMethod");
        IC.Public.showLoading("");

        //Disable the Continue button to prevent more than one click            
        $(this).parent().addClass('disabled')

        //Submit the Form through AJAX
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();

            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
                $(this).parent().removeClass('disabled');
            }
        });
    },

    onCancelButtonClick: function (events) {
        var self = events.data.obj;
        var urlLocation = "/";
        window.location.href = urlLocation;
    },

    getSecuritiesToSell: function () {
        var selectedSecurities = '';
        $('.securitiesToSell').each(function () {
            var regex = /^\d*[1-9]\d*$/;
            var securityToSell = $(this).val().replaceAll(",", "");
            var security = parseInt(securityToSell);
            if (securityToSell == '' || !(securityToSell.match(regex))|| security == 0)
                return;
            var securityId = $(this).attr('securityId');

            if (selectedSecurities == '')
                selectedSecurities = securityId + "|" + securityToSell;
            else
                selectedSecurities += "," + securityId + "|" + securityToSell;
        });

        return selectedSecurities;
    },


    // Online sale securities grid related methods
    registerEvents: function (self) {
        this.registerGridRelatedControlEvents();
    },

    registerGridRelatedControlEvents: function () {
        this.sellAllCheckBox = $("#SellAll");
        this.sellAllCheckBox.unbind('click');
        this.sellAllCheckBox.bind('click', { obj: this }, this.onSellAllClick);

        this.grid = $("#OnlineSaleSellSecuritiesGrid");
        this.grid.unbind(OC.MVC.grid.events.loadComplete);
        this.grid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, this.onLoadComplete);
    },

    onSellAllClick: function (events) {
        var _obj = events.data.obj;
        var value = $(this).is(':checked');
        _obj.toggleOptionsToTransactValues(value,events);
    },

    toggleOptionsToTransactValues: function (value,events) {
        var self = this;
        $.each($('.securitiesToSell'), function (i, item) {
            var tr = $(item).parents('tr');
            var securitiesToSell = parseInt(tr.getColumnValue('Available Securities').replaceAll(",", ""));
            var securitiesToSellTextBox = $(item);
            if (value == true) {
                securitiesToSellTextBox.val(securitiesToSell);
            } else {
                securitiesToSellTextBox.val('');
            }
            self.handleGridSellSecuritiesChange(securitiesToSellTextBox,events);
        });
    },

    handleGridSellSecuritiesChange: function (securitiesToSellControl,events) {
        var self = this;
        var tr = securitiesToSellControl.parents('tr');
        var value = securitiesToSellControl.val();
        var estimatedValue = $('#EstimatedValue');
        var totalSecuritiesSelected = $("#TotalSecuritiesSelected");
        if (value) {
            var securityPrice = tr.getColumnValue('PricePerSecurity');
            var _securityPrice = parseFloat(securityPrice);
            var _number = parseInt(securitiesToSellControl.val());

            if (!isNaN(_securityPrice) && !isNaN(_number)) {
                var securitiesToSell = parseInt(tr.getColumnValue('Available Securities').replaceAll(",", ""));
                if (securitiesToSell < _number) {
                    alert('Securities to Sell must be less or equal to Available Securities');
                    securitiesToSellControl.val('');

                }

                var total = _securityPrice * securitiesToSellControl.val();

            }
        } else {

            //estimatedValue.text('');
        }
        self.updateTotals();
        self.validateControls(events);
    },

    onLoadComplete: function (events, gridObj, data) {
        var _obj = events.data.obj;

        var securitiesToSell = $('.securitiesToSell');
        securitiesToSell.numeric();
        securitiesToSell.unbind('keyup');
        securitiesToSell.bind('keyup', { obj: _obj }, _obj.onNumberChange);

        securitiesToSell.each(function () {
            var hasValue = ($(this).val() == "");
            _obj.blockExerciseMethodProcessing = hasValue;
            $(this).trigger('keyup');
            _obj.blockExerciseMethodProcessing = false;
        });
    },

    onNumberChange: function (events) {
        var self = events.data.obj;
        self.handleGridSellSecuritiesChange($(this),events);
    },

    updateTotals: function () {
        var self = this;
        var availableSecurities = 0;
        var _securitiesToSell = $('.securitiesToSell').map(function () {
            var _value = parseInt($(this).val());
            return isNaN(_value) ? 0 : _value;
        }).get();

        this.TotalSecuritiesSelected.text($.formatNumber(_securitiesToSell.sum(), "#,0"));

        var _estimatedValue = $('.securitiesToSell').map(function () {
            var pricePerSecurity = $(this).attr("amount");
            var _value = (parseInt($(this).val()) * parseFloat(pricePerSecurity));
            return isNaN(_value) ? 0 : _value;
        }).get();
        this.EstimatedValue.text($.formatNumber(_estimatedValue.sum(), "#,0.00"));

        //Check/Uncheck Sellall Checkbox
        $.each($('.securitiesToSell'), function (i, item) {
            var tr = $(item).parents('tr');
            availableSecurities += parseInt(tr.getColumnValue('Available Securities').replaceAll(",", ""));        
        });

        var _TotalSecuritiesSelected = _securitiesToSell.sum();
        if (_TotalSecuritiesSelected == availableSecurities && _TotalSecuritiesSelected!=0) {
            $("#SellAll").attr('checked', true);
        } else {
            $("#SellAll").attr('checked', false);
        }

        this.TotalSecurities.text($.formatNumber(availableSecurities, "#,0"));
    },
};IC.Public.onlineSaleSellSecuritiesGridDetails = function () {
    this.init();
};

IC.Public.onlineSaleSellSecuritiesGridDetails.prototype = {
    init: function () {
       

    },

  
};IC.Public.onlineSaleSecuritiesTransactions.transactionMethod = function (validateTransactionPassword) {
    this.init(validateTransactionPassword);
};

IC.Public.onlineSaleSecuritiesTransactions.transactionMethod.prototype = {
    init: function (validateTransactionPassword) {
        //Member Variables
        this.validateTransactionPassword = validateTransactionPassword;
        this.TotalSharesSelected = $("#TotalSharesSelected");
        this.EstimatedValue = $("#EstimatedValue");
        this.SharesToTransactPreviousValue = "";

        //Campaign
        this.viewKey = $("#ViewKey");

        //this.methodDetailItems = $('.method-detail-items').find('input[type=radio]');
        this.methodDetailItemsMKT = $("#sales_methods_MKT");
        this.methodDetailItemsMKT.unbind('click');
        this.methodDetailItemsMKT.bind('click', { obj: this }, this.onMarketOrderClick);

        this.methodDetailItemsLIM = $("#sales_methods_LIM");
        this.methodDetailItemsLIM.unbind('click');
        this.methodDetailItemsLIM.bind('click', { obj: this }, this.onSellDayLimitClick);
        // sales_methods_MKT

        this.paymentMethod = $('input[name=PaymentMethod]');
        this.paymentMethod.unbind('click');
        this.paymentMethod.bind('click', { obj: this }, this.onPaymentMethodClick);
        this.paymentDetails = $('.method-details');

        var self = this;
        this.dayLimitMinPrice = $('#dayLimitMinPrice');
        this.dayLimitMinPrice.unbind('keyup');
        this.dayLimitMinPrice.bind('keyup', function () {
            self.processContinueButton();
        });

        this.TermsAndConditionsAccepted = $("#TermsAndConditionsAccepted");
        this.TermsAndConditionsAccepted.change(function () {
            self.processContinueButton();
        });

        this.btnContinue = $('#btnContinue');
        this.btnContinue.unbind('click');
        this.btnContinue.bind("click", { obj: this }, this.onContinueButtonClick);

        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind('click');
        this.btnCancel.bind("click", { obj: this }, this.onCancelClick);

        //this.rdoSellByDayLimit = $("#sales_methods_LIM");
        //this.rdoSellByDayLimit.unbind('click');
        //this.rdoSellByDayLimit.bind('click', this.onSellDayLimitClick);

        //ViewBar change event
        var holdingFilter = $("form.holdingFilterForm");
        if (holdingFilter.length) {
            holdingFilter.unbind("applyHoldingFilter");
            holdingFilter.bind("applyHoldingFilter", { obj: self }, self.onFilterClick);
        }

        this.updateBankAccountLink = $('.update-bank-account');
        this.updateBankAccountLink.unbind('click');
        this.updateBankAccountLink.bind("click", { obj: this }, this.onUpdateBankAccountLinkClick);

        //This will only happen when BACK button is clicked in second screen
        if ($("input[name=TransactionMethods]:checked").length > 0) {
            this.showTermsAndConditions($("input[name=TransactionMethods]:checked").val());
        }

        if (IC.Public.IsThirdPartyImpersonateUser()) $("#btnContinue").attr("disabled", true);
        this.isLoading = false;
    },


    onSharesToTransactFocus: function (events) {
        var _obj = events.data.obj;
        _obj.SharesToTransactPreviousValue = $(this).val();

    },
    getSecuritiesToSell: function () {
        var selectedSecurities = '';
        $('.securitiesToSell').each(function () {
            var securityToSell = $(this).attr('selectedvalue');
            var securityId = $(this).attr('securityId');

            if (selectedSecurities == '')
                selectedSecurities = securityId + "|" + securityToSell;
            else
                selectedSecurities += "," + securityId + "|" + securityToSell;
        });

        return selectedSecurities;
    },
    onSharesToTransactNumberChange: function (events) {
        var _obj = events.data.obj;

        var tr = $(this).parents('tr');

        //It should be sum of all rows
        var availableSharedValue = tr.getColumnValue('AvailableShares'); // $(tr).children().eq(4).text();


        var sharesToTransactValue = $(this).val();
        var sharesToTransactPreviousValue = _obj.SharesToTransactPreviousValue;

        if (sharesToTransactValue) {

            var _availableSharedValue = parseInt(availableSharedValue.replaceAll(",", ""));
            var _sharesToTransactValue = parseInt(sharesToTransactValue.replaceAll(",", ""));
            var _sharesToTransactPreviousValue = parseInt(sharesToTransactPreviousValue.replaceAll(",", ""));

            if (!isNaN(_availableSharedValue) && !isNaN(_sharesToTransactValue)) {
                if (_availableSharedValue < _sharesToTransactValue) {
                    alert('Securities to Transact must be less or equal to Available Securities');
                    $(this).val('');
                    _obj.updateTotals();
                    return;
                }

                //if (_sharesToTransactValue == totalAvailableShares) {
                //    $("#IsSellAll").attr('checked', true);
                //} else {
                //    $("#IsSellAll").attr('checked', false);
                //}

                if (_sharesToTransactValue != _sharesToTransactPreviousValue) {
                    _obj.toggleSellNumberInputs(false);
                }
            }

            _obj.updateTotals();
        }
        _obj.processContinueButton();
    },

    onSellDayLimitClick: function (events) {
        var _obj = events.data.obj;
        $("#dayLimitMinPrice").focus();
        _obj.processContinueButton();
    },



    onMethodSelect: function (events) {
        var _obj = events.data.obj;
        var optionId = $(this).val();
        var containers = $('.securities-method-container');
        containers.hide();
        $('.method-details').hide();
        containers.find("input:radio").removeAttr("checked");

        var id = "#TransactionMethods_" + optionId + "_container";
        $(id).show();
        _obj.processContinueButton();
        _obj.showTermsAndConditions(optionId);
    },

    showTermsAndConditions: function (methodType) {
        $('.terms-conditions').show();
        if (methodType == 'S') {
            $('#tncSell').show();
            $('#tncTransfer').hide();
        } else {
            $('#tncSell').hide();
            $('#tncTransfer').show();
        }
    },

    onMarketOrderClick: function (events) {
        var self = events.data.obj;
        $("#dayLimitMinPrice").val('');
        //$(this).parents('ul.method-detail-items').find('.method-details').hide();
        //  $(this).parents('ul.method-detail-items li').find('.method-details').show();
        self.processContinueButton();
    },

    onPaymentMethodClick: function (events) {
        var self = events.data.obj;
        self.paymentDetails.hide();
        var id = "paymentDetails_" + $(this).val() + "_" + $(this).attr('data-payment-proceed-seq-number');
        $('#' + id).show();

        if ($(this).val() == 'FDC' || $(this).val() == 'IBW') {
            $("div .callout").show();
        } else {
            $("div .callout").hide();
        }
        self.processContinueButton();
    },

    processContinueButton: function () {
        //var selectedSecurities = self.getSecuritiesToSell();
        //var isValid = (selectedSecurities != '');
        var salesMethod = $("input[name=SalesMethod]:checked").val();
        //var paymentMethod = $("input[name=PaymentMethod]:checked").val();

        var isValid = salesMethod != null ? true : false;
        var paymentMethod = $('div.paymentDetails').attr('value');
        var proceedSeqNumber = $('.update-bank-account').attr('proceedSeqNumber');
        var accountNameSpan = $('#accountNameSpan_' + proceedSeqNumber);
        var bsbSpan = $('#bsbSpan_' + proceedSeqNumber);
        var accountNumberSpan = $('#accountNumberSpan_' + proceedSeqNumber);

        isValid = accountNameSpan.text() != 'N/A' && bsbSpan.text() != 'N/A' && accountNumberSpan.text() != 'N/A' && salesMethod != null;
        if (isValid) {
            if (salesMethod == IC.Public.Plans.SaleMethods.SellByLimit) {
                var limitPrice = parseFloat($("#dayLimitMinPrice").val().replaceAll(",", ""));
                isValid = (!isNaN(limitPrice) && limitPrice > 0);
            }


            //if (isValid) {
            //    if ($("input[name=PaymentMethod]:checked").attr('data-payment-proceed-seq-number').substring(0, 2) == '-1') {
            //        isValid = false;
            //    }
            //}
        }
        isValid = (isValid && this.TermsAndConditionsAccepted.is(':checked'));

        if (isValid)
            this.btnContinue.parent().removeClass('disabled');
        else
            this.btnContinue.parent().addClass('disabled');

        if (IC.Public.IsThirdPartyImpersonateUser())
            this.btnContinue.parent().addClass('disabled');
    },

    getSharesToTransact: function () {
        var selectedGrants = '';
        $('.SharesToTransact').each(function () {
            var numberToTransact = $(this).val().replaceAll(",", "");
            if (numberToTransact == '' || numberToTransact == '0')
                return;
            var manageSeqNumber = $(this).attr('manageSeqNumber');

            if (selectedGrants == '')
                selectedGrants = manageSeqNumber + "|" + numberToTransact;
            else
                selectedGrants += "," + manageSeqNumber + "|" + numberToTransact;
        });

        return selectedGrants;
    },

    onContinueButtonClick: function (events) {
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }
        var self = events.data.obj;

        //Prepare "ManageSeqNumber|SharesToTransact" array string
        //var selectedGrants = self.getSharesToTransact();
        //if (selectedGrants == '')
        //    return;

        //var transactionMethod = $('input[name=TransactionMethods]:checked').val();
        //if (transactionMethod == null) {
        //    return;
        //}
        var selectedSecurities = self.getSecuritiesToSell();
        if (selectedSecurities == '')
            return;
        var SelectedSecurities = $("<input>").attr("type", "hidden").attr("name", "SelectedSecurities").val(selectedSecurities);
        //SaleMethod and PaymentMethod validation

        if ($("input[name=SalesMethod]:checked").length == 0) {
            return;
        }

        //if ($("input[name=PaymentMethod]:checked").length == 0) {
        //    return;
        //}


        $('#hdnSelectedGrants').val(selectedSecurities);

        //SalePrice
        var salePriceInput = 0;
        if ($("#sales_methods_MKT").is(':checked')) {
            salePriceInput = $("#marketOrderPrice").text();
        } else {
            salePriceInput = $("#dayLimitMinPrice").val();
        }
        var LimitPrice = $("<input>").attr("type", "hidden").attr("name", "LimitPrice").val(salePriceInput);

        ////PaymentMethod
        //var paymentMethodInput = $("input[name=PaymentMethod]:checked").val();
        //var paymentMethod = $("<input>").attr("type", "hidden").attr("name", "paymentMethod").val(paymentMethodInput);

        //ViewKey
        var viewKeyInput = $("#ViewKey").val();
        var viewKey = $("<input>").attr("type", "hidden").attr("name", "ViewKey").val(viewKeyInput);
        var firstName = $("#FirstName").val();
        var middleName = $("#MiddleName").val();
        var lastName = $("#LastName").val();

        if (firstName.indexOf("'") > 0) {
            firstName = firstName.replaceAll("'", " ");
        }

        if (lastName.indexOf("'") > 0) {
            lastName = lastName.replaceAll("'", " ");
        }

        if (middleName.indexOf("'") > 0) {
            middleName = middleName.replaceAll("'", " ");
        }

        var SharepPrice20MinDelay = $("#SharepPrice20MinDelay").val();
        //Prepare the Form for submit
        var form = $("form#OnlineSaleTransactionMethodForm");
        
        form.append($(SelectedSecurities)).append($(LimitPrice)).append($(viewKey)).append($(firstName)).append($(middleName)).append($(lastName)).append($(SharepPrice20MinDelay)); //Append misc inputs at the end of the form //append($(paymentMethod)).
        var data = form.serializeObject();

        IC.Public.showLoading("");

        //Disable the COntinue button to prevent more than one click            
        $(this).parent().addClass('disabled');

        //Submit the Form through AJAX
        $.ajax({
            url: IC.Public.urls.OnlineSale.OnlineSaleConfirmSecuritySaleDetails,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();

            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
                $(this).parent().removeClass('disabled');
            }
        });
    },

    onFilterClick: function (events) {
        var url = IC.Public.urls.OnlineSale.OnlineSaleSellSecurities;
        OC.MVC.util.loadMainContainerView(url, null);
    },
    onCancelClick: function (events) {
        IC.Public.showLoading("");
        var self = events.data.obj;
        var viewKey = $("#ViewKey").val();
        var message = $('#WarningMessage').val();
        var modal = new IC.Public.securitiesMessage();
        modal.showModal(viewKey, message);
        IC.Public.hideLoading();

        //IC.Public.showLoading("");
        //var urlLocation = "/Securities/OnlineSaleSellSecurities";
        //window.location.href = urlLocation;
    },

    onUpdateBankAccountLinkClick: function (events) {
        var self = events.data.obj;
        var proceedSeqNumber = $(this).attr('proceedSeqNumber');
        IC.Public.showLoading("");
        var investorCode = $('#InvestorCode').val();
        var issuerCode = $('#IssuerCode').val();
        var securityId = $('#SecurityId').val();
        var holderValue = $('#HolderValue').val();
        var paymentMethodCode = $(this).attr('paymentMethodTypeCode');
        var paymentMethodDesc = $(this).attr('paymentMethodTypeDesc');
        var modal = new IC.Public.onlineSaleBankAccountDetails();
        modal.showModal(securityId, holderValue, investorCode, issuerCode, proceedSeqNumber, IC.Public.Plans.Common.MenuSubItemTypes.Securities, self.validateTransactionPassword, paymentMethodCode, paymentMethodDesc);
    },

    onIBWUpdateAccountLinkClick: function (events) {
        var proceedSeqNumber = parseInt($(this).attr('proceedSeqNumber'));
        var modal = new IC.Public.Plans.InternationalBankWire();
        IC.Public.showLoading("");
        modal.showModal(proceedSeqNumber, IC.Public.Plans.Common.MenuSubItemTypes.Securities);
    },

    onUpdateAddressLinkClick: function () {
        var url = OC.MVC.util.getLink("Address", "Edit");
        OC.MVC.util.loadMainContainerView(url);
    },

    onExerciseMessageLinkClick: function () {
        var type = $(this).attr("type");
        var code = $(this).attr("code");
        var viewKey = $("#ViewKey").val();
        var transactionType = $(this).attr("transactionType");
        var windowId = $(this).attr("windowId");

        var modal = new IC.Public.Plans.message();
        modal.showModal(type, code, viewKey, transactionType, windowId, IC.Public.Plans.Common.MenuSubItemTypes.Securities);
    }
};
;/// <reference path="../../Holdings/ic.public.transactionGrid.js" />
IC.Public.onlineSaleConfirmSecuritySale = function (validateTransactionPassword) {
    this.init(validateTransactionPassword);
};


IC.Public.onlineSaleConfirmSecuritySale.prototype = {
    init: function (validateTransactionPassword) {
        this.validateTransactionPassword = validateTransactionPassword;
        this.confirmOnlineSaleSecuritySaleForm = $('form#confirmOnlineSaleSecuritySaleForm');
        
        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind('click');
        this.btnCancel.bind("click", { obj: this }, this.onCancelButtonClick);

        this.btnBackButton = $('#btnBack');
        this.btnBackButton.unbind('click');
        this.btnBackButton.bind("click", { obj: this }, this.onBackButtonClick);

        this.btnContinueButton = $('#btnContinue');
        this.btnContinueButton.unbind('click');
        this.btnContinueButton.bind("click", { obj: this }, this.onContinueButtonClick);

        var transactionPasswordRequired = $('#TransactionPasswordRequired').val().toLowerCase();

        var textBox = $('#validationModel_TransactionPins_0__TransactionPin').val();
        if (textBox != undefined) {
            $('#btnContinue').parent().addClass('disabled');            
            this.smsPinTextBox = $('#validationModel_TransactionPins_0__TransactionPin');
            this.smsPinTextBox.unbind('keyup');
            this.smsPinTextBox.bind("keyup", { obj: this }, this.processContinueButton)
        }

    },
    processContinueButton: function (events) {
   
            if ($('#validationModel_TransactionPins_0__TransactionPin').val() == "") {
                $('#btnContinue').parent().addClass('disabled');
            }
            else {
                $('#btnContinue').parent().removeClass('disabled');
            }
        
    },
    onCancelButtonClick: function (events) {
        //IC.Public.showLoading("");
        //var self = events.data.obj;
        //var data = self.confirmOnlineSaleSecuritySaleForm.serializeObject();
        //var viewKey = data.ViewKey;
        //var message = $('#WarningMessage').val();
        //var modal = new IC.Public.securitiesMessage();
        //modal.showModal(viewKey, message);
        //IC.Public.hideLoading();

        IC.Public.showLoading("");
        var urlLocation = "/Securities/OnlineSaleSellSecurities";
        window.location.href = urlLocation;
    },

    onBackButtonClick: function (events) {
        var self = events.data.obj;
        var data = self.confirmOnlineSaleSecuritySaleForm.serializeObject();
        var selectedSecurities = self.getSecuritiesToSell();
        

        IC.Public.showLoading("");

        var url = OC.MVC.util.getLink("Securities", "OnlineSaleTransactionMethodDetails");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();

            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
                $(this).parent().removeClass('disabled');
            }
        });
    },

    onContinueButtonClick: function (events) {
        
        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handleConfirmSecuritySaleClick();
            return;
        }

        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        var formData = self.confirmOnlineSaleSecuritySaleForm.serializeObject();
        IC.Public.security.validateTransactionPin(formData, function () { self.handleConfirmSecuritySaleClick(); }, function () { self.disableContinueButton(); });
    },

    handleConfirmSecuritySaleClick: function () {
      
        var data = this.confirmOnlineSaleSecuritySaleForm.serializeObject();
        var selectedSecurities = this.getSecuritiesToSell();
        //var postData = {
        //    ViewKey: data.ViewKey,
        //    QuoteId:data.QuoteId,
        //    SelectedSecurities: selectedSecurities,
        //    FirstName: data.FirstName,
        //    MiddleName: data.MiddleName,
        //    LastName:data.LastName
        //};
        //var url = OC.MVC.util.getLink("Securities", "OnlineSaleConfirmationRecieptDetails");
        var url = OC.MVC.util.getLink("Securities", "OnlineSaleInvestorDetails");
        IC.Public.showLoading("");
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },

    disableContinueButton: function () {
        this.btnContinueButton.parent().addClass('disabled');
    },

    getSecuritiesToSell: function () {
        var selectedSecurities = '';
        $('.securitiesToSell').each(function () {
            var securityToSell = $(this).attr('selectedvalue');
            var securityId = $(this).attr('securityId');

            if (selectedSecurities == '')
                selectedSecurities = securityId + "|" + securityToSell;
            else
                selectedSecurities += "," + securityId + "|" + securityToSell;
        });

        return selectedSecurities;
    },
};
;IC.Public.onlineSaleConfirmationReceipt = function (isSaleReceipt) {
    this.init(isSaleReceipt);
};

IC.Public.onlineSaleConfirmationReceipt.prototype = {
    init: function (isSaleReceipt) {
        this.isSaleReceipt = isSaleReceipt;

        this.btnDoneButton = $('#btnDone');
        this.btnDoneButton.unbind('click');
        this.btnDoneButton.bind("click", { obj: this }, this.onDoneButtonClick);

        this.aDownloadReceiptLink = $("#aDownloadReceiptLink");
        this.aDownloadReceiptLink.unbind();
        this.aDownloadReceiptLink.bind("click", { obj: this }, this.OnDownloadReceiptLinkClicked);

        this.onlineSaleConfirmationReceiptForm = $('form#onlineSaleConfirmationReceiptForm');
    },

    

    onDoneButtonClick: function (events) {
        var self = events.data.obj;
        var data = self.onlineSaleConfirmationReceiptForm.serializeObject();
        var viewKey = data.ViewKey;

        IC.Public.showLoading("");
        var urlLocation = "/Securities/OnlineSaleSellSecurities";
        window.location.href = urlLocation;
        //var url = OC.MVC.util.getLink("Securities", "OnlineSaleSellSecurities");
        //OC.MVC.util.loadMainContainerView(url, { viewKey: viewKey });
        IC.Public.hideLoading();
    },

    OnDownloadReceiptLinkClicked: function (events) {
        var self = events.data.obj;
        var url = OC.MVC.util.getLink("Securities", "OnlineSaleViewConfirmation");
        window.location = url + "?viewKey=" + $("#ViewKey").val() + "&orderId=" + $("#ConfirmationNumber").text();
    }
};IC.Public.onlineSaleBankAccountDetails = function (options) {
    if (options) {
        $.extend(this, options);
    }
};

IC.Public.onlineSaleBankAccountDetails.prototype = {
    showModal: function (securityId, holderValue, investorCode, issuerCode, proceedSeqNumber, menuSubItemType, validateTransactionPassword, paymentMethodCode, paymentMethodDesc) {
        var self = this;
        self.proceedSeqNumber = proceedSeqNumber;
        self.validateTransactionPassword = validateTransactionPassword;
        var planCodes = null;

        if (paymentMethodCode == "LDC") {
            OC.MVC.util.showModal('PaymentMethod', 'OnlineSaleBankAccountDetails',
                { securityId: securityId, holderValue: holderValue, investorCode: investorCode, issuerCode: issuerCode, viewKey: $('#ViewKey').val(), menuSubItemType: menuSubItemType, paymentMethodCode: paymentMethodCode },
                { minWidth: 600, width: 600, hideCloseButton: true }, function () { self.init(self.proceedSeqNumber, self.validateTransactionPassword); }
                );

        } else {
            OC.MVC.util.showModal('PaymentMethod', 'NewOnlineSaleBankAccountDetails',
                { securityId: securityId, holderValue: holderValue, investorCode: investorCode, issuerCode: issuerCode, viewKey: $('#ViewKey').val(), paymentMethodCode: paymentMethodCode, paymentMethodDesc: paymentMethodDesc, menuSubItemType: menuSubItemType },
                { minWidth: 600, width: 600, hideCloseButton: true }, function () { self.init(self.proceedSeqNumber, self.validateTransactionPassword); }
            );
        }
    },
    init: function (proceedSeqNumber, validateTransactionPassword) {
        IC.Public.hideLoading();
        this.proceedSeqNumber = proceedSeqNumber;
        this.validateTransactionPassword = validateTransactionPassword;
        var self = this;

        this.cancelButton = $('#btnCancelUpdate');
        this.cancelButton.unbind('click');
        this.cancelButton.bind('click', function () {
            $.modal.close();
        });

        this.updateButton = $('#btnUpdate');
        this.updateButton.unbind('click');
        this.updateButton.bind('click', { obj: self }, self.onUpdateClick);

        $('html').unbind('keypress');
        $('html').bind('keypress', function (e) {
            if (e.keyCode == 13) {
                if (IC.Public.DoNothingForChatbotEnterKeyPress(e) == true) {
                    //Do Nothing
                } else {
                    $('#btnUpdate').click();
                }
            }
        });

        this.paymentTypesDropDown = $('#PaymentType');
        this.paymentTypesDropDown.unbind('change');
        this.paymentTypesDropDown.bind('change', { obj: self }, self.onPaymentTypeChange);
        this.buildingSocietyRefDiv = $('#buildingSocietyRefDiv');
        this.showOrHideBuildingSocietyRefField(this.paymentTypesDropDown.val());
        this.suffixDiv = $('#suffixDiv');
        this.showOrHideSuffixField(this.paymentTypesDropDown.val());

        this.smsPinRegisterLink = $("#bankAccountDetailsForm").find("#smsPinRegisterLink");
        this.smsPinRegisterLink.unbind('click');
        this.smsPinRegisterLink.bind('click', { obj: this }, this.onSmsPinRegisterLinkClick);

        this.closeButton = $('#btnClose');
        this.closeButton.unbind('click');
        this.closeButton.bind('click', { obj: self }, self.onCloseClick);

        $('#simplemodal-container').css('width', '600px');
        this.adjustModalSizeAndPosition();
    },

    onSmsPinRegisterLinkClick: function (eventArgs) {
        $.modal.close();
        var url = IC.Public.urls.Settings.smsPinRegistration;
        OC.MVC.util.loadMainContainerView(url);
    },

    onCloseClick: function (events) {
        var self = events.data.obj;
        //Refresh bank details
        $('#bsbSpan_-1' + $("input[name=PaymentMethod]:checked").val()).text($('#BsbCode').val());
        $('#accountNumberSpan_-1' + $("input[name=PaymentMethod]:checked").val()).text($('#AccountNumber').val());
        $('#updateAccount_-1' + $("input[name=PaymentMethod]:checked").val()).text('Update');
        $('#updateAccount_-1' + $("input[name=PaymentMethod]:checked").val()).attr('proceedseqnumber', $('#PSN').val());
        $("input[name=PaymentMethod]:checked").attr('data-payment-proceed-seq-number', $('#PSN').val());


        //setting the ul payment details div when new account added ,this div shows on respective payment method radio button click.
        var idVal = "paymentDetails_" + $("input[name=PaymentMethod]:checked").val() + "_" + $('#PSN').val();
        var ulPaymentDetailDiv = $("input[name=PaymentMethod]:checked").parent();
        if (ulPaymentDetailDiv && ulPaymentDetailDiv.siblings("ul.method-details:first")) {
            ulPaymentDetailDiv.siblings("ul.method-details:first").attr('id', idVal);
        }

        //Un-checking the Accept T&C checkbox so that validation for continue button get fired. It is difficult to fire processContinueButton() from here as that method resides in IC.Public.onlineSaleSecuritiesTransactions.transactionMethod
        $('#TermsAndConditionsAccepted').attr('checked', false);
        var paymentmethod = $('#PaymentMethodCode').val();
        var bsbid = 'bsbSpan_' + $('#ProceedSeqNumber').val();
        var paytype = $('#PaymentType').val();
        if (paytype.toLowerCase() == 'ggy' || paytype.toLowerCase() == 'ism' || paytype.toLowerCase() == 'imn' || paytype.toLowerCase() == 'jey' || paytype.toLowerCase() == 'gbr') {
            $("label[for='" + bsbid + "']").text("Sort Code");
        }
        else if (paytype.toLowerCase() == "nzl") {
            $("label[for='" + bsbid + "']").text("Bank/Branch");
        }

        else {
            $("label[for='" + bsbid + "']").text("BSB");

        }
        $('#btnContinue').parent().addClass('disabled');
        $.modal.close();
    },

    onUpdateClick: function (events) {
        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        var self = events.data.obj;
        if (!self.validateTransactionPassword) {
            self.handleUpdateBankAccountDetails(events);
            return;
        }

        if ($(this).parent().hasClass('disabled')) {
            events.preventDefault();
            return;
        }

        var formData = $('form#bankAccountDetailsForm').serializeObject();
        IC.Public.security.validateTransactionPin(formData, function () { self.handleUpdateBankAccountDetails(events); }, function () { $("#btnUpdate").parent().addClass('disabled'); });
        self.adjustModalSizeAndPosition();
    },

    handleUpdateBankAccountDetails: function (events) {
        var isElectedDividendAccount = $('#IsElectedDividendAccount').val().toLowerCase();
        if (isElectedDividendAccount == 'true') {
            if (!confirm("Are you sure you want to update your dividend account details?")) {
                return false;
            }
        }

        var form = $('form#bankAccountDetailsForm');
        var data = form.serializeObject();
        var self = events.data.obj;
        IC.Public.showLoading("");
        $.ajax({
            url: IC.Public.urls.PaymentMethod.onlineSalebankAccountDetails,
            type: 'POST',
            data: data,
            success: function (result) {
                IC.Public.hideLoading();
                $("#simplemodal-data").html(result);
                self.init($('#ProceedSeqNumber').val(), self.validateTransactionPassword);
                self.adjustModalSizeAndPosition();
                self.processPaymentMethodCheckBox();
            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
            }
        });
    },

    adjustModalSizeAndPosition: function () {
        $('#simplemodal-container').css('height', 'auto');
        $.modal.setPosition();
    },

    processPaymentMethodCheckBox: function () {
        var isSuccess = $('#updatedSucceededMessage').is(':visible');
        if (!isSuccess) {
            return;
        }
        // Enable the payment method checkbox and hide the Specify Account link
        var isNewAccount = $('#IsNewAccount').val().toLowerCase() == "true";
        var proceedSeqNumber = $('#PSN').val();
        var savedProceedSeqNumber = $('#PSN').val();

        if (isNewAccount) {
            proceedSeqNumber = "-1" + $('#PaymentMethodCode').val();

            var updateAccountLink = $('a[id=updateAccount_' + proceedSeqNumber + ']');
            updateAccountLink.attr('id', 'updateAccount_' + savedProceedSeqNumber);
            updateAccountLink.attr('proceedseqnumber', savedProceedSeqNumber);
            updateAccountLink.text("Update");
        }

        var paymentMethodCheckBox = $('input[data-payment-proceed-seq-number=' + proceedSeqNumber + ']');
        if (paymentMethodCheckBox != undefined) {
            paymentMethodCheckBox.removeAttr('disabled');
            if (isNewAccount) {
                paymentMethodCheckBox.attr('checked', 'checked');
                paymentMethodCheckBox.trigger('click');
            }
        }

        var specifyAccountLink = $('a[id=addAccount_' + proceedSeqNumber + ']');
        if (specifyAccountLink != undefined)
            specifyAccountLink.hide();

        // Update the save values in the payment method details section

        var accountNameSpan = $('#accountNameSpan_' + proceedSeqNumber);
        if (accountNameSpan != undefined) {
            accountNameSpan.text($('#AccountName').val());
            accountNameSpan.attr('id', 'accountNameSpan_' + savedProceedSeqNumber);
        }

        var bsbSpan = $('#bsbSpan_' + proceedSeqNumber);
        if (bsbSpan != undefined) {
            bsbSpan.text($('#BsbCode').val());
            bsbSpan.attr('id', 'bsbSpan_' + savedProceedSeqNumber);
        }

        var accountNumberSpan = $('#accountNumberSpan_' + proceedSeqNumber);
        if (accountNumberSpan != undefined) {
            accountNumberSpan.text($('#MaskedAccountNumber').val());
            accountNumberSpan.attr('id', 'accountNumberSpan_' + savedProceedSeqNumber);
        }
    },

    onPaymentTypeChange: function (events) {
        var viewkey = $('#ViewKey').val();
        var ProceedSeqNumber = $("#ProceedSeqNumber").val();
        var PlanCodes = $('#PlanCodes').val();

        $('.errorContainer').hide();

        $('#AccountName').removeClass('input-validation-error');
        $('#BsbCode').removeClass('input-validation-error');
        $('#AccountNumber').removeClass('input-validation-error');
        $('#Suffix').removeClass('input-validation-error');
        var self = events.data.obj;
        var paymentType = $(this).val();
        self.showOrHideBuildingSocietyRefField(paymentType);
        self.showOrHideSuffixField(paymentType);
        var url = OC.MVC.util.getLink("PaymentMethod", "GetBankDetails");

        $.ajax({
            url: url,
            data: { 'proceedSeqNumber': ProceedSeqNumber, 'viewKey': viewkey, 'menuSubItemType': 'Securities', 'planCodes': null, 'Paymentcode': paymentType },
            type: 'GET',

            success: function (data) {
                if (data != "") {
                    var arr = data.split(',');
                    $('#AccountName').val(arr[0]);
                    $('#BsbCode').val(arr[1]);
                    $('#AccountNumber').val(arr[2]);
                    $('#BuildingSocietyRef').val(arr[3]);
                }
                else {
                    $('#AccountName').val("");
                    $('#BsbCode').val("");
                    $('#AccountNumber').val("");
                    $('#BuildingSocietyRef').val("");
                }
            },
            error: function (e)
            { }
        });

        if (paymentType.toLowerCase() == 'ggy' || paymentType.toLowerCase() == 'ism' || paymentType.toLowerCase() == 'imn' || paymentType.toLowerCase() == 'jey' || paymentType.toLowerCase() == 'gbr') {
            $('#lblSortCode').show();
            $('#lblSortCode').html('Sort code');
            $('#lblBsbCode').hide();
        }
        else if (paymentType.toLowerCase() == 'nzl') {
            $('#lblSortCode').show();
            $('#lblSortCode').html('Bank/Branch');
            $('#lblBsbCode').hide();
        }
        else {
            $('#lblSortCode').hide();
            $('#lblBsbCode').show();

        }







    },

    showOrHideBuildingSocietyRefField: function (paymentType) {
        if (paymentType == undefined)
            return;

        if (paymentType.toLowerCase() == 'gbr') {
            this.buildingSocietyRefDiv.show();
        } else {
            this.buildingSocietyRefDiv.hide();
        }
        $.modal.setContainerDimensions();
    },

    showOrHideSuffixField: function (paymentType) {
        if (paymentType == undefined)
            return;

        if (paymentType.toLowerCase() == 'nzl') {
            this.suffixDiv.show();
        } else {
            this.suffixDiv.hide();
        }
        $.modal.setContainerDimensions();
    }
};
;IC.Public.onlineSalePlaceTrade = function () {
    this.init();
};

IC.Public.onlineSalePlaceTrade.prototype = {
    init: function () {
        var isGreenIdValidated = $('#greenIdValidated');

        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind('click');
        this.btnCancel.bind("click", { obj: this }, this.onCancelButtonClick);

        this.btnPlaceTrade = $('#btnPlaceTrade');
        this.btnPlaceTrade.unbind('click');
        this.btnPlaceTrade.bind("click", { obj: this }, this.onPlaceTradeButtonClick);

        this.btnRetry = $('#btnRetry');
        this.btnRetry.unbind('click');
        this.btnRetry.bind("click", { obj: this }, this.onRetryButtonClick);

        if ((isGreenIdValidated.val() == 'False') || (isGreenIdValidated.val() == 'false')) {
            this.disablePlaceTradeButton();
        }

        this.onlineSalePlaceTradeForm = $('form#onlineSalePlaceTradeForm');

        // display the message
        var overallState = $('#VerificationResult').val();
        switch (overallState) {
            case "VERIFIED":
            case "VERIFIED_WITH_CHANGES":
                if ($("#PEPStatus").val().toLowerCase() == "true" || $("#SanctionStatus").val().toLowerCase() == "true")
                {
                    $("#btnPlaceTrade").attr("disabled", "disabled");
                    $("#btnPlaceTrade").parent().addClass("disabled");
                }
                else {
                    $("#btnPlaceTrade").removeAttr("disabled");
                    $("#btnPlaceTrade").parent().removeClass("disabled");
                }
                break;
            case "IN_PROGRESS":
                $("#btnPlaceTrade").attr("disabled", "disabled");
                $("#btnPlaceTrade").parent().addClass("disabled");
                //$("#btnRetry").show();
                $("#btnRetry").parent().show();
                break;
            case "LOCKED_OUT":
            case "PENDING":
                $("#btnPlaceTrade").attr("disabled", "disabled");
                $("#btnPlaceTrade").parent().addClass("disabled");
                $("#btnRetry").hide();
                break;
            case "":
                $("#btnPlaceTrade").attr("disabled", "disabled");
                $("#btnPlaceTrade").parent().addClass("disabled");
                //$("#btnRetry").show();
                $("#btnRetry").parent().show();
                break;

        }
    },

    onRetryButtonClick: function () {
        var token = $("#VerificationReferenceNumber").val();
        var accountId = $("#accountId").val();
        var apiCode = $("#apiCode").val();
        greenidUI.show(accountId, apiCode, token);
    },

    onPlaceTradeButtonClick: function (events) {
        var self = events.data.obj;
        if ($('#IsImpersonateUser').val().toLowerCase() == "true") {
            alert(IC.Public.consts.impersonateUserNoAccessMessage);
            return;
        }
        var data = self.onlineSalePlaceTradeForm.serializeObject();
        var postData = {
            ViewKey: data.ViewKey,
            QuoteId: data.QuoteId,
            SelectedSecurities: data.SelectedSecurities,
            VerificationReferenceNumber: data.VerificationReferenceNumber,
            VerificationId: data.VerificationId,
            VerificationResult: data.VerificationResult,
            PEPStatus: data.PEPStatus,
            SanctionStatus: data.SanctionStatus
        };

        IC.Public.showLoading("");

        var url = OC.MVC.util.getLink("Securities", "OnlineSaleConfirmationRecieptDetails");
        $.ajax({
            url: url,
            type: 'POST',
            data: postData,
            success: function (result) {
                $("#" + OC.MVC.constants.mainContainer).html(result);
                IC.Public.hideLoading();

            },
            error: function (err) {
                OC.MVC.util.errorMessage(err.responseText);
                IC.Public.hideLoading();
                $(this).parent().removeClass('disabled');
            }
        });
    },

    disablePlaceTradeButton: function () {
        this.btnPlaceTrade.parent().addClass('disabled');
    },

    onCancelButtonClick: function (events) {
        //IC.Public.showLoading("");
        //var self = events.data.obj;
        //var data = self.onlineSalePlaceTradeForm.serializeObject();
        //var viewKey = data.ViewKey;
        //var message = $('#WarningMessage').val();
        //var modal = new IC.Public.securitiesMessage();
        //modal.showModal(viewKey, message);
        //IC.Public.hideLoading();

        IC.Public.showLoading("");
        var urlLocation = "/Securities/OnlineSaleSellSecurities";
        window.location.href = urlLocation;
    },
};IC.Public.onlineSaleInvestorDetails = function () {
    this.init();
};

IC.Public.onlineSaleInvestorDetails.prototype = {
    init: function () {

        this.validateGreenIdSumbit();

        this.btnCancel = $('#btnCancel');
        this.btnCancel.unbind('click');
        this.btnCancel.bind("click", { obj: this }, this.onCancelButtonClick);

        this.btnContinue = $('#btnContinue');
        this.btnContinue.unbind('click');
        this.btnContinue.bind("click", { obj: this }, this.onContinueButtonClick);

        this.btnContinue = $('#ResidentialStreetAddress');
        this.btnContinue.unbind('keyup');
        this.btnContinue.bind("keyup", { obj: this }, this.validateGreenIdSumbit);

        this.DateOfBirth = $('#DateOfBirth');
        this.DateOfBirth.unbind('change');
        this.DateOfBirth.bind("change", { obj: this }, this.onChangeActionDOB);
        
        this.onlineSaleInvestorDetailsForm = $('form#onlineSaleInvestorDetailsForm');
        

        $('#greenid-div-output').bind("DOMNodeInserted", { obj: this }, function (event) {
            $('body').find("div[id*='greenid-modal-backdrop']").each(function () {
                $("#greenid-modal-backdrop").css({ "display": "none" });
                $("#greenid-source-content").css({ "height": "" });
                $('#greenid-intro-words').find("p:first").addClass('hidden');
            });
            if ($('body').hasClass("modal-open")) {
                $(this).removeClass("modal-open");
            }
            if ($("#greenid-modal-title").html() == "Unexpected Error")
            {
                $(".modal-body").html("Please note that our online trading facility only supports Australian or New Zealand residential addresses.");
            }

            $('body').find("div[id*='greenid-wait-modal']").each(function () {
                $(this).css({ "display": "none" });
            });

            $("#greenid-wait-modal").hide();
            $("#greenid-modal-backdrop").hide();
            $('body').removeClass("modal-open");

            // Remove the Medibank option from the GreenID form
            $("#greenid-intro-content h1").hide();
            $("#greenid-sources a:contains('Medibank ')").parent().hide();
            $("#greenid-sources a:contains('Medibank')").parent().hide();


            // CR - First name and Last name should be readonly
            $("#greenid-div-output input[id*='givenname']").length > 0 ? $("input[id*='givenname']").attr("readonly", "readonly") : "";
            $("#greenid-div-output input[id*='surname']").length > 0 ? $("input[id*='surname']").attr("readonly", "readonly") : "";

            $("#greenid-div-output input[id*='spousegivenname']").length > 0 ? $("input[id*='spousegivenname']").removeAttr("readonly") : "";
            $("#greenid-div-output input[id*='spousesurname']").length > 0 ? $("input[id*='spousesurname']").removeAttr("readonly") : "";

            // IC-365 - Skip button is not available in AU Certificate verification
            if ($("#greenid-source-cancel_cer").length == 0) {
                $("#greenid-u-agree").append('<button type="button" id="greenid-source-cancel_cer" class="btn btn-default">Cancel</button>'); $("#greenid-source-cancel_cer").click(function () { $("#greenid-source-cancel").trigger("click") });
            }
        });

        // Call Google Api for Address suggestion
        InvokeInvestorDetailsGooglePlaceAPI();

        // Green ID implementaion
        greenidUI.setup({
            environment: $("#greenIdEnvironment").val(),
            formId: "onlineSaleInvestorDetailsForm",
            frameId: "greenid-div-output",
            country: "usethiscountry",
            debug: false,
            registerCallback: this.onGreenIDRegister,
            sessionCompleteCallback: this.onGreenIDSessionComplete,
            // preSubmitValidationCallback: onValidation,
            errorCallback: this.onGreenIDError,
            sessionCancelledCallback: this.onGreenIDSessionCancel,
            submitCallback: this.onGreenIDSubmit,
            enableCancelButton: true,
            enableBackButtonWarning:true
        });
        greenidConfig.setOverrides({
            "enable_save_and_complete_later": false,
            "cancel_button_text": "Cancel"
        });
    },
    validateGreenIdSumbit: function () {
        $('#btnContinue').attr("disabled", "disabled");
        $("#btnContinue").parent().addClass("disabled");
        if (($("#ResidentialStreetAddress").val() != "") && ($("#FirstName").val() != "") && ($("#LastName").val() != "") && ($('#DateOfBirth').val()!=""))
        {
            $("#btnContinue").removeAttr("disabled");
            $("#btnContinue").parent().removeClass("disabled");
        }
    },
    ValidateDate: function (dtValue) {
        var dtRegex = new RegExp(/\b\d{1,2}[\/-]\d{1,2}[\/-]\d{4}\b/);
        return dtRegex.test(dtValue);
    },
    validateDateAndMonth: function () {
        try {
            $.datepicker.parseDate('dd/mm/yy', $("[name='DateOfBirth']").val());
        } catch (e) {
            //alert(e);
            return false;
        };
        return true;
    },
    onChangeActionDOB: function (events) {
        var _obj = events.data.obj;
        var date = $("[name=DateOfBirth]").val();
        if (_obj.ValidateDate(date) && _obj.validateDateAndMonth()) {

            var dateValue = $.datepicker.parseDate("dd/mm/yy", $("[name='DateOfBirth']").val());
            var newDateValue = new Date();
            //var dateCurrentValue = $.datepicker.parseDate("dd/mm/yy", newDateValue.getDate());

            if (dateValue > newDateValue) {
                $("[name='DateOfBirth']").val($("[name='IndividualInvestor.DateOfBirthValue']").val());
            }
            else {
                var value = $("[name='DateOfBirth']").val();
                $("[name='IndividualInvestor.DateOfBirthValue']").val(value);
            }

            _obj.validateGreenIdSumbit();
        }
        else {
            $("[name='DateOfBirth']").val('');
            $("[name='IndividualInvestor.DateOfBirthValue']").val('');
            _obj.validateGreenIdSumbit();
        }
    },
    onGreenIDRegister:function(verificationId, userdata) {
            $('#verificationId').val(verificationId);
            $('#verificationReferenceNumber').val(verificationId);
            if ($('a.modal').length) {
                $(document).off("click", "[a.modal]").on("click", "[a.modal]", { obj: self }, this.onbtninfoclicked);
            }
    },
        
    onGreenIDSessionComplete:function(verificationToken, overallState) {
            console.log("SessionComplete called for verificationToken " + verificationToken + ", " + overallState);
    },

    onGreenIDError:function(verificationToken, errorName) {
            console.log("Error called for verificationToken " + verificationToken + ", " + errorName);
    },

    onGreenIDSessionCancel:function(verificationToken, overallState) {
            console.log("Error called for verificationToken " + verificationToken + ", " + overallState);
    },
    onGreenIDSubmit: function (verificationToken, overallState) {
        $('#verificationReferenceNumber').val(verificationToken);
        $('#verificationStatus').val(overallState);
        $('body').removeClass('modal-open');
        IC.Public.onlineSaleInvestorDetails.prototype.placeTrade();
        return false;
    },
    placeTrade: function() {
            IC.Public.showLoading("");
            var data = $("#onlineSaleInvestorDetailsForm").serializeObject();
            // Process the Green ID result with service
            var url = OC.MVC.util.getLink("Securities", "VerificationResult");
            $.ajax({
                url: url,
                type: "POST",
                data: data,
                error: function (error) {
                    OC.MVC.util.errorMessage(error.responseText);
                    IC.Public.hideLoading();
                    $(this).parent().removeClass('disabled');
                    console.log(error.responseText);
                },
                success: function (result) {
                    // if details logged then process the place trade screen
                    var postData = {
                        ViewKey: data.ViewKey,
                        QuoteId: data.QuoteId,
                        SelectedSecurities: data.SelectedSecurities,
                        VerificationReferenceNumber: $("#verificationReferenceNumber").val(),
                        VerificationId: $("#verificationId").val(),
                        VerificationResult: $("#verificationStatus").val(),
                        PEPStatus: result.ResponseObjectContainer.PEPStatus,
                        SanctionStatus: result.ResponseObjectContainer.SanctionStatus
                    };

                    var url = OC.MVC.util.getLink("Securities", "OnlineSalePlaceTrade");
                    $.ajax({
                        url: url,
                        type: 'POST',
                        data: postData,
                        success: function (result) {
                            $("#" + OC.MVC.constants.mainContainer).html(result);
                            IC.Public.hideLoading();
                        },
                        error: function (err) {
                            OC.MVC.util.errorMessage(err.responseText);
                            IC.Public.hideLoading();
                            $(this).parent().removeClass('disabled');
                        }
                    });
                }
            });


    },
    
    onContinueButtonClick: function (events) {
        // Fill the form values to GreenID post
        $("#givenNames").val($("#FirstName").val());
        $("#middleNames").val($("#MiddleName").val());
        $("#surname").val($("#LastName").val());
        $("#dob").val($("#DateOfBirth").val());
        // Google Map API fix
        if ($("#streetName").val() == "")
            $("#streetName").val($("#ResidentialStreetAddress").val());
        $('#frmIdentityCheckModule').submit();
    },

    onCancelButtonClick: function (events) {
        IC.Public.showLoading("");
        var self = events.data.obj;
        var data = self.onlineSaleInvestorDetailsForm.serializeObject();
        var viewKey = data.ViewKey;
        var message = $('#WarningMessage').val();
        var modal = new IC.Public.securitiesMessage();
        modal.showModal(viewKey, message);
        IC.Public.hideLoading();
        
        //IC.Public.showLoading("");
        //var urlLocation = "/Securities/OnlineSaleSellSecurities";
        //window.location.href = urlLocation;
    },
}

// Google API integration
var placeSearch, autocomplete;
var componentForm = {
    locality: 'long_name',//city
    administrative_area_level_1: 'short_name',//state
    country: 'long_name',
    postal_code: 'short_name',
    subpremise: 'short_name',
    street_number: 'long_name',
    sublocality_level_1: 'short_name', // Street address
    route: 'long_name' // Route
};


function InvokeInvestorDetailsGooglePlaceAPI() {
    autocomplete = new google.maps.places.Autocomplete(
        (document.getElementById('ResidentialStreetAddress')),
        { types: ['geocode'] });

    // When the user selects an address from the dropdown, populate the address
    // fields in the form.
    autocomplete.addListener('place_changed', fillInvestorAddress);
}

function fillInvestorAddress() {
    IC.Public.onlineSaleInvestorDetails.prototype.validateGreenIdSumbit();
    // Get the place details from the autocomplete object.
    var place = autocomplete.getPlace();
    // If google api address not available then give street name as whole
    if (place == null || place == undefined || place.address_components.length == 0)
        $("#streetName").val($("#ResidentialStreetAddress").val());
    else
        $("#streetName").val("");

    // Fix for Victoria State - Null appended address 
    $("#flatNumber").val(' ');
    $("#streetNumber").val(' ');

    // Bind the address to respecive GreenId forms
    for (var i = 0; i < place.address_components.length; i++) {
        var addressType = place.address_components[i].types[0];// get google parameter
        if (componentForm[addressType]) {
            var val = place.address_components[i][componentForm[addressType]]; // respective address type values and binding it
            switch (addressType) {
                case "country":
                    // To support AU & NZ address
                    if (val.toUpperCase() == "NEW ZEALAND")
                    {
                        $("#usethiscountry").val("NZ")
                    }
                    else
                        $("#usethiscountry").val("AU")
                    break;
                case "sublocality_level_1":
                    $("#streetName").val(val);
                    break;
                case "route":
                    if ($("#flatNumber").val() != " " && $("#streetNumber").val() != " ")
                        $("#streetName").val(val);
                    else
                        $("#streetName").val((($("#streetName").val() == "") ? $("#streetName").val() : $("#streetName").val() + ", " + val));
                    break;
                case "locality":
                    $("#suburb").val(val);
                    break;
                case "administrative_area_level_1":
                    $("#state").val(val);
                    break;
                case "postal_code":
                    $("#postcode").val(val);
                    break;
                case "subpremise":
                    $("#flatNumber").val(val == "" ? ' ' : val);
                    break;
                case "street_number":
                    $("#streetNumber").val(val == "" ? ' ' : val);
                    break;
                default:
                    break;
            }

        }
    }
};IC.Public.securitiesMessage = function () {

};

IC.Public.securitiesMessage.prototype = {
    showModal: function (viewKey, message) {
       
        var self = this;
        var url = OC.MVC.util.getLink("Securities", "SecuritiesMessagePopUp");

        self.viewKey = viewKey;
        self.message = message;

        var data = {
            viewKey: viewKey,
            message: message,
        };
        OC.MVC.util.showModalByUrl(url, data,
               { maxHeight: 300, maxWidth: 500, hideCloseButton: false }, function () { self.init(); });
    },

    init: function () {

        var self = this;
        self.registerEvents();
        self.adjustModalSizeAndPosition();
    },

    registerEvents: function () {

        this.btnConfirm = $('#btnConfirm');
        this.btnConfirm.unbind('click');
        this.btnConfirm.bind('click', { obj: this }, this.onConfirmClick);
    },

    onConfirmClick: function (events) {
       // If transaction method page then navigation will be Holding page
        if ($("#isTransactionpages").length != 0)
        {
            IC.Public.showLoading("");
            $.modal.close();
            window.location.href = "/";
            return false;
        }

        // Work arround for the GreenID session issues
        IC.Public.showLoading("");
        $.modal.close();
        window.location.href = "/Securities/OnlineSaleSellSecurities";
        return false;
        /*
        var self = events.data.obj

        IC.Public.showLoading("");

        var url = OC.MVC.util.getLink("Securities", "OnlineSaleSellSecurities");
        OC.MVC.util.loadMainContainerView(url, { viewKey: self.viewKey });
        $.modal.close();
        IC.Public.hideLoading();
        */
    },

    adjustModalSizeAndPosition: function () {
        $('#simplemodal-container').css('height', 'auto');
        $.modal.setPosition();
    }
};
;IC.Public.onlineSaleValidateInvestorPopUp = function () {
    
};

IC.Public.onlineSaleValidateInvestorPopUp.prototype = {
    showModal: function () {
       
        var self = this;
        var url = OC.MVC.util.getLink("Securities", "ValidateInvestorRequestPopUp");
        var data = {};
           
        OC.MVC.util.showModalByUrl(url, data,
               { maxHeight: 300, maxWidth: 500, hideCloseButton: false }, function (result) {
                  
                   $('#simplemodal-container').html(result)
                   self.init();
               });
    },

    init: function () {
        
        var self = this;
        self.adjustModalSizeAndPosition();
    },

    adjustModalSizeAndPosition: function () {
        $('#simplemodal-container').css('height', 'auto');
        $.modal.setPosition();
    }
};;IC.Public.Plans.TRS = function (tabNum) {
	this.planSummaryContainer = $('#planSummaryContainer');
	this.init(0);
};

IC.Public.Plans.TRS.prototype = {
	init: function (tabNum) {
	    this.registerEvents();
	    
		$('#downloadLink').attr('href', 'javascript:onDownloadClick("CSV");');
		var url = OC.MVC.util.getLink("TotalReward", "Home");
		IC.Public.Plans.plansummaryPostAjax("planSummaryTabsGridContainer", url, null);

		$('#planDropDownDiv').hide();
		$('#vestingCalculatorDiv').hide();
		if (tabNum != null && tabNum > -1) {
			this.planSummaryContainer.find('.displayLinks a').eq(tabNum).trigger('click'); // trigger required tab
		}
	},
	registerEvents: function () {
		//this.init(tabNum);
		var self = this;
		this.planSummaryContainer.find('.displayLinks a').each(function () {
			$(this).bind('click', { obj: self, url: $(this).attr('actionurl') }, self.displayLinkOnClick);
		});
		var self = this;
		var holdingFilter = $("form.holdingFilterForm");
		if (holdingFilter.length) {
		    holdingFilter.unbind("applyHoldingFilter");
		    holdingFilter.bind("applyHoldingFilter", { obj: self }, self.onFilterClick);
		}

	},
	onFilterClick: function (events) {
	    var url = OC.MVC.util.getLink("TotalReward", "Index");
	    OC.MVC.util.loadMainContainerView(url, null);
	},
	displayLinkOnClick: function (events) {
		// if not already selected
		if (!$(this).hasClass('selected')) {
			$('#planDropDownDiv').hide();
			IC.Public.showLoading("");
			var _obj = events.data.obj;
			_obj.planSummaryContainer.find('.displayLinks a').each(function () {
				$(this).removeClass('selected');
			});
			$(this).addClass('selected');
			var url = events.data.url;
			IC.Public.Plans.plansummaryPostAjax("TRSplanSummaryTabsGridContainer", url, null);
		}
	},
};IC.Public.Plans.RewardTarget = function () {
    this.init();
};

IC.Public.Plans.RewardTarget.prototype = {
    init: function () {
        var self = this;
        $('#planDropDownDiv').hide();
        $('#vestingCalculatorDiv').hide();
        $('#downloadLink').attr('href', 'javascript:onDownloadClick("CSV");');
        this.financialYearList = $("select[name=FinancialYear]");

        var remCurrencyCode = $('#CurrencyCode').val();
        $('#conversionCurrency option').each(function () {
            if ($(this).val() == remCurrencyCode)
                $(this).attr('selected', 'selected');
        });

        this.conversionCurrency = $('#conversionCurrency');
        this.targetRewardGrid = $("#TargetRewardGrid");
        this.targetRewardGrid.unbind(OC.MVC.grid.events.loadComplete);
        this.targetRewardGrid.bind(OC.MVC.grid.events.loadComplete, { obj: this }, function (events) {
            var _obj = events.data.obj;
            var isCurrencyConversionApplied = IC.Public.Currency.isCurrencyConversionApplied();
            var conversionCurrency = _obj.conversionCurrency.val();
            _obj.setGridColumnCurrencyCodes(conversionCurrency, isCurrencyConversionApplied);

            var tabType = $('#TabType').val();
            var financial = $("#FinancialYear").val();

            var currencyConversionFailed = $(this).getUserDataItem('CurrencyConversionFailed');
            if (currencyConversionFailed) {
                $(this).addClass('noRecords').html('<tr><td>Sorry - there has been a temporary problem trying to load your data. <br/><br/>Please <a href="javascript:void(0);">try again</a> </td></tr>')
                $("#TargetRewardGrid_pager").hide();
                $("#btnDownloadStatement").parent().hide();
                IC.Public.hideLoading();
                alert($('#hidCurrencyConversionRateNotFoundErrorMessage').val());

                //IC.Public.Currency.displayErrorMessage($('#hidCurrency').val(), $('#hidCurrencyConversionErrorRateNotFound').val(), $('#conversionCurrency'));
            }
            else
            {
                try {
                    
                    // Building DoughNut Chart
                    if ($("#ShowDonutChart").val() == "True") {
                        var dynBackgroundColor = ['#f26729', '#252D59', '#5083C2', '#DDA238', '#2386A2', '#F2CA30', '#89B9DB', '#4E4E50', '#579BAB', '#DBDCDD'];
                        var dynLabel = [];
                        var dynLabelValue = [];
                        var ctx = document.getElementById("myChart").getContext('2d');
                        ctx.canvas.width = 300;
                        ctx.canvas.height = 300;

                        var ctxPDF = document.getElementById("myChartPDF").getContext('2d');
                        ctxPDF.canvas.width = 300;
                        ctxPDF.canvas.height = 300;

                        Chart.defaults.global.tooltips.enabled = false;
                        IC.Public.showLoading();
                        $.ajax({
                            url: OC.MVC.util.getLink("TotalReward", "TRODoughnutData"),
                            data: { "viewKey": $("#ViewKey").val() },
                            type: "POST",
                            success: function (result) {
                                if (result != undefined) {
                                    if (result.length > 0) {
                                        $.each(result, function (key, value) {
                                            dynLabel.push(value.RewardDescriptionChart);
                                            dynLabelValue.push(value.RewardValueChart);
                                        });
                                    }
                                }
                                var legend = '<table id="legend" style="width:100%">';
                                var total = 0;
                                $.each(dynLabelValue, function (key, value) {
                                    total += value;
                                });
                                var showDonutPercentage = $('#ShowDonutPercentage').val().toLowerCase();
                                if (showDonutPercentage == "true") {
                                    $.each(dynLabel, function (key, value) {
                                        var percentage = (dynLabelValue[key] / total) * 100;
                                        legend += '<tr> <td> <div class="d-legend-color" style="background-color: ' + dynBackgroundColor[key] + '">&nbsp;</div></td> <td class="d-legend"><span>' + value + ' - ' + percentage.toFixed(2) + '%</span></td></tr>';
                                    });
                                }
                                else {
                                    $.each(dynLabel, function (key, value) {
                                        legend += '<tr> <td><div class="d-legend-color" style="background-color: ' + dynBackgroundColor[key] + '">&nbsp;</div></td><td class="d-legend"> <span> ' + value + '</span></td></tr>';
                                    });
                                }
                                legend += '</table>';
                                $(".legendContainer").html(legend);

                                var legendPDF = '<table id="legendPDF" style="width:100%">';
                                if (showDonutPercentage == "true") {
                                    $.each(dynLabel, function (key, value) {
                                        var percentage = (dynLabelValue[key] / total) * 100;
                                        legendPDF += '<tr> <td><div class="d-legend"> <div class="d-legend-color" style="background-color: ' + dynBackgroundColor[key] + '">&nbsp;</div></td> <td><span style="font-family: Calibri; font-size: small;">' + value + ' - ' + percentage.toFixed(2) + '%</span></div></td></tr>';
                                    });
                                }
                                else {
                                    $.each(dynLabel, function (key, value) {
                                        legendPDF += '<tr> <td><div class="d-legend-color" style="background-color: ' + dynBackgroundColor[key] + '">&nbsp;</div></td><td class="d-legend"> <span style="font-family: Calibri; font-size: small;"> ' + value + '</span></td></tr>';
                                    });
                                }
                                legendPDF += '</table>';
                                $("#legendPDF").html(legendPDF);

                                var myChart = new Chart(ctx, {
                                    type: 'doughnut',
                                    data: { labels: dynLabel, datasets: [{ label: '# of Votes', data: dynLabelValue, backgroundColor: dynBackgroundColor, borderWidth: 1 }] },
                                    options: { legend: { display: false }, responsive: true, maintainAspectRatio: false }
                                });

                                var myChartPDF = new Chart(ctxPDF, {
                                    type: 'doughnut',
                                    data: { labels: dynLabel, datasets: [{ label: '# of Votes', data: dynLabelValue, backgroundColor: dynBackgroundColor, borderWidth: 1 }] },
                                    options: { legend: { display: false }, responsive: true, maintainAspectRatio: false }
                                });

                                IC.Public.hideLoading();
                            }
                        });
                    }

                    // Load LTI Grid
                    var tabType = $('#TabType').val();
                    var financial = $("#FinancialYear").val();
                    var data = { financialYear: financial, tabType: tabType };
                    var url = OC.MVC.util.getLink("TotalReward", "VestedLTIAward");
                    IC.Public.showLoading();
                    $.ajax({
                        url: url,
                        type: 'GET',
                        data: data,
                        success: function (result) {
                            $('.vestedLTIGrid').html(result);
                            IC.Public.hideLoading();
                        },
                        error: function (xmlHttpRequest, textStatus) {
                            alert('Error loading content: ' + textStatus);
                        }
                    });
                }
                catch (e) {
                    console.log(e);
                    IC.Public.hideLoading();
                    $(".tro-screen-load").remove();
                }

                $("#TargetRewardGrid tr").each(function () {
                    var isselected = $(this).find('td[aria-describedby="TargetRewardGrid_RewardCode"]').text();
                    var tabType = $('#TabType').val();

                    if (isselected == "AAI") {
                        var element = $(this).find('td[aria-describedby="TargetRewardGrid_Remuneration Component"]');
                        var text = element.html();
                        var financial = (parseInt($(this).find('td[aria-describedby="TargetRewardGrid_FinancialYear"]').text())).toString().slice(-2);
                        var rewardsReceivedAAISubtext = $('#RewardsReceivedAAISubtext').val();
                        var targetRewardsAAISubtext = $('#TargetRewardsAAISubtext').val();

                        if (tabType == "RewardReceived") {
                            if (rewardsReceivedAAISubtext !=undefined && rewardsReceivedAAISubtext.trim() != "") {
                                element.html(text + '<br />' + '<span style="font-style:italic; font-size:0.95em;">' + rewardsReceivedAAISubtext + financial + '</span>');
                            }
                        }
                        else {
                            if (targetRewardsAAISubtext!=undefined && targetRewardsAAISubtext.trim() != "") {
                                element.html(text + '<br />' + '<span style="font-style:italic; font-size:0.95em;">' + targetRewardsAAISubtext + financial + '</span>');
                            }
                        }
                    }
                    if (isselected == "LTI") {
                        var element = $(this).find('td[aria-describedby="TargetRewardGrid_Remuneration Component"]');
                        element.find('span').removeAttr('title');
                        element.find('span').removeClass('tooltip');
                        element.find('a').removeClass('cluetooltip');
                        var text = element.html();
                        var financial = (parseInt($(this).find('td[aria-describedby="TargetRewardGrid_FinancialYear"]').text())).toString().slice(-2);

                        var rewardsReceivedLTISubtext = $('#RewardsReceivedLTISubtext').val();
                        var targetRewardsLTISubtext = $('#TargetRewardsLTISubtext').val();

                        if (tabType == "RewardReceived") {
                            if (rewardsReceivedLTISubtext!=undefined && rewardsReceivedLTISubtext.trim() != "") {
                                element.html(text + '<br />' + '<span style="font-style:italic; font-size:0.95em;">' + rewardsReceivedLTISubtext + financial + '</span>');
                            }
                        }
                        else {
                            if (targetRewardsLTISubtext!=undefined && targetRewardsLTISubtext.trim() != "") {
                                element.html(text + '<br />' + '<span style="font-style:italic; font-size:0.95em;">' + targetRewardsLTISubtext + financial + '</span>');
                            }
                        }

                    }
                    if (isselected == "TRR") {
                        var element = $(this).find('td[aria-describedby="TargetRewardGrid_Remuneration Component"]');
                        element.find('span').removeAttr('title');
                        element.find('span').removeClass('tooltip');
                        $(this).css("background-color", "gainsboro");
                    }
                });

                $("#vestedLTI").bind("click", { obj: _obj }, _obj.onVestedLTIAwardLinkClick);
                $("#TargetRewardGrid_pager").hide();
                IC.Public.hideLoading();
            }            
        });

        this.form = $('#totalRewardOnlineForm');
        this.holdingFilterTargetReward = $("form.holdingFilterForm");

        this.FinancialYearDropdown = $("#FinancialYear");
        this.FinancialYearDropdown.unbind("change");
        this.FinancialYearDropdown.bind("change", { obj: this }, this.onGoClick);

        this.downloadStatement = $('#btnDownloadStatement');
        this.downloadStatement.unbind('click');
        this.downloadStatement.bind('click', { obj: this }, this.onDownStatementClick);
    },

    setGridColumnCurrencyCodes: function (currencyCode, isCurrencyConversionApplied) {
        currencyCode = (!isCurrencyConversionApplied) ? $('#CurrencyCode').val() : currencyCode;
        if (currencyCode != "-1") {
            $('#valueRemunerationCurrency').text(currencyCode);
        }
    },
    onVestedLTIAwardLinkClick: function () {
        $('.vestedLTIGrid').slideToggle('slow');
    },
    onDownStatementClick: function (eventArgs) {
        var tabType = $('#TabType').val();
        var financial = $("#FinancialYear").val();
        var currency = $('#conversionCurrency').val();
        var url = OC.MVC.util.getLink("TotalReward", "RewardsDownloadStatement");
        var imageData = "";

        html2canvas($("#myChartDivPDF")[0], {
            scale: 1
        }).then(function (canvas) {
            imageData = canvas.toDataURL("image/png");
            imageData = imageData.replace('data:image/png;base64,', '');

            $.ajax({
                type: 'POST',
                url: OC.MVC.util.getLink("TotalReward", "UploadImage"),
                data: '{ "imageData" : "' + imageData + '" }',
                contentType: 'application/json; charset=utf-8',
                dataType: 'text',
                async: false,
                success: function (msg) {
                    window.location = url + "?tabType=" + tabType + "&financialYear=" + financial + "&currency=" + currency;
                },
                error: function (msg) {
                    window.location = url + "?tabType=" + tabType + "&financialYear=" + financial + "&currency=" + currency;
                }
            });
        });
        //window.location = url + "?tabType=" + tabType + "&financialYear=" + financial + "&currency=" + currency + "&imageData=" + imageData;
    },
    onGoClick: function (events) {
        var _obj = events.data.obj;
        var financialYearSelected = '';
        if (events.data.isLoading != true) {
            financialYearSelected = _obj.financialYearList.val();
        }
        var financialYear = { financialYear: financialYearSelected };
        var url = OC.MVC.util.getLink("TotalReward", "RewardsReceived");

        if ($("#TabType").val() == "TargetReward")
            url = OC.MVC.util.getLink("TotalReward", "TargetReceived");
        IC.Public.showLoading();
        IC.Public.Plans.plansummaryPostAjax("planSummaryTabsGridContainer", url, financialYear);
    },
};/*!
 * Chart.js
 * http://chartjs.org/
 * Version: 2.7.0
 *
 * Copyright 2017 Nick Downie
 * Released under the MIT license
 * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md
 */
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/* MIT license */
var colorNames = require(5);

module.exports = {
   getRgba: getRgba,
   getHsla: getHsla,
   getRgb: getRgb,
   getHsl: getHsl,
   getHwb: getHwb,
   getAlpha: getAlpha,

   hexString: hexString,
   rgbString: rgbString,
   rgbaString: rgbaString,
   percentString: percentString,
   percentaString: percentaString,
   hslString: hslString,
   hslaString: hslaString,
   hwbString: hwbString,
   keyword: keyword
}

function getRgba(string) {
   if (!string) {
      return;
   }
   var abbr =  /^#([a-fA-F0-9]{3})$/i,
       hex =  /^#([a-fA-F0-9]{6})$/i,
       rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i,
       per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i,
       keyword = /(\w+)/;

   var rgb = [0, 0, 0],
       a = 1,
       match = string.match(abbr);
   if (match) {
      match = match[1];
      for (var i = 0; i < rgb.length; i++) {
         rgb[i] = parseInt(match[i] + match[i], 16);
      }
   }
   else if (match = string.match(hex)) {
      match = match[1];
      for (var i = 0; i < rgb.length; i++) {
         rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16);
      }
   }
   else if (match = string.match(rgba)) {
      for (var i = 0; i < rgb.length; i++) {
         rgb[i] = parseInt(match[i + 1]);
      }
      a = parseFloat(match[4]);
   }
   else if (match = string.match(per)) {
      for (var i = 0; i < rgb.length; i++) {
         rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
      }
      a = parseFloat(match[4]);
   }
   else if (match = string.match(keyword)) {
      if (match[1] == "transparent") {
         return [0, 0, 0, 0];
      }
      rgb = colorNames[match[1]];
      if (!rgb) {
         return;
      }
   }

   for (var i = 0; i < rgb.length; i++) {
      rgb[i] = scale(rgb[i], 0, 255);
   }
   if (!a && a != 0) {
      a = 1;
   }
   else {
      a = scale(a, 0, 1);
   }
   rgb[3] = a;
   return rgb;
}

function getHsla(string) {
   if (!string) {
      return;
   }
   var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
   var match = string.match(hsl);
   if (match) {
      var alpha = parseFloat(match[4]);
      var h = scale(parseInt(match[1]), 0, 360),
          s = scale(parseFloat(match[2]), 0, 100),
          l = scale(parseFloat(match[3]), 0, 100),
          a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
      return [h, s, l, a];
   }
}

function getHwb(string) {
   if (!string) {
      return;
   }
   var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
   var match = string.match(hwb);
   if (match) {
    var alpha = parseFloat(match[4]);
      var h = scale(parseInt(match[1]), 0, 360),
          w = scale(parseFloat(match[2]), 0, 100),
          b = scale(parseFloat(match[3]), 0, 100),
          a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
      return [h, w, b, a];
   }
}

function getRgb(string) {
   var rgba = getRgba(string);
   return rgba && rgba.slice(0, 3);
}

function getHsl(string) {
  var hsla = getHsla(string);
  return hsla && hsla.slice(0, 3);
}

function getAlpha(string) {
   var vals = getRgba(string);
   if (vals) {
      return vals[3];
   }
   else if (vals = getHsla(string)) {
      return vals[3];
   }
   else if (vals = getHwb(string)) {
      return vals[3];
   }
}

// generators
function hexString(rgb) {
   return "#" + hexDouble(rgb[0]) + hexDouble(rgb[1])
              + hexDouble(rgb[2]);
}

function rgbString(rgba, alpha) {
   if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
      return rgbaString(rgba, alpha);
   }
   return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")";
}

function rgbaString(rgba, alpha) {
   if (alpha === undefined) {
      alpha = (rgba[3] !== undefined ? rgba[3] : 1);
   }
   return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2]
           + ", " + alpha + ")";
}

function percentString(rgba, alpha) {
   if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
      return percentaString(rgba, alpha);
   }
   var r = Math.round(rgba[0]/255 * 100),
       g = Math.round(rgba[1]/255 * 100),
       b = Math.round(rgba[2]/255 * 100);

   return "rgb(" + r + "%, " + g + "%, " + b + "%)";
}

function percentaString(rgba, alpha) {
   var r = Math.round(rgba[0]/255 * 100),
       g = Math.round(rgba[1]/255 * 100),
       b = Math.round(rgba[2]/255 * 100);
   return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")";
}

function hslString(hsla, alpha) {
   if (alpha < 1 || (hsla[3] && hsla[3] < 1)) {
      return hslaString(hsla, alpha);
   }
   return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)";
}

function hslaString(hsla, alpha) {
   if (alpha === undefined) {
      alpha = (hsla[3] !== undefined ? hsla[3] : 1);
   }
   return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, "
           + alpha + ")";
}

// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax
// (hwb have alpha optional & 1 is default value)
function hwbString(hwb, alpha) {
   if (alpha === undefined) {
      alpha = (hwb[3] !== undefined ? hwb[3] : 1);
   }
   return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%"
           + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")";
}

function keyword(rgb) {
  return reverseNames[rgb.slice(0, 3)];
}

// helpers
function scale(num, min, max) {
   return Math.min(Math.max(min, num), max);
}

function hexDouble(num) {
  var str = num.toString(16).toUpperCase();
  return (str.length < 2) ? "0" + str : str;
}


//create a list of reverse color names
var reverseNames = {};
for (var name in colorNames) {
   reverseNames[colorNames[name]] = name;
}

},{"5":5}],2:[function(require,module,exports){
/* MIT license */
var convert = require(4);
var string = require(1);

var Color = function (obj) {
	if (obj instanceof Color) {
		return obj;
	}
	if (!(this instanceof Color)) {
		return new Color(obj);
	}

	this.valid = false;
	this.values = {
		rgb: [0, 0, 0],
		hsl: [0, 0, 0],
		hsv: [0, 0, 0],
		hwb: [0, 0, 0],
		cmyk: [0, 0, 0, 0],
		alpha: 1
	};

	// parse Color() argument
	var vals;
	if (typeof obj === 'string') {
		vals = string.getRgba(obj);
		if (vals) {
			this.setValues('rgb', vals);
		} else if (vals = string.getHsla(obj)) {
			this.setValues('hsl', vals);
		} else if (vals = string.getHwb(obj)) {
			this.setValues('hwb', vals);
		}
	} else if (typeof obj === 'object') {
		vals = obj;
		if (vals.r !== undefined || vals.red !== undefined) {
			this.setValues('rgb', vals);
		} else if (vals.l !== undefined || vals.lightness !== undefined) {
			this.setValues('hsl', vals);
		} else if (vals.v !== undefined || vals.value !== undefined) {
			this.setValues('hsv', vals);
		} else if (vals.w !== undefined || vals.whiteness !== undefined) {
			this.setValues('hwb', vals);
		} else if (vals.c !== undefined || vals.cyan !== undefined) {
			this.setValues('cmyk', vals);
		}
	}
};

Color.prototype = {
	isValid: function () {
		return this.valid;
	},
	rgb: function () {
		return this.setSpace('rgb', arguments);
	},
	hsl: function () {
		return this.setSpace('hsl', arguments);
	},
	hsv: function () {
		return this.setSpace('hsv', arguments);
	},
	hwb: function () {
		return this.setSpace('hwb', arguments);
	},
	cmyk: function () {
		return this.setSpace('cmyk', arguments);
	},

	rgbArray: function () {
		return this.values.rgb;
	},
	hslArray: function () {
		return this.values.hsl;
	},
	hsvArray: function () {
		return this.values.hsv;
	},
	hwbArray: function () {
		var values = this.values;
		if (values.alpha !== 1) {
			return values.hwb.concat([values.alpha]);
		}
		return values.hwb;
	},
	cmykArray: function () {
		return this.values.cmyk;
	},
	rgbaArray: function () {
		var values = this.values;
		return values.rgb.concat([values.alpha]);
	},
	hslaArray: function () {
		var values = this.values;
		return values.hsl.concat([values.alpha]);
	},
	alpha: function (val) {
		if (val === undefined) {
			return this.values.alpha;
		}
		this.setValues('alpha', val);
		return this;
	},

	red: function (val) {
		return this.setChannel('rgb', 0, val);
	},
	green: function (val) {
		return this.setChannel('rgb', 1, val);
	},
	blue: function (val) {
		return this.setChannel('rgb', 2, val);
	},
	hue: function (val) {
		if (val) {
			val %= 360;
			val = val < 0 ? 360 + val : val;
		}
		return this.setChannel('hsl', 0, val);
	},
	saturation: function (val) {
		return this.setChannel('hsl', 1, val);
	},
	lightness: function (val) {
		return this.setChannel('hsl', 2, val);
	},
	saturationv: function (val) {
		return this.setChannel('hsv', 1, val);
	},
	whiteness: function (val) {
		return this.setChannel('hwb', 1, val);
	},
	blackness: function (val) {
		return this.setChannel('hwb', 2, val);
	},
	value: function (val) {
		return this.setChannel('hsv', 2, val);
	},
	cyan: function (val) {
		return this.setChannel('cmyk', 0, val);
	},
	magenta: function (val) {
		return this.setChannel('cmyk', 1, val);
	},
	yellow: function (val) {
		return this.setChannel('cmyk', 2, val);
	},
	black: function (val) {
		return this.setChannel('cmyk', 3, val);
	},

	hexString: function () {
		return string.hexString(this.values.rgb);
	},
	rgbString: function () {
		return string.rgbString(this.values.rgb, this.values.alpha);
	},
	rgbaString: function () {
		return string.rgbaString(this.values.rgb, this.values.alpha);
	},
	percentString: function () {
		return string.percentString(this.values.rgb, this.values.alpha);
	},
	hslString: function () {
		return string.hslString(this.values.hsl, this.values.alpha);
	},
	hslaString: function () {
		return string.hslaString(this.values.hsl, this.values.alpha);
	},
	hwbString: function () {
		return string.hwbString(this.values.hwb, this.values.alpha);
	},
	keyword: function () {
		return string.keyword(this.values.rgb, this.values.alpha);
	},

	rgbNumber: function () {
		var rgb = this.values.rgb;
		return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
	},

	luminosity: function () {
		// http://www.w3.org/TR/WCAG20/#relativeluminancedef
		var rgb = this.values.rgb;
		var lum = [];
		for (var i = 0; i < rgb.length; i++) {
			var chan = rgb[i] / 255;
			lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);
		}
		return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
	},

	contrast: function (color2) {
		// http://www.w3.org/TR/WCAG20/#contrast-ratiodef
		var lum1 = this.luminosity();
		var lum2 = color2.luminosity();
		if (lum1 > lum2) {
			return (lum1 + 0.05) / (lum2 + 0.05);
		}
		return (lum2 + 0.05) / (lum1 + 0.05);
	},

	level: function (color2) {
		var contrastRatio = this.contrast(color2);
		if (contrastRatio >= 7.1) {
			return 'AAA';
		}

		return (contrastRatio >= 4.5) ? 'AA' : '';
	},

	dark: function () {
		// YIQ equation from http://24ways.org/2010/calculating-color-contrast
		var rgb = this.values.rgb;
		var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
		return yiq < 128;
	},

	light: function () {
		return !this.dark();
	},

	negate: function () {
		var rgb = [];
		for (var i = 0; i < 3; i++) {
			rgb[i] = 255 - this.values.rgb[i];
		}
		this.setValues('rgb', rgb);
		return this;
	},

	lighten: function (ratio) {
		var hsl = this.values.hsl;
		hsl[2] += hsl[2] * ratio;
		this.setValues('hsl', hsl);
		return this;
	},

	darken: function (ratio) {
		var hsl = this.values.hsl;
		hsl[2] -= hsl[2] * ratio;
		this.setValues('hsl', hsl);
		return this;
	},

	saturate: function (ratio) {
		var hsl = this.values.hsl;
		hsl[1] += hsl[1] * ratio;
		this.setValues('hsl', hsl);
		return this;
	},

	desaturate: function (ratio) {
		var hsl = this.values.hsl;
		hsl[1] -= hsl[1] * ratio;
		this.setValues('hsl', hsl);
		return this;
	},

	whiten: function (ratio) {
		var hwb = this.values.hwb;
		hwb[1] += hwb[1] * ratio;
		this.setValues('hwb', hwb);
		return this;
	},

	blacken: function (ratio) {
		var hwb = this.values.hwb;
		hwb[2] += hwb[2] * ratio;
		this.setValues('hwb', hwb);
		return this;
	},

	greyscale: function () {
		var rgb = this.values.rgb;
		// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
		var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
		this.setValues('rgb', [val, val, val]);
		return this;
	},

	clearer: function (ratio) {
		var alpha = this.values.alpha;
		this.setValues('alpha', alpha - (alpha * ratio));
		return this;
	},

	opaquer: function (ratio) {
		var alpha = this.values.alpha;
		this.setValues('alpha', alpha + (alpha * ratio));
		return this;
	},

	rotate: function (degrees) {
		var hsl = this.values.hsl;
		var hue = (hsl[0] + degrees) % 360;
		hsl[0] = hue < 0 ? 360 + hue : hue;
		this.setValues('hsl', hsl);
		return this;
	},

	/**
	 * Ported from sass implementation in C
	 * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209
	 */
	mix: function (mixinColor, weight) {
		var color1 = this;
		var color2 = mixinColor;
		var p = weight === undefined ? 0.5 : weight;

		var w = 2 * p - 1;
		var a = color1.alpha() - color2.alpha();

		var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
		var w2 = 1 - w1;

		return this
			.rgb(
				w1 * color1.red() + w2 * color2.red(),
				w1 * color1.green() + w2 * color2.green(),
				w1 * color1.blue() + w2 * color2.blue()
			)
			.alpha(color1.alpha() * p + color2.alpha() * (1 - p));
	},

	toJSON: function () {
		return this.rgb();
	},

	clone: function () {
		// NOTE(SB): using node-clone creates a dependency to Buffer when using browserify,
		// making the final build way to big to embed in Chart.js. So let's do it manually,
		// assuming that values to clone are 1 dimension arrays containing only numbers,
		// except 'alpha' which is a number.
		var result = new Color();
		var source = this.values;
		var target = result.values;
		var value, type;

		for (var prop in source) {
			if (source.hasOwnProperty(prop)) {
				value = source[prop];
				type = ({}).toString.call(value);
				if (type === '[object Array]') {
					target[prop] = value.slice(0);
				} else if (type === '[object Number]') {
					target[prop] = value;
				} else {
					console.error('unexpected color value:', value);
				}
			}
		}

		return result;
	}
};

Color.prototype.spaces = {
	rgb: ['red', 'green', 'blue'],
	hsl: ['hue', 'saturation', 'lightness'],
	hsv: ['hue', 'saturation', 'value'],
	hwb: ['hue', 'whiteness', 'blackness'],
	cmyk: ['cyan', 'magenta', 'yellow', 'black']
};

Color.prototype.maxes = {
	rgb: [255, 255, 255],
	hsl: [360, 100, 100],
	hsv: [360, 100, 100],
	hwb: [360, 100, 100],
	cmyk: [100, 100, 100, 100]
};

Color.prototype.getValues = function (space) {
	var values = this.values;
	var vals = {};

	for (var i = 0; i < space.length; i++) {
		vals[space.charAt(i)] = values[space][i];
	}

	if (values.alpha !== 1) {
		vals.a = values.alpha;
	}

	// {r: 255, g: 255, b: 255, a: 0.4}
	return vals;
};

Color.prototype.setValues = function (space, vals) {
	var values = this.values;
	var spaces = this.spaces;
	var maxes = this.maxes;
	var alpha = 1;
	var i;

	this.valid = true;

	if (space === 'alpha') {
		alpha = vals;
	} else if (vals.length) {
		// [10, 10, 10]
		values[space] = vals.slice(0, space.length);
		alpha = vals[space.length];
	} else if (vals[space.charAt(0)] !== undefined) {
		// {r: 10, g: 10, b: 10}
		for (i = 0; i < space.length; i++) {
			values[space][i] = vals[space.charAt(i)];
		}

		alpha = vals.a;
	} else if (vals[spaces[space][0]] !== undefined) {
		// {red: 10, green: 10, blue: 10}
		var chans = spaces[space];

		for (i = 0; i < space.length; i++) {
			values[space][i] = vals[chans[i]];
		}

		alpha = vals.alpha;
	}

	values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha)));

	if (space === 'alpha') {
		return false;
	}

	var capped;

	// cap values of the space prior converting all values
	for (i = 0; i < space.length; i++) {
		capped = Math.max(0, Math.min(maxes[space][i], values[space][i]));
		values[space][i] = Math.round(capped);
	}

	// convert to all the other color spaces
	for (var sname in spaces) {
		if (sname !== space) {
			values[sname] = convert[space][sname](values[space]);
		}
	}

	return true;
};

Color.prototype.setSpace = function (space, args) {
	var vals = args[0];

	if (vals === undefined) {
		// color.rgb()
		return this.getValues(space);
	}

	// color.rgb(10, 10, 10)
	if (typeof vals === 'number') {
		vals = Array.prototype.slice.call(args);
	}

	this.setValues(space, vals);
	return this;
};

Color.prototype.setChannel = function (space, index, val) {
	var svalues = this.values[space];
	if (val === undefined) {
		// color.red()
		return svalues[index];
	} else if (val === svalues[index]) {
		// color.red(color.red())
		return this;
	}

	// color.red(100)
	svalues[index] = val;
	this.setValues(space, svalues);

	return this;
};

if (typeof window !== 'undefined') {
	window.Color = Color;
}

module.exports = Color;

},{"1":1,"4":4}],3:[function(require,module,exports){
/* MIT license */

module.exports = {
  rgb2hsl: rgb2hsl,
  rgb2hsv: rgb2hsv,
  rgb2hwb: rgb2hwb,
  rgb2cmyk: rgb2cmyk,
  rgb2keyword: rgb2keyword,
  rgb2xyz: rgb2xyz,
  rgb2lab: rgb2lab,
  rgb2lch: rgb2lch,

  hsl2rgb: hsl2rgb,
  hsl2hsv: hsl2hsv,
  hsl2hwb: hsl2hwb,
  hsl2cmyk: hsl2cmyk,
  hsl2keyword: hsl2keyword,

  hsv2rgb: hsv2rgb,
  hsv2hsl: hsv2hsl,
  hsv2hwb: hsv2hwb,
  hsv2cmyk: hsv2cmyk,
  hsv2keyword: hsv2keyword,

  hwb2rgb: hwb2rgb,
  hwb2hsl: hwb2hsl,
  hwb2hsv: hwb2hsv,
  hwb2cmyk: hwb2cmyk,
  hwb2keyword: hwb2keyword,

  cmyk2rgb: cmyk2rgb,
  cmyk2hsl: cmyk2hsl,
  cmyk2hsv: cmyk2hsv,
  cmyk2hwb: cmyk2hwb,
  cmyk2keyword: cmyk2keyword,

  keyword2rgb: keyword2rgb,
  keyword2hsl: keyword2hsl,
  keyword2hsv: keyword2hsv,
  keyword2hwb: keyword2hwb,
  keyword2cmyk: keyword2cmyk,
  keyword2lab: keyword2lab,
  keyword2xyz: keyword2xyz,

  xyz2rgb: xyz2rgb,
  xyz2lab: xyz2lab,
  xyz2lch: xyz2lch,

  lab2xyz: lab2xyz,
  lab2rgb: lab2rgb,
  lab2lch: lab2lch,

  lch2lab: lch2lab,
  lch2xyz: lch2xyz,
  lch2rgb: lch2rgb
}


function rgb2hsl(rgb) {
  var r = rgb[0]/255,
      g = rgb[1]/255,
      b = rgb[2]/255,
      min = Math.min(r, g, b),
      max = Math.max(r, g, b),
      delta = max - min,
      h, s, l;

  if (max == min)
    h = 0;
  else if (r == max)
    h = (g - b) / delta;
  else if (g == max)
    h = 2 + (b - r) / delta;
  else if (b == max)
    h = 4 + (r - g)/ delta;

  h = Math.min(h * 60, 360);

  if (h < 0)
    h += 360;

  l = (min + max) / 2;

  if (max == min)
    s = 0;
  else if (l <= 0.5)
    s = delta / (max + min);
  else
    s = delta / (2 - max - min);

  return [h, s * 100, l * 100];
}

function rgb2hsv(rgb) {
  var r = rgb[0],
      g = rgb[1],
      b = rgb[2],
      min = Math.min(r, g, b),
      max = Math.max(r, g, b),
      delta = max - min,
      h, s, v;

  if (max == 0)
    s = 0;
  else
    s = (delta/max * 1000)/10;

  if (max == min)
    h = 0;
  else if (r == max)
    h = (g - b) / delta;
  else if (g == max)
    h = 2 + (b - r) / delta;
  else if (b == max)
    h = 4 + (r - g) / delta;

  h = Math.min(h * 60, 360);

  if (h < 0)
    h += 360;

  v = ((max / 255) * 1000) / 10;

  return [h, s, v];
}

function rgb2hwb(rgb) {
  var r = rgb[0],
      g = rgb[1],
      b = rgb[2],
      h = rgb2hsl(rgb)[0],
      w = 1/255 * Math.min(r, Math.min(g, b)),
      b = 1 - 1/255 * Math.max(r, Math.max(g, b));

  return [h, w * 100, b * 100];
}

function rgb2cmyk(rgb) {
  var r = rgb[0] / 255,
      g = rgb[1] / 255,
      b = rgb[2] / 255,
      c, m, y, k;

  k = Math.min(1 - r, 1 - g, 1 - b);
  c = (1 - r - k) / (1 - k) || 0;
  m = (1 - g - k) / (1 - k) || 0;
  y = (1 - b - k) / (1 - k) || 0;
  return [c * 100, m * 100, y * 100, k * 100];
}

function rgb2keyword(rgb) {
  return reverseKeywords[JSON.stringify(rgb)];
}

function rgb2xyz(rgb) {
  var r = rgb[0] / 255,
      g = rgb[1] / 255,
      b = rgb[2] / 255;

  // assume sRGB
  r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);
  g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);
  b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);

  var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
  var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
  var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);

  return [x * 100, y *100, z * 100];
}

function rgb2lab(rgb) {
  var xyz = rgb2xyz(rgb),
        x = xyz[0],
        y = xyz[1],
        z = xyz[2],
        l, a, b;

  x /= 95.047;
  y /= 100;
  z /= 108.883;

  x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
  y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
  z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);

  l = (116 * y) - 16;
  a = 500 * (x - y);
  b = 200 * (y - z);

  return [l, a, b];
}

function rgb2lch(args) {
  return lab2lch(rgb2lab(args));
}

function hsl2rgb(hsl) {
  var h = hsl[0] / 360,
      s = hsl[1] / 100,
      l = hsl[2] / 100,
      t1, t2, t3, rgb, val;

  if (s == 0) {
    val = l * 255;
    return [val, val, val];
  }

  if (l < 0.5)
    t2 = l * (1 + s);
  else
    t2 = l + s - l * s;
  t1 = 2 * l - t2;

  rgb = [0, 0, 0];
  for (var i = 0; i < 3; i++) {
    t3 = h + 1 / 3 * - (i - 1);
    t3 < 0 && t3++;
    t3 > 1 && t3--;

    if (6 * t3 < 1)
      val = t1 + (t2 - t1) * 6 * t3;
    else if (2 * t3 < 1)
      val = t2;
    else if (3 * t3 < 2)
      val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
    else
      val = t1;

    rgb[i] = val * 255;
  }

  return rgb;
}

function hsl2hsv(hsl) {
  var h = hsl[0],
      s = hsl[1] / 100,
      l = hsl[2] / 100,
      sv, v;

  if(l === 0) {
      // no need to do calc on black
      // also avoids divide by 0 error
      return [0, 0, 0];
  }

  l *= 2;
  s *= (l <= 1) ? l : 2 - l;
  v = (l + s) / 2;
  sv = (2 * s) / (l + s);
  return [h, sv * 100, v * 100];
}

function hsl2hwb(args) {
  return rgb2hwb(hsl2rgb(args));
}

function hsl2cmyk(args) {
  return rgb2cmyk(hsl2rgb(args));
}

function hsl2keyword(args) {
  return rgb2keyword(hsl2rgb(args));
}


function hsv2rgb(hsv) {
  var h = hsv[0] / 60,
      s = hsv[1] / 100,
      v = hsv[2] / 100,
      hi = Math.floor(h) % 6;

  var f = h - Math.floor(h),
      p = 255 * v * (1 - s),
      q = 255 * v * (1 - (s * f)),
      t = 255 * v * (1 - (s * (1 - f))),
      v = 255 * v;

  switch(hi) {
    case 0:
      return [v, t, p];
    case 1:
      return [q, v, p];
    case 2:
      return [p, v, t];
    case 3:
      return [p, q, v];
    case 4:
      return [t, p, v];
    case 5:
      return [v, p, q];
  }
}

function hsv2hsl(hsv) {
  var h = hsv[0],
      s = hsv[1] / 100,
      v = hsv[2] / 100,
      sl, l;

  l = (2 - s) * v;
  sl = s * v;
  sl /= (l <= 1) ? l : 2 - l;
  sl = sl || 0;
  l /= 2;
  return [h, sl * 100, l * 100];
}

function hsv2hwb(args) {
  return rgb2hwb(hsv2rgb(args))
}

function hsv2cmyk(args) {
  return rgb2cmyk(hsv2rgb(args));
}

function hsv2keyword(args) {
  return rgb2keyword(hsv2rgb(args));
}

// http://dev.w3.org/csswg/css-color/#hwb-to-rgb
function hwb2rgb(hwb) {
  var h = hwb[0] / 360,
      wh = hwb[1] / 100,
      bl = hwb[2] / 100,
      ratio = wh + bl,
      i, v, f, n;

  // wh + bl cant be > 1
  if (ratio > 1) {
    wh /= ratio;
    bl /= ratio;
  }

  i = Math.floor(6 * h);
  v = 1 - bl;
  f = 6 * h - i;
  if ((i & 0x01) != 0) {
    f = 1 - f;
  }
  n = wh + f * (v - wh);  // linear interpolation

  switch (i) {
    default:
    case 6:
    case 0: r = v; g = n; b = wh; break;
    case 1: r = n; g = v; b = wh; break;
    case 2: r = wh; g = v; b = n; break;
    case 3: r = wh; g = n; b = v; break;
    case 4: r = n; g = wh; b = v; break;
    case 5: r = v; g = wh; b = n; break;
  }

  return [r * 255, g * 255, b * 255];
}

function hwb2hsl(args) {
  return rgb2hsl(hwb2rgb(args));
}

function hwb2hsv(args) {
  return rgb2hsv(hwb2rgb(args));
}

function hwb2cmyk(args) {
  return rgb2cmyk(hwb2rgb(args));
}

function hwb2keyword(args) {
  return rgb2keyword(hwb2rgb(args));
}

function cmyk2rgb(cmyk) {
  var c = cmyk[0] / 100,
      m = cmyk[1] / 100,
      y = cmyk[2] / 100,
      k = cmyk[3] / 100,
      r, g, b;

  r = 1 - Math.min(1, c * (1 - k) + k);
  g = 1 - Math.min(1, m * (1 - k) + k);
  b = 1 - Math.min(1, y * (1 - k) + k);
  return [r * 255, g * 255, b * 255];
}

function cmyk2hsl(args) {
  return rgb2hsl(cmyk2rgb(args));
}

function cmyk2hsv(args) {
  return rgb2hsv(cmyk2rgb(args));
}

function cmyk2hwb(args) {
  return rgb2hwb(cmyk2rgb(args));
}

function cmyk2keyword(args) {
  return rgb2keyword(cmyk2rgb(args));
}


function xyz2rgb(xyz) {
  var x = xyz[0] / 100,
      y = xyz[1] / 100,
      z = xyz[2] / 100,
      r, g, b;

  r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
  g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
  b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);

  // assume sRGB
  r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
    : r = (r * 12.92);

  g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
    : g = (g * 12.92);

  b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
    : b = (b * 12.92);

  r = Math.min(Math.max(0, r), 1);
  g = Math.min(Math.max(0, g), 1);
  b = Math.min(Math.max(0, b), 1);

  return [r * 255, g * 255, b * 255];
}

function xyz2lab(xyz) {
  var x = xyz[0],
      y = xyz[1],
      z = xyz[2],
      l, a, b;

  x /= 95.047;
  y /= 100;
  z /= 108.883;

  x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
  y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
  z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);

  l = (116 * y) - 16;
  a = 500 * (x - y);
  b = 200 * (y - z);

  return [l, a, b];
}

function xyz2lch(args) {
  return lab2lch(xyz2lab(args));
}

function lab2xyz(lab) {
  var l = lab[0],
      a = lab[1],
      b = lab[2],
      x, y, z, y2;

  if (l <= 8) {
    y = (l * 100) / 903.3;
    y2 = (7.787 * (y / 100)) + (16 / 116);
  } else {
    y = 100 * Math.pow((l + 16) / 116, 3);
    y2 = Math.pow(y / 100, 1/3);
  }

  x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3);

  z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3);

  return [x, y, z];
}

function lab2lch(lab) {
  var l = lab[0],
      a = lab[1],
      b = lab[2],
      hr, h, c;

  hr = Math.atan2(b, a);
  h = hr * 360 / 2 / Math.PI;
  if (h < 0) {
    h += 360;
  }
  c = Math.sqrt(a * a + b * b);
  return [l, c, h];
}

function lab2rgb(args) {
  return xyz2rgb(lab2xyz(args));
}

function lch2lab(lch) {
  var l = lch[0],
      c = lch[1],
      h = lch[2],
      a, b, hr;

  hr = h / 360 * 2 * Math.PI;
  a = c * Math.cos(hr);
  b = c * Math.sin(hr);
  return [l, a, b];
}

function lch2xyz(args) {
  return lab2xyz(lch2lab(args));
}

function lch2rgb(args) {
  return lab2rgb(lch2lab(args));
}

function keyword2rgb(keyword) {
  return cssKeywords[keyword];
}

function keyword2hsl(args) {
  return rgb2hsl(keyword2rgb(args));
}

function keyword2hsv(args) {
  return rgb2hsv(keyword2rgb(args));
}

function keyword2hwb(args) {
  return rgb2hwb(keyword2rgb(args));
}

function keyword2cmyk(args) {
  return rgb2cmyk(keyword2rgb(args));
}

function keyword2lab(args) {
  return rgb2lab(keyword2rgb(args));
}

function keyword2xyz(args) {
  return rgb2xyz(keyword2rgb(args));
}

var cssKeywords = {
  aliceblue:  [240,248,255],
  antiquewhite: [250,235,215],
  aqua: [0,255,255],
  aquamarine: [127,255,212],
  azure:  [240,255,255],
  beige:  [245,245,220],
  bisque: [255,228,196],
  black:  [0,0,0],
  blanchedalmond: [255,235,205],
  blue: [0,0,255],
  blueviolet: [138,43,226],
  brown:  [165,42,42],
  burlywood:  [222,184,135],
  cadetblue:  [95,158,160],
  chartreuse: [127,255,0],
  chocolate:  [210,105,30],
  coral:  [255,127,80],
  cornflowerblue: [100,149,237],
  cornsilk: [255,248,220],
  crimson:  [220,20,60],
  cyan: [0,255,255],
  darkblue: [0,0,139],
  darkcyan: [0,139,139],
  darkgoldenrod:  [184,134,11],
  darkgray: [169,169,169],
  darkgreen:  [0,100,0],
  darkgrey: [169,169,169],
  darkkhaki:  [189,183,107],
  darkmagenta:  [139,0,139],
  darkolivegreen: [85,107,47],
  darkorange: [255,140,0],
  darkorchid: [153,50,204],
  darkred:  [139,0,0],
  darksalmon: [233,150,122],
  darkseagreen: [143,188,143],
  darkslateblue:  [72,61,139],
  darkslategray:  [47,79,79],
  darkslategrey:  [47,79,79],
  darkturquoise:  [0,206,209],
  darkviolet: [148,0,211],
  deeppink: [255,20,147],
  deepskyblue:  [0,191,255],
  dimgray:  [105,105,105],
  dimgrey:  [105,105,105],
  dodgerblue: [30,144,255],
  firebrick:  [178,34,34],
  floralwhite:  [255,250,240],
  forestgreen:  [34,139,34],
  fuchsia:  [255,0,255],
  gainsboro:  [220,220,220],
  ghostwhite: [248,248,255],
  gold: [255,215,0],
  goldenrod:  [218,165,32],
  gray: [128,128,128],
  green:  [0,128,0],
  greenyellow:  [173,255,47],
  grey: [128,128,128],
  honeydew: [240,255,240],
  hotpink:  [255,105,180],
  indianred:  [205,92,92],
  indigo: [75,0,130],
  ivory:  [255,255,240],
  khaki:  [240,230,140],
  lavender: [230,230,250],
  lavenderblush:  [255,240,245],
  lawngreen:  [124,252,0],
  lemonchiffon: [255,250,205],
  lightblue:  [173,216,230],
  lightcoral: [240,128,128],
  lightcyan:  [224,255,255],
  lightgoldenrodyellow: [250,250,210],
  lightgray:  [211,211,211],
  lightgreen: [144,238,144],
  lightgrey:  [211,211,211],
  lightpink:  [255,182,193],
  lightsalmon:  [255,160,122],
  lightseagreen:  [32,178,170],
  lightskyblue: [135,206,250],
  lightslategray: [119,136,153],
  lightslategrey: [119,136,153],
  lightsteelblue: [176,196,222],
  lightyellow:  [255,255,224],
  lime: [0,255,0],
  limegreen:  [50,205,50],
  linen:  [250,240,230],
  magenta:  [255,0,255],
  maroon: [128,0,0],
  mediumaquamarine: [102,205,170],
  mediumblue: [0,0,205],
  mediumorchid: [186,85,211],
  mediumpurple: [147,112,219],
  mediumseagreen: [60,179,113],
  mediumslateblue:  [123,104,238],
  mediumspringgreen:  [0,250,154],
  mediumturquoise:  [72,209,204],
  mediumvioletred:  [199,21,133],
  midnightblue: [25,25,112],
  mintcream:  [245,255,250],
  mistyrose:  [255,228,225],
  moccasin: [255,228,181],
  navajowhite:  [255,222,173],
  navy: [0,0,128],
  oldlace:  [253,245,230],
  olive:  [128,128,0],
  olivedrab:  [107,142,35],
  orange: [255,165,0],
  orangered:  [255,69,0],
  orchid: [218,112,214],
  palegoldenrod:  [238,232,170],
  palegreen:  [152,251,152],
  paleturquoise:  [175,238,238],
  palevioletred:  [219,112,147],
  papayawhip: [255,239,213],
  peachpuff:  [255,218,185],
  peru: [205,133,63],
  pink: [255,192,203],
  plum: [221,160,221],
  powderblue: [176,224,230],
  purple: [128,0,128],
  rebeccapurple: [102, 51, 153],
  red:  [255,0,0],
  rosybrown:  [188,143,143],
  royalblue:  [65,105,225],
  saddlebrown:  [139,69,19],
  salmon: [250,128,114],
  sandybrown: [244,164,96],
  seagreen: [46,139,87],
  seashell: [255,245,238],
  sienna: [160,82,45],
  silver: [192,192,192],
  skyblue:  [135,206,235],
  slateblue:  [106,90,205],
  slategray:  [112,128,144],
  slategrey:  [112,128,144],
  snow: [255,250,250],
  springgreen:  [0,255,127],
  steelblue:  [70,130,180],
  tan:  [210,180,140],
  teal: [0,128,128],
  thistle:  [216,191,216],
  tomato: [255,99,71],
  turquoise:  [64,224,208],
  violet: [238,130,238],
  wheat:  [245,222,179],
  white:  [255,255,255],
  whitesmoke: [245,245,245],
  yellow: [255,255,0],
  yellowgreen:  [154,205,50]
};

var reverseKeywords = {};
for (var key in cssKeywords) {
  reverseKeywords[JSON.stringify(cssKeywords[key])] = key;
}

},{}],4:[function(require,module,exports){
var conversions = require(3);

var convert = function() {
   return new Converter();
}

for (var func in conversions) {
  // export Raw versions
  convert[func + "Raw"] =  (function(func) {
    // accept array or plain args
    return function(arg) {
      if (typeof arg == "number")
        arg = Array.prototype.slice.call(arguments);
      return conversions[func](arg);
    }
  })(func);

  var pair = /(\w+)2(\w+)/.exec(func),
      from = pair[1],
      to = pair[2];

  // export rgb2hsl and ["rgb"]["hsl"]
  convert[from] = convert[from] || {};

  convert[from][to] = convert[func] = (function(func) { 
    return function(arg) {
      if (typeof arg == "number")
        arg = Array.prototype.slice.call(arguments);
      
      var val = conversions[func](arg);
      if (typeof val == "string" || val === undefined)
        return val; // keyword

      for (var i = 0; i < val.length; i++)
        val[i] = Math.round(val[i]);
      return val;
    }
  })(func);
}


/* Converter does lazy conversion and caching */
var Converter = function() {
   this.convs = {};
};

/* Either get the values for a space or
  set the values for a space, depending on args */
Converter.prototype.routeSpace = function(space, args) {
   var values = args[0];
   if (values === undefined) {
      // color.rgb()
      return this.getValues(space);
   }
   // color.rgb(10, 10, 10)
   if (typeof values == "number") {
      values = Array.prototype.slice.call(args);        
   }

   return this.setValues(space, values);
};
  
/* Set the values for a space, invalidating cache */
Converter.prototype.setValues = function(space, values) {
   this.space = space;
   this.convs = {};
   this.convs[space] = values;
   return this;
};

/* Get the values for a space. If there's already
  a conversion for the space, fetch it, otherwise
  compute it */
Converter.prototype.getValues = function(space) {
   var vals = this.convs[space];
   if (!vals) {
      var fspace = this.space,
          from = this.convs[fspace];
      vals = convert[fspace][space](from);

      this.convs[space] = vals;
   }
  return vals;
};

["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) {
   Converter.prototype[space] = function(vals) {
      return this.routeSpace(space, arguments);
   }
});

module.exports = convert;
},{"3":3}],5:[function(require,module,exports){
'use strict'

module.exports = {
	"aliceblue": [240, 248, 255],
	"antiquewhite": [250, 235, 215],
	"aqua": [0, 255, 255],
	"aquamarine": [127, 255, 212],
	"azure": [240, 255, 255],
	"beige": [245, 245, 220],
	"bisque": [255, 228, 196],
	"black": [0, 0, 0],
	"blanchedalmond": [255, 235, 205],
	"blue": [0, 0, 255],
	"blueviolet": [138, 43, 226],
	"brown": [165, 42, 42],
	"burlywood": [222, 184, 135],
	"cadetblue": [95, 158, 160],
	"chartreuse": [127, 255, 0],
	"chocolate": [210, 105, 30],
	"coral": [255, 127, 80],
	"cornflowerblue": [100, 149, 237],
	"cornsilk": [255, 248, 220],
	"crimson": [220, 20, 60],
	"cyan": [0, 255, 255],
	"darkblue": [0, 0, 139],
	"darkcyan": [0, 139, 139],
	"darkgoldenrod": [184, 134, 11],
	"darkgray": [169, 169, 169],
	"darkgreen": [0, 100, 0],
	"darkgrey": [169, 169, 169],
	"darkkhaki": [189, 183, 107],
	"darkmagenta": [139, 0, 139],
	"darkolivegreen": [85, 107, 47],
	"darkorange": [255, 140, 0],
	"darkorchid": [153, 50, 204],
	"darkred": [139, 0, 0],
	"darksalmon": [233, 150, 122],
	"darkseagreen": [143, 188, 143],
	"darkslateblue": [72, 61, 139],
	"darkslategray": [47, 79, 79],
	"darkslategrey": [47, 79, 79],
	"darkturquoise": [0, 206, 209],
	"darkviolet": [148, 0, 211],
	"deeppink": [255, 20, 147],
	"deepskyblue": [0, 191, 255],
	"dimgray": [105, 105, 105],
	"dimgrey": [105, 105, 105],
	"dodgerblue": [30, 144, 255],
	"firebrick": [178, 34, 34],
	"floralwhite": [255, 250, 240],
	"forestgreen": [34, 139, 34],
	"fuchsia": [255, 0, 255],
	"gainsboro": [220, 220, 220],
	"ghostwhite": [248, 248, 255],
	"gold": [255, 215, 0],
	"goldenrod": [218, 165, 32],
	"gray": [128, 128, 128],
	"green": [0, 128, 0],
	"greenyellow": [173, 255, 47],
	"grey": [128, 128, 128],
	"honeydew": [240, 255, 240],
	"hotpink": [255, 105, 180],
	"indianred": [205, 92, 92],
	"indigo": [75, 0, 130],
	"ivory": [255, 255, 240],
	"khaki": [240, 230, 140],
	"lavender": [230, 230, 250],
	"lavenderblush": [255, 240, 245],
	"lawngreen": [124, 252, 0],
	"lemonchiffon": [255, 250, 205],
	"lightblue": [173, 216, 230],
	"lightcoral": [240, 128, 128],
	"lightcyan": [224, 255, 255],
	"lightgoldenrodyellow": [250, 250, 210],
	"lightgray": [211, 211, 211],
	"lightgreen": [144, 238, 144],
	"lightgrey": [211, 211, 211],
	"lightpink": [255, 182, 193],
	"lightsalmon": [255, 160, 122],
	"lightseagreen": [32, 178, 170],
	"lightskyblue": [135, 206, 250],
	"lightslategray": [119, 136, 153],
	"lightslategrey": [119, 136, 153],
	"lightsteelblue": [176, 196, 222],
	"lightyellow": [255, 255, 224],
	"lime": [0, 255, 0],
	"limegreen": [50, 205, 50],
	"linen": [250, 240, 230],
	"magenta": [255, 0, 255],
	"maroon": [128, 0, 0],
	"mediumaquamarine": [102, 205, 170],
	"mediumblue": [0, 0, 205],
	"mediumorchid": [186, 85, 211],
	"mediumpurple": [147, 112, 219],
	"mediumseagreen": [60, 179, 113],
	"mediumslateblue": [123, 104, 238],
	"mediumspringgreen": [0, 250, 154],
	"mediumturquoise": [72, 209, 204],
	"mediumvioletred": [199, 21, 133],
	"midnightblue": [25, 25, 112],
	"mintcream": [245, 255, 250],
	"mistyrose": [255, 228, 225],
	"moccasin": [255, 228, 181],
	"navajowhite": [255, 222, 173],
	"navy": [0, 0, 128],
	"oldlace": [253, 245, 230],
	"olive": [128, 128, 0],
	"olivedrab": [107, 142, 35],
	"orange": [255, 165, 0],
	"orangered": [255, 69, 0],
	"orchid": [218, 112, 214],
	"palegoldenrod": [238, 232, 170],
	"palegreen": [152, 251, 152],
	"paleturquoise": [175, 238, 238],
	"palevioletred": [219, 112, 147],
	"papayawhip": [255, 239, 213],
	"peachpuff": [255, 218, 185],
	"peru": [205, 133, 63],
	"pink": [255, 192, 203],
	"plum": [221, 160, 221],
	"powderblue": [176, 224, 230],
	"purple": [128, 0, 128],
	"rebeccapurple": [102, 51, 153],
	"red": [255, 0, 0],
	"rosybrown": [188, 143, 143],
	"royalblue": [65, 105, 225],
	"saddlebrown": [139, 69, 19],
	"salmon": [250, 128, 114],
	"sandybrown": [244, 164, 96],
	"seagreen": [46, 139, 87],
	"seashell": [255, 245, 238],
	"sienna": [160, 82, 45],
	"silver": [192, 192, 192],
	"skyblue": [135, 206, 235],
	"slateblue": [106, 90, 205],
	"slategray": [112, 128, 144],
	"slategrey": [112, 128, 144],
	"snow": [255, 250, 250],
	"springgreen": [0, 255, 127],
	"steelblue": [70, 130, 180],
	"tan": [210, 180, 140],
	"teal": [0, 128, 128],
	"thistle": [216, 191, 216],
	"tomato": [255, 99, 71],
	"turquoise": [64, 224, 208],
	"violet": [238, 130, 238],
	"wheat": [245, 222, 179],
	"white": [255, 255, 255],
	"whitesmoke": [245, 245, 245],
	"yellow": [255, 255, 0],
	"yellowgreen": [154, 205, 50]
};

},{}],6:[function(require,module,exports){
//! moment.js
//! version : 2.18.1
//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
//! license : MIT
//! momentjs.com

;(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    global.moment = factory()
}(this, (function () { 'use strict';

var hookCallback;

function hooks () {
    return hookCallback.apply(null, arguments);
}

// This is done to register the method called with moment()
// without creating circular dependencies.
function setHookCallback (callback) {
    hookCallback = callback;
}

function isArray(input) {
    return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]';
}

function isObject(input) {
    // IE8 will treat undefined and null as object if it wasn't for
    // input != null
    return input != null && Object.prototype.toString.call(input) === '[object Object]';
}

function isObjectEmpty(obj) {
    var k;
    for (k in obj) {
        // even if its not own property I'd still call it non-empty
        return false;
    }
    return true;
}

function isUndefined(input) {
    return input === void 0;
}

function isNumber(input) {
    return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]';
}

function isDate(input) {
    return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]';
}

function map(arr, fn) {
    var res = [], i;
    for (i = 0; i < arr.length; ++i) {
        res.push(fn(arr[i], i));
    }
    return res;
}

function hasOwnProp(a, b) {
    return Object.prototype.hasOwnProperty.call(a, b);
}

function extend(a, b) {
    for (var i in b) {
        if (hasOwnProp(b, i)) {
            a[i] = b[i];
        }
    }

    if (hasOwnProp(b, 'toString')) {
        a.toString = b.toString;
    }

    if (hasOwnProp(b, 'valueOf')) {
        a.valueOf = b.valueOf;
    }

    return a;
}

function createUTC (input, format, locale, strict) {
    return createLocalOrUTC(input, format, locale, strict, true).utc();
}

function defaultParsingFlags() {
    // We need to deep clone this object.
    return {
        empty           : false,
        unusedTokens    : [],
        unusedInput     : [],
        overflow        : -2,
        charsLeftOver   : 0,
        nullInput       : false,
        invalidMonth    : null,
        invalidFormat   : false,
        userInvalidated : false,
        iso             : false,
        parsedDateParts : [],
        meridiem        : null,
        rfc2822         : false,
        weekdayMismatch : false
    };
}

function getParsingFlags(m) {
    if (m._pf == null) {
        m._pf = defaultParsingFlags();
    }
    return m._pf;
}

var some;
if (Array.prototype.some) {
    some = Array.prototype.some;
} else {
    some = function (fun) {
        var t = Object(this);
        var len = t.length >>> 0;

        for (var i = 0; i < len; i++) {
            if (i in t && fun.call(this, t[i], i, t)) {
                return true;
            }
        }

        return false;
    };
}

var some$1 = some;

function isValid(m) {
    if (m._isValid == null) {
        var flags = getParsingFlags(m);
        var parsedParts = some$1.call(flags.parsedDateParts, function (i) {
            return i != null;
        });
        var isNowValid = !isNaN(m._d.getTime()) &&
            flags.overflow < 0 &&
            !flags.empty &&
            !flags.invalidMonth &&
            !flags.invalidWeekday &&
            !flags.nullInput &&
            !flags.invalidFormat &&
            !flags.userInvalidated &&
            (!flags.meridiem || (flags.meridiem && parsedParts));

        if (m._strict) {
            isNowValid = isNowValid &&
                flags.charsLeftOver === 0 &&
                flags.unusedTokens.length === 0 &&
                flags.bigHour === undefined;
        }

        if (Object.isFrozen == null || !Object.isFrozen(m)) {
            m._isValid = isNowValid;
        }
        else {
            return isNowValid;
        }
    }
    return m._isValid;
}

function createInvalid (flags) {
    var m = createUTC(NaN);
    if (flags != null) {
        extend(getParsingFlags(m), flags);
    }
    else {
        getParsingFlags(m).userInvalidated = true;
    }

    return m;
}

// Plugins that add properties should also add the key here (null value),
// so we can properly clone ourselves.
var momentProperties = hooks.momentProperties = [];

function copyConfig(to, from) {
    var i, prop, val;

    if (!isUndefined(from._isAMomentObject)) {
        to._isAMomentObject = from._isAMomentObject;
    }
    if (!isUndefined(from._i)) {
        to._i = from._i;
    }
    if (!isUndefined(from._f)) {
        to._f = from._f;
    }
    if (!isUndefined(from._l)) {
        to._l = from._l;
    }
    if (!isUndefined(from._strict)) {
        to._strict = from._strict;
    }
    if (!isUndefined(from._tzm)) {
        to._tzm = from._tzm;
    }
    if (!isUndefined(from._isUTC)) {
        to._isUTC = from._isUTC;
    }
    if (!isUndefined(from._offset)) {
        to._offset = from._offset;
    }
    if (!isUndefined(from._pf)) {
        to._pf = getParsingFlags(from);
    }
    if (!isUndefined(from._locale)) {
        to._locale = from._locale;
    }

    if (momentProperties.length > 0) {
        for (i = 0; i < momentProperties.length; i++) {
            prop = momentProperties[i];
            val = from[prop];
            if (!isUndefined(val)) {
                to[prop] = val;
            }
        }
    }

    return to;
}

var updateInProgress = false;

// Moment prototype object
function Moment(config) {
    copyConfig(this, config);
    this._d = new Date(config._d != null ? config._d.getTime() : NaN);
    if (!this.isValid()) {
        this._d = new Date(NaN);
    }
    // Prevent infinite loop in case updateOffset creates new moment
    // objects.
    if (updateInProgress === false) {
        updateInProgress = true;
        hooks.updateOffset(this);
        updateInProgress = false;
    }
}

function isMoment (obj) {
    return obj instanceof Moment || (obj != null && obj._isAMomentObject != null);
}

function absFloor (number) {
    if (number < 0) {
        // -0 -> 0
        return Math.ceil(number) || 0;
    } else {
        return Math.floor(number);
    }
}

function toInt(argumentForCoercion) {
    var coercedNumber = +argumentForCoercion,
        value = 0;

    if (coercedNumber !== 0 && isFinite(coercedNumber)) {
        value = absFloor(coercedNumber);
    }

    return value;
}

// compare two arrays, return the number of differences
function compareArrays(array1, array2, dontConvert) {
    var len = Math.min(array1.length, array2.length),
        lengthDiff = Math.abs(array1.length - array2.length),
        diffs = 0,
        i;
    for (i = 0; i < len; i++) {
        if ((dontConvert && array1[i] !== array2[i]) ||
            (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
            diffs++;
        }
    }
    return diffs + lengthDiff;
}

function warn(msg) {
    if (hooks.suppressDeprecationWarnings === false &&
            (typeof console !==  'undefined') && console.warn) {
        console.warn('Deprecation warning: ' + msg);
    }
}

function deprecate(msg, fn) {
    var firstTime = true;

    return extend(function () {
        if (hooks.deprecationHandler != null) {
            hooks.deprecationHandler(null, msg);
        }
        if (firstTime) {
            var args = [];
            var arg;
            for (var i = 0; i < arguments.length; i++) {
                arg = '';
                if (typeof arguments[i] === 'object') {
                    arg += '\n[' + i + '] ';
                    for (var key in arguments[0]) {
                        arg += key + ': ' + arguments[0][key] + ', ';
                    }
                    arg = arg.slice(0, -2); // Remove trailing comma and space
                } else {
                    arg = arguments[i];
                }
                args.push(arg);
            }
            warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack);
            firstTime = false;
        }
        return fn.apply(this, arguments);
    }, fn);
}

var deprecations = {};

function deprecateSimple(name, msg) {
    if (hooks.deprecationHandler != null) {
        hooks.deprecationHandler(name, msg);
    }
    if (!deprecations[name]) {
        warn(msg);
        deprecations[name] = true;
    }
}

hooks.suppressDeprecationWarnings = false;
hooks.deprecationHandler = null;

function isFunction(input) {
    return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]';
}

function set (config) {
    var prop, i;
    for (i in config) {
        prop = config[i];
        if (isFunction(prop)) {
            this[i] = prop;
        } else {
            this['_' + i] = prop;
        }
    }
    this._config = config;
    // Lenient ordinal parsing accepts just a number in addition to
    // number + (possibly) stuff coming from _dayOfMonthOrdinalParse.
    // TODO: Remove "ordinalParse" fallback in next major release.
    this._dayOfMonthOrdinalParseLenient = new RegExp(
        (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) +
            '|' + (/\d{1,2}/).source);
}

function mergeConfigs(parentConfig, childConfig) {
    var res = extend({}, parentConfig), prop;
    for (prop in childConfig) {
        if (hasOwnProp(childConfig, prop)) {
            if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) {
                res[prop] = {};
                extend(res[prop], parentConfig[prop]);
                extend(res[prop], childConfig[prop]);
            } else if (childConfig[prop] != null) {
                res[prop] = childConfig[prop];
            } else {
                delete res[prop];
            }
        }
    }
    for (prop in parentConfig) {
        if (hasOwnProp(parentConfig, prop) &&
                !hasOwnProp(childConfig, prop) &&
                isObject(parentConfig[prop])) {
            // make sure changes to properties don't modify parent config
            res[prop] = extend({}, res[prop]);
        }
    }
    return res;
}

function Locale(config) {
    if (config != null) {
        this.set(config);
    }
}

var keys;

if (Object.keys) {
    keys = Object.keys;
} else {
    keys = function (obj) {
        var i, res = [];
        for (i in obj) {
            if (hasOwnProp(obj, i)) {
                res.push(i);
            }
        }
        return res;
    };
}

var keys$1 = keys;

var defaultCalendar = {
    sameDay : '[Today at] LT',
    nextDay : '[Tomorrow at] LT',
    nextWeek : 'dddd [at] LT',
    lastDay : '[Yesterday at] LT',
    lastWeek : '[Last] dddd [at] LT',
    sameElse : 'L'
};

function calendar (key, mom, now) {
    var output = this._calendar[key] || this._calendar['sameElse'];
    return isFunction(output) ? output.call(mom, now) : output;
}

var defaultLongDateFormat = {
    LTS  : 'h:mm:ss A',
    LT   : 'h:mm A',
    L    : 'MM/DD/YYYY',
    LL   : 'MMMM D, YYYY',
    LLL  : 'MMMM D, YYYY h:mm A',
    LLLL : 'dddd, MMMM D, YYYY h:mm A'
};

function longDateFormat (key) {
    var format = this._longDateFormat[key],
        formatUpper = this._longDateFormat[key.toUpperCase()];

    if (format || !formatUpper) {
        return format;
    }

    this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) {
        return val.slice(1);
    });

    return this._longDateFormat[key];
}

var defaultInvalidDate = 'Invalid date';

function invalidDate () {
    return this._invalidDate;
}

var defaultOrdinal = '%d';
var defaultDayOfMonthOrdinalParse = /\d{1,2}/;

function ordinal (number) {
    return this._ordinal.replace('%d', number);
}

var defaultRelativeTime = {
    future : 'in %s',
    past   : '%s ago',
    s  : 'a few seconds',
    ss : '%d seconds',
    m  : 'a minute',
    mm : '%d minutes',
    h  : 'an hour',
    hh : '%d hours',
    d  : 'a day',
    dd : '%d days',
    M  : 'a month',
    MM : '%d months',
    y  : 'a year',
    yy : '%d years'
};

function relativeTime (number, withoutSuffix, string, isFuture) {
    var output = this._relativeTime[string];
    return (isFunction(output)) ?
        output(number, withoutSuffix, string, isFuture) :
        output.replace(/%d/i, number);
}

function pastFuture (diff, output) {
    var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
    return isFunction(format) ? format(output) : format.replace(/%s/i, output);
}

var aliases = {};

function addUnitAlias (unit, shorthand) {
    var lowerCase = unit.toLowerCase();
    aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit;
}

function normalizeUnits(units) {
    return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined;
}

function normalizeObjectUnits(inputObject) {
    var normalizedInput = {},
        normalizedProp,
        prop;

    for (prop in inputObject) {
        if (hasOwnProp(inputObject, prop)) {
            normalizedProp = normalizeUnits(prop);
            if (normalizedProp) {
                normalizedInput[normalizedProp] = inputObject[prop];
            }
        }
    }

    return normalizedInput;
}

var priorities = {};

function addUnitPriority(unit, priority) {
    priorities[unit] = priority;
}

function getPrioritizedUnits(unitsObj) {
    var units = [];
    for (var u in unitsObj) {
        units.push({unit: u, priority: priorities[u]});
    }
    units.sort(function (a, b) {
        return a.priority - b.priority;
    });
    return units;
}

function makeGetSet (unit, keepTime) {
    return function (value) {
        if (value != null) {
            set$1(this, unit, value);
            hooks.updateOffset(this, keepTime);
            return this;
        } else {
            return get(this, unit);
        }
    };
}

function get (mom, unit) {
    return mom.isValid() ?
        mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN;
}

function set$1 (mom, unit, value) {
    if (mom.isValid()) {
        mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
    }
}

// MOMENTS

function stringGet (units) {
    units = normalizeUnits(units);
    if (isFunction(this[units])) {
        return this[units]();
    }
    return this;
}


function stringSet (units, value) {
    if (typeof units === 'object') {
        units = normalizeObjectUnits(units);
        var prioritized = getPrioritizedUnits(units);
        for (var i = 0; i < prioritized.length; i++) {
            this[prioritized[i].unit](units[prioritized[i].unit]);
        }
    } else {
        units = normalizeUnits(units);
        if (isFunction(this[units])) {
            return this[units](value);
        }
    }
    return this;
}

function zeroFill(number, targetLength, forceSign) {
    var absNumber = '' + Math.abs(number),
        zerosToFill = targetLength - absNumber.length,
        sign = number >= 0;
    return (sign ? (forceSign ? '+' : '') : '-') +
        Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber;
}

var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g;

var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g;

var formatFunctions = {};

var formatTokenFunctions = {};

// token:    'M'
// padded:   ['MM', 2]
// ordinal:  'Mo'
// callback: function () { this.month() + 1 }
function addFormatToken (token, padded, ordinal, callback) {
    var func = callback;
    if (typeof callback === 'string') {
        func = function () {
            return this[callback]();
        };
    }
    if (token) {
        formatTokenFunctions[token] = func;
    }
    if (padded) {
        formatTokenFunctions[padded[0]] = function () {
            return zeroFill(func.apply(this, arguments), padded[1], padded[2]);
        };
    }
    if (ordinal) {
        formatTokenFunctions[ordinal] = function () {
            return this.localeData().ordinal(func.apply(this, arguments), token);
        };
    }
}

function removeFormattingTokens(input) {
    if (input.match(/\[[\s\S]/)) {
        return input.replace(/^\[|\]$/g, '');
    }
    return input.replace(/\\/g, '');
}

function makeFormatFunction(format) {
    var array = format.match(formattingTokens), i, length;

    for (i = 0, length = array.length; i < length; i++) {
        if (formatTokenFunctions[array[i]]) {
            array[i] = formatTokenFunctions[array[i]];
        } else {
            array[i] = removeFormattingTokens(array[i]);
        }
    }

    return function (mom) {
        var output = '', i;
        for (i = 0; i < length; i++) {
            output += isFunction(array[i]) ? array[i].call(mom, format) : array[i];
        }
        return output;
    };
}

// format date using native date object
function formatMoment(m, format) {
    if (!m.isValid()) {
        return m.localeData().invalidDate();
    }

    format = expandFormat(format, m.localeData());
    formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format);

    return formatFunctions[format](m);
}

function expandFormat(format, locale) {
    var i = 5;

    function replaceLongDateFormatTokens(input) {
        return locale.longDateFormat(input) || input;
    }

    localFormattingTokens.lastIndex = 0;
    while (i >= 0 && localFormattingTokens.test(format)) {
        format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
        localFormattingTokens.lastIndex = 0;
        i -= 1;
    }

    return format;
}

var match1         = /\d/;            //       0 - 9
var match2         = /\d\d/;          //      00 - 99
var match3         = /\d{3}/;         //     000 - 999
var match4         = /\d{4}/;         //    0000 - 9999
var match6         = /[+-]?\d{6}/;    // -999999 - 999999
var match1to2      = /\d\d?/;         //       0 - 99
var match3to4      = /\d\d\d\d?/;     //     999 - 9999
var match5to6      = /\d\d\d\d\d\d?/; //   99999 - 999999
var match1to3      = /\d{1,3}/;       //       0 - 999
var match1to4      = /\d{1,4}/;       //       0 - 9999
var match1to6      = /[+-]?\d{1,6}/;  // -999999 - 999999

var matchUnsigned  = /\d+/;           //       0 - inf
var matchSigned    = /[+-]?\d+/;      //    -inf - inf

var matchOffset    = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z
var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z

var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123

// any word (or two) characters or numbers including two/three word month in arabic.
// includes scottish gaelic two word and hyphenated months
var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i;


var regexes = {};

function addRegexToken (token, regex, strictRegex) {
    regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) {
        return (isStrict && strictRegex) ? strictRegex : regex;
    };
}

function getParseRegexForToken (token, config) {
    if (!hasOwnProp(regexes, token)) {
        return new RegExp(unescapeFormat(token));
    }

    return regexes[token](config._strict, config._locale);
}

// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
function unescapeFormat(s) {
    return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
        return p1 || p2 || p3 || p4;
    }));
}

function regexEscape(s) {
    return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}

var tokens = {};

function addParseToken (token, callback) {
    var i, func = callback;
    if (typeof token === 'string') {
        token = [token];
    }
    if (isNumber(callback)) {
        func = function (input, array) {
            array[callback] = toInt(input);
        };
    }
    for (i = 0; i < token.length; i++) {
        tokens[token[i]] = func;
    }
}

function addWeekParseToken (token, callback) {
    addParseToken(token, function (input, array, config, token) {
        config._w = config._w || {};
        callback(input, config._w, config, token);
    });
}

function addTimeToArrayFromToken(token, input, config) {
    if (input != null && hasOwnProp(tokens, token)) {
        tokens[token](input, config._a, config, token);
    }
}

var YEAR = 0;
var MONTH = 1;
var DATE = 2;
var HOUR = 3;
var MINUTE = 4;
var SECOND = 5;
var MILLISECOND = 6;
var WEEK = 7;
var WEEKDAY = 8;

var indexOf;

if (Array.prototype.indexOf) {
    indexOf = Array.prototype.indexOf;
} else {
    indexOf = function (o) {
        // I know
        var i;
        for (i = 0; i < this.length; ++i) {
            if (this[i] === o) {
                return i;
            }
        }
        return -1;
    };
}

var indexOf$1 = indexOf;

function daysInMonth(year, month) {
    return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
}

// FORMATTING

addFormatToken('M', ['MM', 2], 'Mo', function () {
    return this.month() + 1;
});

addFormatToken('MMM', 0, 0, function (format) {
    return this.localeData().monthsShort(this, format);
});

addFormatToken('MMMM', 0, 0, function (format) {
    return this.localeData().months(this, format);
});

// ALIASES

addUnitAlias('month', 'M');

// PRIORITY

addUnitPriority('month', 8);

// PARSING

addRegexToken('M',    match1to2);
addRegexToken('MM',   match1to2, match2);
addRegexToken('MMM',  function (isStrict, locale) {
    return locale.monthsShortRegex(isStrict);
});
addRegexToken('MMMM', function (isStrict, locale) {
    return locale.monthsRegex(isStrict);
});

addParseToken(['M', 'MM'], function (input, array) {
    array[MONTH] = toInt(input) - 1;
});

addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {
    var month = config._locale.monthsParse(input, token, config._strict);
    // if we didn't find a month name, mark the date as invalid.
    if (month != null) {
        array[MONTH] = month;
    } else {
        getParsingFlags(config).invalidMonth = input;
    }
});

// LOCALES

var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/;
var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_');
function localeMonths (m, format) {
    if (!m) {
        return isArray(this._months) ? this._months :
            this._months['standalone'];
    }
    return isArray(this._months) ? this._months[m.month()] :
        this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()];
}

var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_');
function localeMonthsShort (m, format) {
    if (!m) {
        return isArray(this._monthsShort) ? this._monthsShort :
            this._monthsShort['standalone'];
    }
    return isArray(this._monthsShort) ? this._monthsShort[m.month()] :
        this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()];
}

function handleStrictParse(monthName, format, strict) {
    var i, ii, mom, llc = monthName.toLocaleLowerCase();
    if (!this._monthsParse) {
        // this is not used
        this._monthsParse = [];
        this._longMonthsParse = [];
        this._shortMonthsParse = [];
        for (i = 0; i < 12; ++i) {
            mom = createUTC([2000, i]);
            this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase();
            this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase();
        }
    }

    if (strict) {
        if (format === 'MMM') {
            ii = indexOf$1.call(this._shortMonthsParse, llc);
            return ii !== -1 ? ii : null;
        } else {
            ii = indexOf$1.call(this._longMonthsParse, llc);
            return ii !== -1 ? ii : null;
        }
    } else {
        if (format === 'MMM') {
            ii = indexOf$1.call(this._shortMonthsParse, llc);
            if (ii !== -1) {
                return ii;
            }
            ii = indexOf$1.call(this._longMonthsParse, llc);
            return ii !== -1 ? ii : null;
        } else {
            ii = indexOf$1.call(this._longMonthsParse, llc);
            if (ii !== -1) {
                return ii;
            }
            ii = indexOf$1.call(this._shortMonthsParse, llc);
            return ii !== -1 ? ii : null;
        }
    }
}

function localeMonthsParse (monthName, format, strict) {
    var i, mom, regex;

    if (this._monthsParseExact) {
        return handleStrictParse.call(this, monthName, format, strict);
    }

    if (!this._monthsParse) {
        this._monthsParse = [];
        this._longMonthsParse = [];
        this._shortMonthsParse = [];
    }

    // TODO: add sorting
    // Sorting makes sure if one month (or abbr) is a prefix of another
    // see sorting in computeMonthsParse
    for (i = 0; i < 12; i++) {
        // make the regex if we don't have it already
        mom = createUTC([2000, i]);
        if (strict && !this._longMonthsParse[i]) {
            this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i');
            this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i');
        }
        if (!strict && !this._monthsParse[i]) {
            regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
            this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
        }
        // test the regex
        if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) {
            return i;
        } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) {
            return i;
        } else if (!strict && this._monthsParse[i].test(monthName)) {
            return i;
        }
    }
}

// MOMENTS

function setMonth (mom, value) {
    var dayOfMonth;

    if (!mom.isValid()) {
        // No op
        return mom;
    }

    if (typeof value === 'string') {
        if (/^\d+$/.test(value)) {
            value = toInt(value);
        } else {
            value = mom.localeData().monthsParse(value);
            // TODO: Another silent failure?
            if (!isNumber(value)) {
                return mom;
            }
        }
    }

    dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value));
    mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
    return mom;
}

function getSetMonth (value) {
    if (value != null) {
        setMonth(this, value);
        hooks.updateOffset(this, true);
        return this;
    } else {
        return get(this, 'Month');
    }
}

function getDaysInMonth () {
    return daysInMonth(this.year(), this.month());
}

var defaultMonthsShortRegex = matchWord;
function monthsShortRegex (isStrict) {
    if (this._monthsParseExact) {
        if (!hasOwnProp(this, '_monthsRegex')) {
            computeMonthsParse.call(this);
        }
        if (isStrict) {
            return this._monthsShortStrictRegex;
        } else {
            return this._monthsShortRegex;
        }
    } else {
        if (!hasOwnProp(this, '_monthsShortRegex')) {
            this._monthsShortRegex = defaultMonthsShortRegex;
        }
        return this._monthsShortStrictRegex && isStrict ?
            this._monthsShortStrictRegex : this._monthsShortRegex;
    }
}

var defaultMonthsRegex = matchWord;
function monthsRegex (isStrict) {
    if (this._monthsParseExact) {
        if (!hasOwnProp(this, '_monthsRegex')) {
            computeMonthsParse.call(this);
        }
        if (isStrict) {
            return this._monthsStrictRegex;
        } else {
            return this._monthsRegex;
        }
    } else {
        if (!hasOwnProp(this, '_monthsRegex')) {
            this._monthsRegex = defaultMonthsRegex;
        }
        return this._monthsStrictRegex && isStrict ?
            this._monthsStrictRegex : this._monthsRegex;
    }
}

function computeMonthsParse () {
    function cmpLenRev(a, b) {
        return b.length - a.length;
    }

    var shortPieces = [], longPieces = [], mixedPieces = [],
        i, mom;
    for (i = 0; i < 12; i++) {
        // make the regex if we don't have it already
        mom = createUTC([2000, i]);
        shortPieces.push(this.monthsShort(mom, ''));
        longPieces.push(this.months(mom, ''));
        mixedPieces.push(this.months(mom, ''));
        mixedPieces.push(this.monthsShort(mom, ''));
    }
    // Sorting makes sure if one month (or abbr) is a prefix of another it
    // will match the longer piece.
    shortPieces.sort(cmpLenRev);
    longPieces.sort(cmpLenRev);
    mixedPieces.sort(cmpLenRev);
    for (i = 0; i < 12; i++) {
        shortPieces[i] = regexEscape(shortPieces[i]);
        longPieces[i] = regexEscape(longPieces[i]);
    }
    for (i = 0; i < 24; i++) {
        mixedPieces[i] = regexEscape(mixedPieces[i]);
    }

    this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
    this._monthsShortRegex = this._monthsRegex;
    this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
    this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
}

// FORMATTING

addFormatToken('Y', 0, 0, function () {
    var y = this.year();
    return y <= 9999 ? '' + y : '+' + y;
});

addFormatToken(0, ['YY', 2], 0, function () {
    return this.year() % 100;
});

addFormatToken(0, ['YYYY',   4],       0, 'year');
addFormatToken(0, ['YYYYY',  5],       0, 'year');
addFormatToken(0, ['YYYYYY', 6, true], 0, 'year');

// ALIASES

addUnitAlias('year', 'y');

// PRIORITIES

addUnitPriority('year', 1);

// PARSING

addRegexToken('Y',      matchSigned);
addRegexToken('YY',     match1to2, match2);
addRegexToken('YYYY',   match1to4, match4);
addRegexToken('YYYYY',  match1to6, match6);
addRegexToken('YYYYYY', match1to6, match6);

addParseToken(['YYYYY', 'YYYYYY'], YEAR);
addParseToken('YYYY', function (input, array) {
    array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input);
});
addParseToken('YY', function (input, array) {
    array[YEAR] = hooks.parseTwoDigitYear(input);
});
addParseToken('Y', function (input, array) {
    array[YEAR] = parseInt(input, 10);
});

// HELPERS

function daysInYear(year) {
    return isLeapYear(year) ? 366 : 365;
}

function isLeapYear(year) {
    return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
}

// HOOKS

hooks.parseTwoDigitYear = function (input) {
    return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
};

// MOMENTS

var getSetYear = makeGetSet('FullYear', true);

function getIsLeapYear () {
    return isLeapYear(this.year());
}

function createDate (y, m, d, h, M, s, ms) {
    // can't just apply() to create a date:
    // https://stackoverflow.com/q/181348
    var date = new Date(y, m, d, h, M, s, ms);

    // the date constructor remaps years 0-99 to 1900-1999
    if (y < 100 && y >= 0 && isFinite(date.getFullYear())) {
        date.setFullYear(y);
    }
    return date;
}

function createUTCDate (y) {
    var date = new Date(Date.UTC.apply(null, arguments));

    // the Date.UTC function remaps years 0-99 to 1900-1999
    if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) {
        date.setUTCFullYear(y);
    }
    return date;
}

// start-of-first-week - start-of-year
function firstWeekOffset(year, dow, doy) {
    var // first-week day -- which january is always in the first week (4 for iso, 1 for other)
        fwd = 7 + dow - doy,
        // first-week day local weekday -- which local weekday is fwd
        fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7;

    return -fwdlw + fwd - 1;
}

// https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
function dayOfYearFromWeeks(year, week, weekday, dow, doy) {
    var localWeekday = (7 + weekday - dow) % 7,
        weekOffset = firstWeekOffset(year, dow, doy),
        dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset,
        resYear, resDayOfYear;

    if (dayOfYear <= 0) {
        resYear = year - 1;
        resDayOfYear = daysInYear(resYear) + dayOfYear;
    } else if (dayOfYear > daysInYear(year)) {
        resYear = year + 1;
        resDayOfYear = dayOfYear - daysInYear(year);
    } else {
        resYear = year;
        resDayOfYear = dayOfYear;
    }

    return {
        year: resYear,
        dayOfYear: resDayOfYear
    };
}

function weekOfYear(mom, dow, doy) {
    var weekOffset = firstWeekOffset(mom.year(), dow, doy),
        week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1,
        resWeek, resYear;

    if (week < 1) {
        resYear = mom.year() - 1;
        resWeek = week + weeksInYear(resYear, dow, doy);
    } else if (week > weeksInYear(mom.year(), dow, doy)) {
        resWeek = week - weeksInYear(mom.year(), dow, doy);
        resYear = mom.year() + 1;
    } else {
        resYear = mom.year();
        resWeek = week;
    }

    return {
        week: resWeek,
        year: resYear
    };
}

function weeksInYear(year, dow, doy) {
    var weekOffset = firstWeekOffset(year, dow, doy),
        weekOffsetNext = firstWeekOffset(year + 1, dow, doy);
    return (daysInYear(year) - weekOffset + weekOffsetNext) / 7;
}

// FORMATTING

addFormatToken('w', ['ww', 2], 'wo', 'week');
addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek');

// ALIASES

addUnitAlias('week', 'w');
addUnitAlias('isoWeek', 'W');

// PRIORITIES

addUnitPriority('week', 5);
addUnitPriority('isoWeek', 5);

// PARSING

addRegexToken('w',  match1to2);
addRegexToken('ww', match1to2, match2);
addRegexToken('W',  match1to2);
addRegexToken('WW', match1to2, match2);

addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) {
    week[token.substr(0, 1)] = toInt(input);
});

// HELPERS

// LOCALES

function localeWeek (mom) {
    return weekOfYear(mom, this._week.dow, this._week.doy).week;
}

var defaultLocaleWeek = {
    dow : 0, // Sunday is the first day of the week.
    doy : 6  // The week that contains Jan 1st is the first week of the year.
};

function localeFirstDayOfWeek () {
    return this._week.dow;
}

function localeFirstDayOfYear () {
    return this._week.doy;
}

// MOMENTS

function getSetWeek (input) {
    var week = this.localeData().week(this);
    return input == null ? week : this.add((input - week) * 7, 'd');
}

function getSetISOWeek (input) {
    var week = weekOfYear(this, 1, 4).week;
    return input == null ? week : this.add((input - week) * 7, 'd');
}

// FORMATTING

addFormatToken('d', 0, 'do', 'day');

addFormatToken('dd', 0, 0, function (format) {
    return this.localeData().weekdaysMin(this, format);
});

addFormatToken('ddd', 0, 0, function (format) {
    return this.localeData().weekdaysShort(this, format);
});

addFormatToken('dddd', 0, 0, function (format) {
    return this.localeData().weekdays(this, format);
});

addFormatToken('e', 0, 0, 'weekday');
addFormatToken('E', 0, 0, 'isoWeekday');

// ALIASES

addUnitAlias('day', 'd');
addUnitAlias('weekday', 'e');
addUnitAlias('isoWeekday', 'E');

// PRIORITY
addUnitPriority('day', 11);
addUnitPriority('weekday', 11);
addUnitPriority('isoWeekday', 11);

// PARSING

addRegexToken('d',    match1to2);
addRegexToken('e',    match1to2);
addRegexToken('E',    match1to2);
addRegexToken('dd',   function (isStrict, locale) {
    return locale.weekdaysMinRegex(isStrict);
});
addRegexToken('ddd',   function (isStrict, locale) {
    return locale.weekdaysShortRegex(isStrict);
});
addRegexToken('dddd',   function (isStrict, locale) {
    return locale.weekdaysRegex(isStrict);
});

addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) {
    var weekday = config._locale.weekdaysParse(input, token, config._strict);
    // if we didn't get a weekday name, mark the date as invalid
    if (weekday != null) {
        week.d = weekday;
    } else {
        getParsingFlags(config).invalidWeekday = input;
    }
});

addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) {
    week[token] = toInt(input);
});

// HELPERS

function parseWeekday(input, locale) {
    if (typeof input !== 'string') {
        return input;
    }

    if (!isNaN(input)) {
        return parseInt(input, 10);
    }

    input = locale.weekdaysParse(input);
    if (typeof input === 'number') {
        return input;
    }

    return null;
}

function parseIsoWeekday(input, locale) {
    if (typeof input === 'string') {
        return locale.weekdaysParse(input) % 7 || 7;
    }
    return isNaN(input) ? null : input;
}

// LOCALES

var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_');
function localeWeekdays (m, format) {
    if (!m) {
        return isArray(this._weekdays) ? this._weekdays :
            this._weekdays['standalone'];
    }
    return isArray(this._weekdays) ? this._weekdays[m.day()] :
        this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()];
}

var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_');
function localeWeekdaysShort (m) {
    return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort;
}

var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_');
function localeWeekdaysMin (m) {
    return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin;
}

function handleStrictParse$1(weekdayName, format, strict) {
    var i, ii, mom, llc = weekdayName.toLocaleLowerCase();
    if (!this._weekdaysParse) {
        this._weekdaysParse = [];
        this._shortWeekdaysParse = [];
        this._minWeekdaysParse = [];

        for (i = 0; i < 7; ++i) {
            mom = createUTC([2000, 1]).day(i);
            this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase();
            this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase();
            this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase();
        }
    }

    if (strict) {
        if (format === 'dddd') {
            ii = indexOf$1.call(this._weekdaysParse, llc);
            return ii !== -1 ? ii : null;
        } else if (format === 'ddd') {
            ii = indexOf$1.call(this._shortWeekdaysParse, llc);
            return ii !== -1 ? ii : null;
        } else {
            ii = indexOf$1.call(this._minWeekdaysParse, llc);
            return ii !== -1 ? ii : null;
        }
    } else {
        if (format === 'dddd') {
            ii = indexOf$1.call(this._weekdaysParse, llc);
            if (ii !== -1) {
                return ii;
            }
            ii = indexOf$1.call(this._shortWeekdaysParse, llc);
            if (ii !== -1) {
                return ii;
            }
            ii = indexOf$1.call(this._minWeekdaysParse, llc);
            return ii !== -1 ? ii : null;
        } else if (format === 'ddd') {
            ii = indexOf$1.call(this._shortWeekdaysParse, llc);
            if (ii !== -1) {
                return ii;
            }
            ii = indexOf$1.call(this._weekdaysParse, llc);
            if (ii !== -1) {
                return ii;
            }
            ii = indexOf$1.call(this._minWeekdaysParse, llc);
            return ii !== -1 ? ii : null;
        } else {
            ii = indexOf$1.call(this._minWeekdaysParse, llc);
            if (ii !== -1) {
                return ii;
            }
            ii = indexOf$1.call(this._weekdaysParse, llc);
            if (ii !== -1) {
                return ii;
            }
            ii = indexOf$1.call(this._shortWeekdaysParse, llc);
            return ii !== -1 ? ii : null;
        }
    }
}

function localeWeekdaysParse (weekdayName, format, strict) {
    var i, mom, regex;

    if (this._weekdaysParseExact) {
        return handleStrictParse$1.call(this, weekdayName, format, strict);
    }

    if (!this._weekdaysParse) {
        this._weekdaysParse = [];
        this._minWeekdaysParse = [];
        this._shortWeekdaysParse = [];
        this._fullWeekdaysParse = [];
    }

    for (i = 0; i < 7; i++) {
        // make the regex if we don't have it already

        mom = createUTC([2000, 1]).day(i);
        if (strict && !this._fullWeekdaysParse[i]) {
            this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i');
            this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i');
            this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i');
        }
        if (!this._weekdaysParse[i]) {
            regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
            this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
        }
        // test the regex
        if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) {
            return i;
        } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) {
            return i;
        } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) {
            return i;
        } else if (!strict && this._weekdaysParse[i].test(weekdayName)) {
            return i;
        }
    }
}

// MOMENTS

function getSetDayOfWeek (input) {
    if (!this.isValid()) {
        return input != null ? this : NaN;
    }
    var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
    if (input != null) {
        input = parseWeekday(input, this.localeData());
        return this.add(input - day, 'd');
    } else {
        return day;
    }
}

function getSetLocaleDayOfWeek (input) {
    if (!this.isValid()) {
        return input != null ? this : NaN;
    }
    var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
    return input == null ? weekday : this.add(input - weekday, 'd');
}

function getSetISODayOfWeek (input) {
    if (!this.isValid()) {
        return input != null ? this : NaN;
    }

    // behaves the same as moment#day except
    // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
    // as a setter, sunday should belong to the previous week.

    if (input != null) {
        var weekday = parseIsoWeekday(input, this.localeData());
        return this.day(this.day() % 7 ? weekday : weekday - 7);
    } else {
        return this.day() || 7;
    }
}

var defaultWeekdaysRegex = matchWord;
function weekdaysRegex (isStrict) {
    if (this._weekdaysParseExact) {
        if (!hasOwnProp(this, '_weekdaysRegex')) {
            computeWeekdaysParse.call(this);
        }
        if (isStrict) {
            return this._weekdaysStrictRegex;
        } else {
            return this._weekdaysRegex;
        }
    } else {
        if (!hasOwnProp(this, '_weekdaysRegex')) {
            this._weekdaysRegex = defaultWeekdaysRegex;
        }
        return this._weekdaysStrictRegex && isStrict ?
            this._weekdaysStrictRegex : this._weekdaysRegex;
    }
}

var defaultWeekdaysShortRegex = matchWord;
function weekdaysShortRegex (isStrict) {
    if (this._weekdaysParseExact) {
        if (!hasOwnProp(this, '_weekdaysRegex')) {
            computeWeekdaysParse.call(this);
        }
        if (isStrict) {
            return this._weekdaysShortStrictRegex;
        } else {
            return this._weekdaysShortRegex;
        }
    } else {
        if (!hasOwnProp(this, '_weekdaysShortRegex')) {
            this._weekdaysShortRegex = defaultWeekdaysShortRegex;
        }
        return this._weekdaysShortStrictRegex && isStrict ?
            this._weekdaysShortStrictRegex : this._weekdaysShortRegex;
    }
}

var defaultWeekdaysMinRegex = matchWord;
function weekdaysMinRegex (isStrict) {
    if (this._weekdaysParseExact) {
        if (!hasOwnProp(this, '_weekdaysRegex')) {
            computeWeekdaysParse.call(this);
        }
        if (isStrict) {
            return this._weekdaysMinStrictRegex;
        } else {
            return this._weekdaysMinRegex;
        }
    } else {
        if (!hasOwnProp(this, '_weekdaysMinRegex')) {
            this._weekdaysMinRegex = defaultWeekdaysMinRegex;
        }
        return this._weekdaysMinStrictRegex && isStrict ?
            this._weekdaysMinStrictRegex : this._weekdaysMinRegex;
    }
}


function computeWeekdaysParse () {
    function cmpLenRev(a, b) {
        return b.length - a.length;
    }

    var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [],
        i, mom, minp, shortp, longp;
    for (i = 0; i < 7; i++) {
        // make the regex if we don't have it already
        mom = createUTC([2000, 1]).day(i);
        minp = this.weekdaysMin(mom, '');
        shortp = this.weekdaysShort(mom, '');
        longp = this.weekdays(mom, '');
        minPieces.push(minp);
        shortPieces.push(shortp);
        longPieces.push(longp);
        mixedPieces.push(minp);
        mixedPieces.push(shortp);
        mixedPieces.push(longp);
    }
    // Sorting makes sure if one weekday (or abbr) is a prefix of another it
    // will match the longer piece.
    minPieces.sort(cmpLenRev);
    shortPieces.sort(cmpLenRev);
    longPieces.sort(cmpLenRev);
    mixedPieces.sort(cmpLenRev);
    for (i = 0; i < 7; i++) {
        shortPieces[i] = regexEscape(shortPieces[i]);
        longPieces[i] = regexEscape(longPieces[i]);
        mixedPieces[i] = regexEscape(mixedPieces[i]);
    }

    this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
    this._weekdaysShortRegex = this._weekdaysRegex;
    this._weekdaysMinRegex = this._weekdaysRegex;

    this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
    this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
    this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i');
}

// FORMATTING

function hFormat() {
    return this.hours() % 12 || 12;
}

function kFormat() {
    return this.hours() || 24;
}

addFormatToken('H', ['HH', 2], 0, 'hour');
addFormatToken('h', ['hh', 2], 0, hFormat);
addFormatToken('k', ['kk', 2], 0, kFormat);

addFormatToken('hmm', 0, 0, function () {
    return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2);
});

addFormatToken('hmmss', 0, 0, function () {
    return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) +
        zeroFill(this.seconds(), 2);
});

addFormatToken('Hmm', 0, 0, function () {
    return '' + this.hours() + zeroFill(this.minutes(), 2);
});

addFormatToken('Hmmss', 0, 0, function () {
    return '' + this.hours() + zeroFill(this.minutes(), 2) +
        zeroFill(this.seconds(), 2);
});

function meridiem (token, lowercase) {
    addFormatToken(token, 0, 0, function () {
        return this.localeData().meridiem(this.hours(), this.minutes(), lowercase);
    });
}

meridiem('a', true);
meridiem('A', false);

// ALIASES

addUnitAlias('hour', 'h');

// PRIORITY
addUnitPriority('hour', 13);

// PARSING

function matchMeridiem (isStrict, locale) {
    return locale._meridiemParse;
}

addRegexToken('a',  matchMeridiem);
addRegexToken('A',  matchMeridiem);
addRegexToken('H',  match1to2);
addRegexToken('h',  match1to2);
addRegexToken('k',  match1to2);
addRegexToken('HH', match1to2, match2);
addRegexToken('hh', match1to2, match2);
addRegexToken('kk', match1to2, match2);

addRegexToken('hmm', match3to4);
addRegexToken('hmmss', match5to6);
addRegexToken('Hmm', match3to4);
addRegexToken('Hmmss', match5to6);

addParseToken(['H', 'HH'], HOUR);
addParseToken(['k', 'kk'], function (input, array, config) {
    var kInput = toInt(input);
    array[HOUR] = kInput === 24 ? 0 : kInput;
});
addParseToken(['a', 'A'], function (input, array, config) {
    config._isPm = config._locale.isPM(input);
    config._meridiem = input;
});
addParseToken(['h', 'hh'], function (input, array, config) {
    array[HOUR] = toInt(input);
    getParsingFlags(config).bigHour = true;
});
addParseToken('hmm', function (input, array, config) {
    var pos = input.length - 2;
    array[HOUR] = toInt(input.substr(0, pos));
    array[MINUTE] = toInt(input.substr(pos));
    getParsingFlags(config).bigHour = true;
});
addParseToken('hmmss', function (input, array, config) {
    var pos1 = input.length - 4;
    var pos2 = input.length - 2;
    array[HOUR] = toInt(input.substr(0, pos1));
    array[MINUTE] = toInt(input.substr(pos1, 2));
    array[SECOND] = toInt(input.substr(pos2));
    getParsingFlags(config).bigHour = true;
});
addParseToken('Hmm', function (input, array, config) {
    var pos = input.length - 2;
    array[HOUR] = toInt(input.substr(0, pos));
    array[MINUTE] = toInt(input.substr(pos));
});
addParseToken('Hmmss', function (input, array, config) {
    var pos1 = input.length - 4;
    var pos2 = input.length - 2;
    array[HOUR] = toInt(input.substr(0, pos1));
    array[MINUTE] = toInt(input.substr(pos1, 2));
    array[SECOND] = toInt(input.substr(pos2));
});

// LOCALES

function localeIsPM (input) {
    // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
    // Using charAt should be more compatible.
    return ((input + '').toLowerCase().charAt(0) === 'p');
}

var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i;
function localeMeridiem (hours, minutes, isLower) {
    if (hours > 11) {
        return isLower ? 'pm' : 'PM';
    } else {
        return isLower ? 'am' : 'AM';
    }
}


// MOMENTS

// Setting the hour should keep the time, because the user explicitly
// specified which hour he wants. So trying to maintain the same hour (in
// a new timezone) makes sense. Adding/subtracting hours does not follow
// this rule.
var getSetHour = makeGetSet('Hours', true);

// months
// week
// weekdays
// meridiem
var baseConfig = {
    calendar: defaultCalendar,
    longDateFormat: defaultLongDateFormat,
    invalidDate: defaultInvalidDate,
    ordinal: defaultOrdinal,
    dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse,
    relativeTime: defaultRelativeTime,

    months: defaultLocaleMonths,
    monthsShort: defaultLocaleMonthsShort,

    week: defaultLocaleWeek,

    weekdays: defaultLocaleWeekdays,
    weekdaysMin: defaultLocaleWeekdaysMin,
    weekdaysShort: defaultLocaleWeekdaysShort,

    meridiemParse: defaultLocaleMeridiemParse
};

// internal storage for locale config files
var locales = {};
var localeFamilies = {};
var globalLocale;

function normalizeLocale(key) {
    return key ? key.toLowerCase().replace('_', '-') : key;
}

// pick the locale from the array
// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
function chooseLocale(names) {
    var i = 0, j, next, locale, split;

    while (i < names.length) {
        split = normalizeLocale(names[i]).split('-');
        j = split.length;
        next = normalizeLocale(names[i + 1]);
        next = next ? next.split('-') : null;
        while (j > 0) {
            locale = loadLocale(split.slice(0, j).join('-'));
            if (locale) {
                return locale;
            }
            if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
                //the next array item is better than a shallower substring of this one
                break;
            }
            j--;
        }
        i++;
    }
    return null;
}

function loadLocale(name) {
    var oldLocale = null;
    // TODO: Find a better way to register and load all the locales in Node
    if (!locales[name] && (typeof module !== 'undefined') &&
            module && module.exports) {
        try {
            oldLocale = globalLocale._abbr;
            require('./locale/' + name);
            // because defineLocale currently also sets the global locale, we
            // want to undo that for lazy loaded locales
            getSetGlobalLocale(oldLocale);
        } catch (e) { }
    }
    return locales[name];
}

// This function will load locale and then set the global locale.  If
// no arguments are passed in, it will simply return the current global
// locale key.
function getSetGlobalLocale (key, values) {
    var data;
    if (key) {
        if (isUndefined(values)) {
            data = getLocale(key);
        }
        else {
            data = defineLocale(key, values);
        }

        if (data) {
            // moment.duration._locale = moment._locale = data;
            globalLocale = data;
        }
    }

    return globalLocale._abbr;
}

function defineLocale (name, config) {
    if (config !== null) {
        var parentConfig = baseConfig;
        config.abbr = name;
        if (locales[name] != null) {
            deprecateSimple('defineLocaleOverride',
                    'use moment.updateLocale(localeName, config) to change ' +
                    'an existing locale. moment.defineLocale(localeName, ' +
                    'config) should only be used for creating a new locale ' +
                    'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.');
            parentConfig = locales[name]._config;
        } else if (config.parentLocale != null) {
            if (locales[config.parentLocale] != null) {
                parentConfig = locales[config.parentLocale]._config;
            } else {
                if (!localeFamilies[config.parentLocale]) {
                    localeFamilies[config.parentLocale] = [];
                }
                localeFamilies[config.parentLocale].push({
                    name: name,
                    config: config
                });
                return null;
            }
        }
        locales[name] = new Locale(mergeConfigs(parentConfig, config));

        if (localeFamilies[name]) {
            localeFamilies[name].forEach(function (x) {
                defineLocale(x.name, x.config);
            });
        }

        // backwards compat for now: also set the locale
        // make sure we set the locale AFTER all child locales have been
        // created, so we won't end up with the child locale set.
        getSetGlobalLocale(name);


        return locales[name];
    } else {
        // useful for testing
        delete locales[name];
        return null;
    }
}

function updateLocale(name, config) {
    if (config != null) {
        var locale, parentConfig = baseConfig;
        // MERGE
        if (locales[name] != null) {
            parentConfig = locales[name]._config;
        }
        config = mergeConfigs(parentConfig, config);
        locale = new Locale(config);
        locale.parentLocale = locales[name];
        locales[name] = locale;

        // backwards compat for now: also set the locale
        getSetGlobalLocale(name);
    } else {
        // pass null for config to unupdate, useful for tests
        if (locales[name] != null) {
            if (locales[name].parentLocale != null) {
                locales[name] = locales[name].parentLocale;
            } else if (locales[name] != null) {
                delete locales[name];
            }
        }
    }
    return locales[name];
}

// returns locale data
function getLocale (key) {
    var locale;

    if (key && key._locale && key._locale._abbr) {
        key = key._locale._abbr;
    }

    if (!key) {
        return globalLocale;
    }

    if (!isArray(key)) {
        //short-circuit everything else
        locale = loadLocale(key);
        if (locale) {
            return locale;
        }
        key = [key];
    }

    return chooseLocale(key);
}

function listLocales() {
    return keys$1(locales);
}

function checkOverflow (m) {
    var overflow;
    var a = m._a;

    if (a && getParsingFlags(m).overflow === -2) {
        overflow =
            a[MONTH]       < 0 || a[MONTH]       > 11  ? MONTH :
            a[DATE]        < 1 || a[DATE]        > daysInMonth(a[YEAR], a[MONTH]) ? DATE :
            a[HOUR]        < 0 || a[HOUR]        > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR :
            a[MINUTE]      < 0 || a[MINUTE]      > 59  ? MINUTE :
            a[SECOND]      < 0 || a[SECOND]      > 59  ? SECOND :
            a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND :
            -1;

        if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
            overflow = DATE;
        }
        if (getParsingFlags(m)._overflowWeeks && overflow === -1) {
            overflow = WEEK;
        }
        if (getParsingFlags(m)._overflowWeekday && overflow === -1) {
            overflow = WEEKDAY;
        }

        getParsingFlags(m).overflow = overflow;
    }

    return m;
}

// iso 8601 regex
// 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)
var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/;
var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/;

var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/;

var isoDates = [
    ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/],
    ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/],
    ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/],
    ['GGGG-[W]WW', /\d{4}-W\d\d/, false],
    ['YYYY-DDD', /\d{4}-\d{3}/],
    ['YYYY-MM', /\d{4}-\d\d/, false],
    ['YYYYYYMMDD', /[+-]\d{10}/],
    ['YYYYMMDD', /\d{8}/],
    // YYYYMM is NOT allowed by the standard
    ['GGGG[W]WWE', /\d{4}W\d{3}/],
    ['GGGG[W]WW', /\d{4}W\d{2}/, false],
    ['YYYYDDD', /\d{7}/]
];

// iso time formats and regexes
var isoTimes = [
    ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/],
    ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/],
    ['HH:mm:ss', /\d\d:\d\d:\d\d/],
    ['HH:mm', /\d\d:\d\d/],
    ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/],
    ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/],
    ['HHmmss', /\d\d\d\d\d\d/],
    ['HHmm', /\d\d\d\d/],
    ['HH', /\d\d/]
];

var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i;

// date from iso format
function configFromISO(config) {
    var i, l,
        string = config._i,
        match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string),
        allowTime, dateFormat, timeFormat, tzFormat;

    if (match) {
        getParsingFlags(config).iso = true;

        for (i = 0, l = isoDates.length; i < l; i++) {
            if (isoDates[i][1].exec(match[1])) {
                dateFormat = isoDates[i][0];
                allowTime = isoDates[i][2] !== false;
                break;
            }
        }
        if (dateFormat == null) {
            config._isValid = false;
            return;
        }
        if (match[3]) {
            for (i = 0, l = isoTimes.length; i < l; i++) {
                if (isoTimes[i][1].exec(match[3])) {
                    // match[2] should be 'T' or space
                    timeFormat = (match[2] || ' ') + isoTimes[i][0];
                    break;
                }
            }
            if (timeFormat == null) {
                config._isValid = false;
                return;
            }
        }
        if (!allowTime && timeFormat != null) {
            config._isValid = false;
            return;
        }
        if (match[4]) {
            if (tzRegex.exec(match[4])) {
                tzFormat = 'Z';
            } else {
                config._isValid = false;
                return;
            }
        }
        config._f = dateFormat + (timeFormat || '') + (tzFormat || '');
        configFromStringAndFormat(config);
    } else {
        config._isValid = false;
    }
}

// RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3
var basicRfcRegex = /^((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d?\d\s(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(?:\d\d)?\d\d\s)(\d\d:\d\d)(\:\d\d)?(\s(?:UT|GMT|[ECMP][SD]T|[A-IK-Za-ik-z]|[+-]\d{4}))$/;

// date and time from ref 2822 format
function configFromRFC2822(config) {
    var string, match, dayFormat,
        dateFormat, timeFormat, tzFormat;
    var timezones = {
        ' GMT': ' +0000',
        ' EDT': ' -0400',
        ' EST': ' -0500',
        ' CDT': ' -0500',
        ' CST': ' -0600',
        ' MDT': ' -0600',
        ' MST': ' -0700',
        ' PDT': ' -0700',
        ' PST': ' -0800'
    };
    var military = 'YXWVUTSRQPONZABCDEFGHIKLM';
    var timezone, timezoneIndex;

    string = config._i
        .replace(/\([^\)]*\)|[\n\t]/g, ' ') // Remove comments and folding whitespace
        .replace(/(\s\s+)/g, ' ') // Replace multiple-spaces with a single space
        .replace(/^\s|\s$/g, ''); // Remove leading and trailing spaces
    match = basicRfcRegex.exec(string);

    if (match) {
        dayFormat = match[1] ? 'ddd' + ((match[1].length === 5) ? ', ' : ' ') : '';
        dateFormat = 'D MMM ' + ((match[2].length > 10) ? 'YYYY ' : 'YY ');
        timeFormat = 'HH:mm' + (match[4] ? ':ss' : '');

        // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check.
        if (match[1]) { // day of week given
            var momentDate = new Date(match[2]);
            var momentDay = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][momentDate.getDay()];

            if (match[1].substr(0,3) !== momentDay) {
                getParsingFlags(config).weekdayMismatch = true;
                config._isValid = false;
                return;
            }
        }

        switch (match[5].length) {
            case 2: // military
                if (timezoneIndex === 0) {
                    timezone = ' +0000';
                } else {
                    timezoneIndex = military.indexOf(match[5][1].toUpperCase()) - 12;
                    timezone = ((timezoneIndex < 0) ? ' -' : ' +') +
                        (('' + timezoneIndex).replace(/^-?/, '0')).match(/..$/)[0] + '00';
                }
                break;
            case 4: // Zone
                timezone = timezones[match[5]];
                break;
            default: // UT or +/-9999
                timezone = timezones[' GMT'];
        }
        match[5] = timezone;
        config._i = match.splice(1).join('');
        tzFormat = ' ZZ';
        config._f = dayFormat + dateFormat + timeFormat + tzFormat;
        configFromStringAndFormat(config);
        getParsingFlags(config).rfc2822 = true;
    } else {
        config._isValid = false;
    }
}

// date from iso format or fallback
function configFromString(config) {
    var matched = aspNetJsonRegex.exec(config._i);

    if (matched !== null) {
        config._d = new Date(+matched[1]);
        return;
    }

    configFromISO(config);
    if (config._isValid === false) {
        delete config._isValid;
    } else {
        return;
    }

    configFromRFC2822(config);
    if (config._isValid === false) {
        delete config._isValid;
    } else {
        return;
    }

    // Final attempt, use Input Fallback
    hooks.createFromInputFallback(config);
}

hooks.createFromInputFallback = deprecate(
    'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' +
    'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' +
    'discouraged and will be removed in an upcoming major release. Please refer to ' +
    'http://momentjs.com/guides/#/warnings/js-date/ for more info.',
    function (config) {
        config._d = new Date(config._i + (config._useUTC ? ' UTC' : ''));
    }
);

// Pick the first defined of two or three arguments.
function defaults(a, b, c) {
    if (a != null) {
        return a;
    }
    if (b != null) {
        return b;
    }
    return c;
}

function currentDateArray(config) {
    // hooks is actually the exported moment object
    var nowValue = new Date(hooks.now());
    if (config._useUTC) {
        return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()];
    }
    return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()];
}

// convert an array to a date.
// the array should mirror the parameters below
// note: all values past the year are optional and will default to the lowest possible value.
// [year, month, day , hour, minute, second, millisecond]
function configFromArray (config) {
    var i, date, input = [], currentDate, yearToUse;

    if (config._d) {
        return;
    }

    currentDate = currentDateArray(config);

    //compute day of the year from weeks and weekdays
    if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
        dayOfYearFromWeekInfo(config);
    }

    //if the day of the year is set, figure out what it is
    if (config._dayOfYear != null) {
        yearToUse = defaults(config._a[YEAR], currentDate[YEAR]);

        if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) {
            getParsingFlags(config)._overflowDayOfYear = true;
        }

        date = createUTCDate(yearToUse, 0, config._dayOfYear);
        config._a[MONTH] = date.getUTCMonth();
        config._a[DATE] = date.getUTCDate();
    }

    // Default to current date.
    // * if no year, month, day of month are given, default to today
    // * if day of month is given, default month and year
    // * if month is given, default only year
    // * if year is given, don't default anything
    for (i = 0; i < 3 && config._a[i] == null; ++i) {
        config._a[i] = input[i] = currentDate[i];
    }

    // Zero out whatever was not defaulted, including time
    for (; i < 7; i++) {
        config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
    }

    // Check for 24:00:00.000
    if (config._a[HOUR] === 24 &&
            config._a[MINUTE] === 0 &&
            config._a[SECOND] === 0 &&
            config._a[MILLISECOND] === 0) {
        config._nextDay = true;
        config._a[HOUR] = 0;
    }

    config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input);
    // Apply timezone offset from input. The actual utcOffset can be changed
    // with parseZone.
    if (config._tzm != null) {
        config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
    }

    if (config._nextDay) {
        config._a[HOUR] = 24;
    }
}

function dayOfYearFromWeekInfo(config) {
    var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow;

    w = config._w;
    if (w.GG != null || w.W != null || w.E != null) {
        dow = 1;
        doy = 4;

        // TODO: We need to take the current isoWeekYear, but that depends on
        // how we interpret now (local, utc, fixed offset). So create
        // a now version of current config (take local/utc/offset flags, and
        // create now).
        weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year);
        week = defaults(w.W, 1);
        weekday = defaults(w.E, 1);
        if (weekday < 1 || weekday > 7) {
            weekdayOverflow = true;
        }
    } else {
        dow = config._locale._week.dow;
        doy = config._locale._week.doy;

        var curWeek = weekOfYear(createLocal(), dow, doy);

        weekYear = defaults(w.gg, config._a[YEAR], curWeek.year);

        // Default to current week.
        week = defaults(w.w, curWeek.week);

        if (w.d != null) {
            // weekday -- low day numbers are considered next week
            weekday = w.d;
            if (weekday < 0 || weekday > 6) {
                weekdayOverflow = true;
            }
        } else if (w.e != null) {
            // local weekday -- counting starts from begining of week
            weekday = w.e + dow;
            if (w.e < 0 || w.e > 6) {
                weekdayOverflow = true;
            }
        } else {
            // default to begining of week
            weekday = dow;
        }
    }
    if (week < 1 || week > weeksInYear(weekYear, dow, doy)) {
        getParsingFlags(config)._overflowWeeks = true;
    } else if (weekdayOverflow != null) {
        getParsingFlags(config)._overflowWeekday = true;
    } else {
        temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy);
        config._a[YEAR] = temp.year;
        config._dayOfYear = temp.dayOfYear;
    }
}

// constant that refers to the ISO standard
hooks.ISO_8601 = function () {};

// constant that refers to the RFC 2822 form
hooks.RFC_2822 = function () {};

// date from string and format string
function configFromStringAndFormat(config) {
    // TODO: Move this to another part of the creation flow to prevent circular deps
    if (config._f === hooks.ISO_8601) {
        configFromISO(config);
        return;
    }
    if (config._f === hooks.RFC_2822) {
        configFromRFC2822(config);
        return;
    }
    config._a = [];
    getParsingFlags(config).empty = true;

    // This array is used to make a Date, either with `new Date` or `Date.UTC`
    var string = '' + config._i,
        i, parsedInput, tokens, token, skipped,
        stringLength = string.length,
        totalParsedInputLength = 0;

    tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];

    for (i = 0; i < tokens.length; i++) {
        token = tokens[i];
        parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];
        // console.log('token', token, 'parsedInput', parsedInput,
        //         'regex', getParseRegexForToken(token, config));
        if (parsedInput) {
            skipped = string.substr(0, string.indexOf(parsedInput));
            if (skipped.length > 0) {
                getParsingFlags(config).unusedInput.push(skipped);
            }
            string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
            totalParsedInputLength += parsedInput.length;
        }
        // don't parse if it's not a known token
        if (formatTokenFunctions[token]) {
            if (parsedInput) {
                getParsingFlags(config).empty = false;
            }
            else {
                getParsingFlags(config).unusedTokens.push(token);
            }
            addTimeToArrayFromToken(token, parsedInput, config);
        }
        else if (config._strict && !parsedInput) {
            getParsingFlags(config).unusedTokens.push(token);
        }
    }

    // add remaining unparsed input length to the string
    getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength;
    if (string.length > 0) {
        getParsingFlags(config).unusedInput.push(string);
    }

    // clear _12h flag if hour is <= 12
    if (config._a[HOUR] <= 12 &&
        getParsingFlags(config).bigHour === true &&
        config._a[HOUR] > 0) {
        getParsingFlags(config).bigHour = undefined;
    }

    getParsingFlags(config).parsedDateParts = config._a.slice(0);
    getParsingFlags(config).meridiem = config._meridiem;
    // handle meridiem
    config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem);

    configFromArray(config);
    checkOverflow(config);
}


function meridiemFixWrap (locale, hour, meridiem) {
    var isPm;

    if (meridiem == null) {
        // nothing to do
        return hour;
    }
    if (locale.meridiemHour != null) {
        return locale.meridiemHour(hour, meridiem);
    } else if (locale.isPM != null) {
        // Fallback
        isPm = locale.isPM(meridiem);
        if (isPm && hour < 12) {
            hour += 12;
        }
        if (!isPm && hour === 12) {
            hour = 0;
        }
        return hour;
    } else {
        // this is not supposed to happen
        return hour;
    }
}

// date from string and array of format strings
function configFromStringAndArray(config) {
    var tempConfig,
        bestMoment,

        scoreToBeat,
        i,
        currentScore;

    if (config._f.length === 0) {
        getParsingFlags(config).invalidFormat = true;
        config._d = new Date(NaN);
        return;
    }

    for (i = 0; i < config._f.length; i++) {
        currentScore = 0;
        tempConfig = copyConfig({}, config);
        if (config._useUTC != null) {
            tempConfig._useUTC = config._useUTC;
        }
        tempConfig._f = config._f[i];
        configFromStringAndFormat(tempConfig);

        if (!isValid(tempConfig)) {
            continue;
        }

        // if there is any input that was not parsed add a penalty for that format
        currentScore += getParsingFlags(tempConfig).charsLeftOver;

        //or tokens
        currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10;

        getParsingFlags(tempConfig).score = currentScore;

        if (scoreToBeat == null || currentScore < scoreToBeat) {
            scoreToBeat = currentScore;
            bestMoment = tempConfig;
        }
    }

    extend(config, bestMoment || tempConfig);
}

function configFromObject(config) {
    if (config._d) {
        return;
    }

    var i = normalizeObjectUnits(config._i);
    config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) {
        return obj && parseInt(obj, 10);
    });

    configFromArray(config);
}

function createFromConfig (config) {
    var res = new Moment(checkOverflow(prepareConfig(config)));
    if (res._nextDay) {
        // Adding is smart enough around DST
        res.add(1, 'd');
        res._nextDay = undefined;
    }

    return res;
}

function prepareConfig (config) {
    var input = config._i,
        format = config._f;

    config._locale = config._locale || getLocale(config._l);

    if (input === null || (format === undefined && input === '')) {
        return createInvalid({nullInput: true});
    }

    if (typeof input === 'string') {
        config._i = input = config._locale.preparse(input);
    }

    if (isMoment(input)) {
        return new Moment(checkOverflow(input));
    } else if (isDate(input)) {
        config._d = input;
    } else if (isArray(format)) {
        configFromStringAndArray(config);
    } else if (format) {
        configFromStringAndFormat(config);
    }  else {
        configFromInput(config);
    }

    if (!isValid(config)) {
        config._d = null;
    }

    return config;
}

function configFromInput(config) {
    var input = config._i;
    if (isUndefined(input)) {
        config._d = new Date(hooks.now());
    } else if (isDate(input)) {
        config._d = new Date(input.valueOf());
    } else if (typeof input === 'string') {
        configFromString(config);
    } else if (isArray(input)) {
        config._a = map(input.slice(0), function (obj) {
            return parseInt(obj, 10);
        });
        configFromArray(config);
    } else if (isObject(input)) {
        configFromObject(config);
    } else if (isNumber(input)) {
        // from milliseconds
        config._d = new Date(input);
    } else {
        hooks.createFromInputFallback(config);
    }
}

function createLocalOrUTC (input, format, locale, strict, isUTC) {
    var c = {};

    if (locale === true || locale === false) {
        strict = locale;
        locale = undefined;
    }

    if ((isObject(input) && isObjectEmpty(input)) ||
            (isArray(input) && input.length === 0)) {
        input = undefined;
    }
    // object construction must be done this way.
    // https://github.com/moment/moment/issues/1423
    c._isAMomentObject = true;
    c._useUTC = c._isUTC = isUTC;
    c._l = locale;
    c._i = input;
    c._f = format;
    c._strict = strict;

    return createFromConfig(c);
}

function createLocal (input, format, locale, strict) {
    return createLocalOrUTC(input, format, locale, strict, false);
}

var prototypeMin = deprecate(
    'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/',
    function () {
        var other = createLocal.apply(null, arguments);
        if (this.isValid() && other.isValid()) {
            return other < this ? this : other;
        } else {
            return createInvalid();
        }
    }
);

var prototypeMax = deprecate(
    'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/',
    function () {
        var other = createLocal.apply(null, arguments);
        if (this.isValid() && other.isValid()) {
            return other > this ? this : other;
        } else {
            return createInvalid();
        }
    }
);

// Pick a moment m from moments so that m[fn](other) is true for all
// other. This relies on the function fn to be transitive.
//
// moments should either be an array of moment objects or an array, whose
// first element is an array of moment objects.
function pickBy(fn, moments) {
    var res, i;
    if (moments.length === 1 && isArray(moments[0])) {
        moments = moments[0];
    }
    if (!moments.length) {
        return createLocal();
    }
    res = moments[0];
    for (i = 1; i < moments.length; ++i) {
        if (!moments[i].isValid() || moments[i][fn](res)) {
            res = moments[i];
        }
    }
    return res;
}

// TODO: Use [].sort instead?
function min () {
    var args = [].slice.call(arguments, 0);

    return pickBy('isBefore', args);
}

function max () {
    var args = [].slice.call(arguments, 0);

    return pickBy('isAfter', args);
}

var now = function () {
    return Date.now ? Date.now() : +(new Date());
};

var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond'];

function isDurationValid(m) {
    for (var key in m) {
        if (!(ordering.indexOf(key) !== -1 && (m[key] == null || !isNaN(m[key])))) {
            return false;
        }
    }

    var unitHasDecimal = false;
    for (var i = 0; i < ordering.length; ++i) {
        if (m[ordering[i]]) {
            if (unitHasDecimal) {
                return false; // only allow non-integers for smallest unit
            }
            if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) {
                unitHasDecimal = true;
            }
        }
    }

    return true;
}

function isValid$1() {
    return this._isValid;
}

function createInvalid$1() {
    return createDuration(NaN);
}

function Duration (duration) {
    var normalizedInput = normalizeObjectUnits(duration),
        years = normalizedInput.year || 0,
        quarters = normalizedInput.quarter || 0,
        months = normalizedInput.month || 0,
        weeks = normalizedInput.week || 0,
        days = normalizedInput.day || 0,
        hours = normalizedInput.hour || 0,
        minutes = normalizedInput.minute || 0,
        seconds = normalizedInput.second || 0,
        milliseconds = normalizedInput.millisecond || 0;

    this._isValid = isDurationValid(normalizedInput);

    // representation for dateAddRemove
    this._milliseconds = +milliseconds +
        seconds * 1e3 + // 1000
        minutes * 6e4 + // 1000 * 60
        hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978
    // Because of dateAddRemove treats 24 hours as different from a
    // day when working around DST, we need to store them separately
    this._days = +days +
        weeks * 7;
    // It is impossible translate months into days without knowing
    // which months you are are talking about, so we have to store
    // it separately.
    this._months = +months +
        quarters * 3 +
        years * 12;

    this._data = {};

    this._locale = getLocale();

    this._bubble();
}

function isDuration (obj) {
    return obj instanceof Duration;
}

function absRound (number) {
    if (number < 0) {
        return Math.round(-1 * number) * -1;
    } else {
        return Math.round(number);
    }
}

// FORMATTING

function offset (token, separator) {
    addFormatToken(token, 0, 0, function () {
        var offset = this.utcOffset();
        var sign = '+';
        if (offset < 0) {
            offset = -offset;
            sign = '-';
        }
        return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2);
    });
}

offset('Z', ':');
offset('ZZ', '');

// PARSING

addRegexToken('Z',  matchShortOffset);
addRegexToken('ZZ', matchShortOffset);
addParseToken(['Z', 'ZZ'], function (input, array, config) {
    config._useUTC = true;
    config._tzm = offsetFromString(matchShortOffset, input);
});

// HELPERS

// timezone chunker
// '+10:00' > ['10',  '00']
// '-1530'  > ['-15', '30']
var chunkOffset = /([\+\-]|\d\d)/gi;

function offsetFromString(matcher, string) {
    var matches = (string || '').match(matcher);

    if (matches === null) {
        return null;
    }

    var chunk   = matches[matches.length - 1] || [];
    var parts   = (chunk + '').match(chunkOffset) || ['-', 0, 0];
    var minutes = +(parts[1] * 60) + toInt(parts[2]);

    return minutes === 0 ?
      0 :
      parts[0] === '+' ? minutes : -minutes;
}

// Return a moment from input, that is local/utc/zone equivalent to model.
function cloneWithOffset(input, model) {
    var res, diff;
    if (model._isUTC) {
        res = model.clone();
        diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf();
        // Use low-level api, because this fn is low-level api.
        res._d.setTime(res._d.valueOf() + diff);
        hooks.updateOffset(res, false);
        return res;
    } else {
        return createLocal(input).local();
    }
}

function getDateOffset (m) {
    // On Firefox.24 Date#getTimezoneOffset returns a floating point.
    // https://github.com/moment/moment/pull/1871
    return -Math.round(m._d.getTimezoneOffset() / 15) * 15;
}

// HOOKS

// This function will be called whenever a moment is mutated.
// It is intended to keep the offset in sync with the timezone.
hooks.updateOffset = function () {};

// MOMENTS

// keepLocalTime = true means only change the timezone, without
// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
// +0200, so we adjust the time as needed, to be valid.
//
// Keeping the time actually adds/subtracts (one hour)
// from the actual represented time. That is why we call updateOffset
// a second time. In case it wants us to change the offset again
// _changeInProgress == true case, then we have to adjust, because
// there is no such time in the given timezone.
function getSetOffset (input, keepLocalTime, keepMinutes) {
    var offset = this._offset || 0,
        localAdjust;
    if (!this.isValid()) {
        return input != null ? this : NaN;
    }
    if (input != null) {
        if (typeof input === 'string') {
            input = offsetFromString(matchShortOffset, input);
            if (input === null) {
                return this;
            }
        } else if (Math.abs(input) < 16 && !keepMinutes) {
            input = input * 60;
        }
        if (!this._isUTC && keepLocalTime) {
            localAdjust = getDateOffset(this);
        }
        this._offset = input;
        this._isUTC = true;
        if (localAdjust != null) {
            this.add(localAdjust, 'm');
        }
        if (offset !== input) {
            if (!keepLocalTime || this._changeInProgress) {
                addSubtract(this, createDuration(input - offset, 'm'), 1, false);
            } else if (!this._changeInProgress) {
                this._changeInProgress = true;
                hooks.updateOffset(this, true);
                this._changeInProgress = null;
            }
        }
        return this;
    } else {
        return this._isUTC ? offset : getDateOffset(this);
    }
}

function getSetZone (input, keepLocalTime) {
    if (input != null) {
        if (typeof input !== 'string') {
            input = -input;
        }

        this.utcOffset(input, keepLocalTime);

        return this;
    } else {
        return -this.utcOffset();
    }
}

function setOffsetToUTC (keepLocalTime) {
    return this.utcOffset(0, keepLocalTime);
}

function setOffsetToLocal (keepLocalTime) {
    if (this._isUTC) {
        this.utcOffset(0, keepLocalTime);
        this._isUTC = false;

        if (keepLocalTime) {
            this.subtract(getDateOffset(this), 'm');
        }
    }
    return this;
}

function setOffsetToParsedOffset () {
    if (this._tzm != null) {
        this.utcOffset(this._tzm, false, true);
    } else if (typeof this._i === 'string') {
        var tZone = offsetFromString(matchOffset, this._i);
        if (tZone != null) {
            this.utcOffset(tZone);
        }
        else {
            this.utcOffset(0, true);
        }
    }
    return this;
}

function hasAlignedHourOffset (input) {
    if (!this.isValid()) {
        return false;
    }
    input = input ? createLocal(input).utcOffset() : 0;

    return (this.utcOffset() - input) % 60 === 0;
}

function isDaylightSavingTime () {
    return (
        this.utcOffset() > this.clone().month(0).utcOffset() ||
        this.utcOffset() > this.clone().month(5).utcOffset()
    );
}

function isDaylightSavingTimeShifted () {
    if (!isUndefined(this._isDSTShifted)) {
        return this._isDSTShifted;
    }

    var c = {};

    copyConfig(c, this);
    c = prepareConfig(c);

    if (c._a) {
        var other = c._isUTC ? createUTC(c._a) : createLocal(c._a);
        this._isDSTShifted = this.isValid() &&
            compareArrays(c._a, other.toArray()) > 0;
    } else {
        this._isDSTShifted = false;
    }

    return this._isDSTShifted;
}

function isLocal () {
    return this.isValid() ? !this._isUTC : false;
}

function isUtcOffset () {
    return this.isValid() ? this._isUTC : false;
}

function isUtc () {
    return this.isValid() ? this._isUTC && this._offset === 0 : false;
}

// ASP.NET json date format regex
var aspNetRegex = /^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/;

// from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
// somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
// and further modified to allow for strings containing both week and day
var isoRegex = /^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;

function createDuration (input, key) {
    var duration = input,
        // matching against regexp is expensive, do it on demand
        match = null,
        sign,
        ret,
        diffRes;

    if (isDuration(input)) {
        duration = {
            ms : input._milliseconds,
            d  : input._days,
            M  : input._months
        };
    } else if (isNumber(input)) {
        duration = {};
        if (key) {
            duration[key] = input;
        } else {
            duration.milliseconds = input;
        }
    } else if (!!(match = aspNetRegex.exec(input))) {
        sign = (match[1] === '-') ? -1 : 1;
        duration = {
            y  : 0,
            d  : toInt(match[DATE])                         * sign,
            h  : toInt(match[HOUR])                         * sign,
            m  : toInt(match[MINUTE])                       * sign,
            s  : toInt(match[SECOND])                       * sign,
            ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match
        };
    } else if (!!(match = isoRegex.exec(input))) {
        sign = (match[1] === '-') ? -1 : 1;
        duration = {
            y : parseIso(match[2], sign),
            M : parseIso(match[3], sign),
            w : parseIso(match[4], sign),
            d : parseIso(match[5], sign),
            h : parseIso(match[6], sign),
            m : parseIso(match[7], sign),
            s : parseIso(match[8], sign)
        };
    } else if (duration == null) {// checks for null or undefined
        duration = {};
    } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) {
        diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to));

        duration = {};
        duration.ms = diffRes.milliseconds;
        duration.M = diffRes.months;
    }

    ret = new Duration(duration);

    if (isDuration(input) && hasOwnProp(input, '_locale')) {
        ret._locale = input._locale;
    }

    return ret;
}

createDuration.fn = Duration.prototype;
createDuration.invalid = createInvalid$1;

function parseIso (inp, sign) {
    // We'd normally use ~~inp for this, but unfortunately it also
    // converts floats to ints.
    // inp may be undefined, so careful calling replace on it.
    var res = inp && parseFloat(inp.replace(',', '.'));
    // apply sign while we're at it
    return (isNaN(res) ? 0 : res) * sign;
}

function positiveMomentsDifference(base, other) {
    var res = {milliseconds: 0, months: 0};

    res.months = other.month() - base.month() +
        (other.year() - base.year()) * 12;
    if (base.clone().add(res.months, 'M').isAfter(other)) {
        --res.months;
    }

    res.milliseconds = +other - +(base.clone().add(res.months, 'M'));

    return res;
}

function momentsDifference(base, other) {
    var res;
    if (!(base.isValid() && other.isValid())) {
        return {milliseconds: 0, months: 0};
    }

    other = cloneWithOffset(other, base);
    if (base.isBefore(other)) {
        res = positiveMomentsDifference(base, other);
    } else {
        res = positiveMomentsDifference(other, base);
        res.milliseconds = -res.milliseconds;
        res.months = -res.months;
    }

    return res;
}

// TODO: remove 'name' arg after deprecation is removed
function createAdder(direction, name) {
    return function (val, period) {
        var dur, tmp;
        //invert the arguments, but complain about it
        if (period !== null && !isNaN(+period)) {
            deprecateSimple(name, 'moment().' + name  + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' +
            'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.');
            tmp = val; val = period; period = tmp;
        }

        val = typeof val === 'string' ? +val : val;
        dur = createDuration(val, period);
        addSubtract(this, dur, direction);
        return this;
    };
}

function addSubtract (mom, duration, isAdding, updateOffset) {
    var milliseconds = duration._milliseconds,
        days = absRound(duration._days),
        months = absRound(duration._months);

    if (!mom.isValid()) {
        // No op
        return;
    }

    updateOffset = updateOffset == null ? true : updateOffset;

    if (milliseconds) {
        mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding);
    }
    if (days) {
        set$1(mom, 'Date', get(mom, 'Date') + days * isAdding);
    }
    if (months) {
        setMonth(mom, get(mom, 'Month') + months * isAdding);
    }
    if (updateOffset) {
        hooks.updateOffset(mom, days || months);
    }
}

var add      = createAdder(1, 'add');
var subtract = createAdder(-1, 'subtract');

function getCalendarFormat(myMoment, now) {
    var diff = myMoment.diff(now, 'days', true);
    return diff < -6 ? 'sameElse' :
            diff < -1 ? 'lastWeek' :
            diff < 0 ? 'lastDay' :
            diff < 1 ? 'sameDay' :
            diff < 2 ? 'nextDay' :
            diff < 7 ? 'nextWeek' : 'sameElse';
}

function calendar$1 (time, formats) {
    // We want to compare the start of today, vs this.
    // Getting start-of-today depends on whether we're local/utc/offset or not.
    var now = time || createLocal(),
        sod = cloneWithOffset(now, this).startOf('day'),
        format = hooks.calendarFormat(this, sod) || 'sameElse';

    var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]);

    return this.format(output || this.localeData().calendar(format, this, createLocal(now)));
}

function clone () {
    return new Moment(this);
}

function isAfter (input, units) {
    var localInput = isMoment(input) ? input : createLocal(input);
    if (!(this.isValid() && localInput.isValid())) {
        return false;
    }
    units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
    if (units === 'millisecond') {
        return this.valueOf() > localInput.valueOf();
    } else {
        return localInput.valueOf() < this.clone().startOf(units).valueOf();
    }
}

function isBefore (input, units) {
    var localInput = isMoment(input) ? input : createLocal(input);
    if (!(this.isValid() && localInput.isValid())) {
        return false;
    }
    units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
    if (units === 'millisecond') {
        return this.valueOf() < localInput.valueOf();
    } else {
        return this.clone().endOf(units).valueOf() < localInput.valueOf();
    }
}

function isBetween (from, to, units, inclusivity) {
    inclusivity = inclusivity || '()';
    return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) &&
        (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units));
}

function isSame (input, units) {
    var localInput = isMoment(input) ? input : createLocal(input),
        inputMs;
    if (!(this.isValid() && localInput.isValid())) {
        return false;
    }
    units = normalizeUnits(units || 'millisecond');
    if (units === 'millisecond') {
        return this.valueOf() === localInput.valueOf();
    } else {
        inputMs = localInput.valueOf();
        return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf();
    }
}

function isSameOrAfter (input, units) {
    return this.isSame(input, units) || this.isAfter(input,units);
}

function isSameOrBefore (input, units) {
    return this.isSame(input, units) || this.isBefore(input,units);
}

function diff (input, units, asFloat) {
    var that,
        zoneDelta,
        delta, output;

    if (!this.isValid()) {
        return NaN;
    }

    that = cloneWithOffset(input, this);

    if (!that.isValid()) {
        return NaN;
    }

    zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4;

    units = normalizeUnits(units);

    if (units === 'year' || units === 'month' || units === 'quarter') {
        output = monthDiff(this, that);
        if (units === 'quarter') {
            output = output / 3;
        } else if (units === 'year') {
            output = output / 12;
        }
    } else {
        delta = this - that;
        output = units === 'second' ? delta / 1e3 : // 1000
            units === 'minute' ? delta / 6e4 : // 1000 * 60
            units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60
            units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst
            units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst
            delta;
    }
    return asFloat ? output : absFloor(output);
}

function monthDiff (a, b) {
    // difference in months
    var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()),
        // b is in (anchor - 1 month, anchor + 1 month)
        anchor = a.clone().add(wholeMonthDiff, 'months'),
        anchor2, adjust;

    if (b - anchor < 0) {
        anchor2 = a.clone().add(wholeMonthDiff - 1, 'months');
        // linear across the month
        adjust = (b - anchor) / (anchor - anchor2);
    } else {
        anchor2 = a.clone().add(wholeMonthDiff + 1, 'months');
        // linear across the month
        adjust = (b - anchor) / (anchor2 - anchor);
    }

    //check for negative zero, return zero if negative zero
    return -(wholeMonthDiff + adjust) || 0;
}

hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ';
hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]';

function toString () {
    return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
}

function toISOString() {
    if (!this.isValid()) {
        return null;
    }
    var m = this.clone().utc();
    if (m.year() < 0 || m.year() > 9999) {
        return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
    }
    if (isFunction(Date.prototype.toISOString)) {
        // native implementation is ~50x faster, use it when we can
        return this.toDate().toISOString();
    }
    return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
}

/**
 * Return a human readable representation of a moment that can
 * also be evaluated to get a new moment which is the same
 *
 * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects
 */
function inspect () {
    if (!this.isValid()) {
        return 'moment.invalid(/* ' + this._i + ' */)';
    }
    var func = 'moment';
    var zone = '';
    if (!this.isLocal()) {
        func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone';
        zone = 'Z';
    }
    var prefix = '[' + func + '("]';
    var year = (0 <= this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY';
    var datetime = '-MM-DD[T]HH:mm:ss.SSS';
    var suffix = zone + '[")]';

    return this.format(prefix + year + datetime + suffix);
}

function format (inputString) {
    if (!inputString) {
        inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat;
    }
    var output = formatMoment(this, inputString);
    return this.localeData().postformat(output);
}

function from (time, withoutSuffix) {
    if (this.isValid() &&
            ((isMoment(time) && time.isValid()) ||
             createLocal(time).isValid())) {
        return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix);
    } else {
        return this.localeData().invalidDate();
    }
}

function fromNow (withoutSuffix) {
    return this.from(createLocal(), withoutSuffix);
}

function to (time, withoutSuffix) {
    if (this.isValid() &&
            ((isMoment(time) && time.isValid()) ||
             createLocal(time).isValid())) {
        return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix);
    } else {
        return this.localeData().invalidDate();
    }
}

function toNow (withoutSuffix) {
    return this.to(createLocal(), withoutSuffix);
}

// If passed a locale key, it will set the locale for this
// instance.  Otherwise, it will return the locale configuration
// variables for this instance.
function locale (key) {
    var newLocaleData;

    if (key === undefined) {
        return this._locale._abbr;
    } else {
        newLocaleData = getLocale(key);
        if (newLocaleData != null) {
            this._locale = newLocaleData;
        }
        return this;
    }
}

var lang = deprecate(
    'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.',
    function (key) {
        if (key === undefined) {
            return this.localeData();
        } else {
            return this.locale(key);
        }
    }
);

function localeData () {
    return this._locale;
}

function startOf (units) {
    units = normalizeUnits(units);
    // the following switch intentionally omits break keywords
    // to utilize falling through the cases.
    switch (units) {
        case 'year':
            this.month(0);
            /* falls through */
        case 'quarter':
        case 'month':
            this.date(1);
            /* falls through */
        case 'week':
        case 'isoWeek':
        case 'day':
        case 'date':
            this.hours(0);
            /* falls through */
        case 'hour':
            this.minutes(0);
            /* falls through */
        case 'minute':
            this.seconds(0);
            /* falls through */
        case 'second':
            this.milliseconds(0);
    }

    // weeks are a special case
    if (units === 'week') {
        this.weekday(0);
    }
    if (units === 'isoWeek') {
        this.isoWeekday(1);
    }

    // quarters are also special
    if (units === 'quarter') {
        this.month(Math.floor(this.month() / 3) * 3);
    }

    return this;
}

function endOf (units) {
    units = normalizeUnits(units);
    if (units === undefined || units === 'millisecond') {
        return this;
    }

    // 'date' is an alias for 'day', so it should be considered as such.
    if (units === 'date') {
        units = 'day';
    }

    return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms');
}

function valueOf () {
    return this._d.valueOf() - ((this._offset || 0) * 60000);
}

function unix () {
    return Math.floor(this.valueOf() / 1000);
}

function toDate () {
    return new Date(this.valueOf());
}

function toArray () {
    var m = this;
    return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()];
}

function toObject () {
    var m = this;
    return {
        years: m.year(),
        months: m.month(),
        date: m.date(),
        hours: m.hours(),
        minutes: m.minutes(),
        seconds: m.seconds(),
        milliseconds: m.milliseconds()
    };
}

function toJSON () {
    // new Date(NaN).toJSON() === null
    return this.isValid() ? this.toISOString() : null;
}

function isValid$2 () {
    return isValid(this);
}

function parsingFlags () {
    return extend({}, getParsingFlags(this));
}

function invalidAt () {
    return getParsingFlags(this).overflow;
}

function creationData() {
    return {
        input: this._i,
        format: this._f,
        locale: this._locale,
        isUTC: this._isUTC,
        strict: this._strict
    };
}

// FORMATTING

addFormatToken(0, ['gg', 2], 0, function () {
    return this.weekYear() % 100;
});

addFormatToken(0, ['GG', 2], 0, function () {
    return this.isoWeekYear() % 100;
});

function addWeekYearFormatToken (token, getter) {
    addFormatToken(0, [token, token.length], 0, getter);
}

addWeekYearFormatToken('gggg',     'weekYear');
addWeekYearFormatToken('ggggg',    'weekYear');
addWeekYearFormatToken('GGGG',  'isoWeekYear');
addWeekYearFormatToken('GGGGG', 'isoWeekYear');

// ALIASES

addUnitAlias('weekYear', 'gg');
addUnitAlias('isoWeekYear', 'GG');

// PRIORITY

addUnitPriority('weekYear', 1);
addUnitPriority('isoWeekYear', 1);


// PARSING

addRegexToken('G',      matchSigned);
addRegexToken('g',      matchSigned);
addRegexToken('GG',     match1to2, match2);
addRegexToken('gg',     match1to2, match2);
addRegexToken('GGGG',   match1to4, match4);
addRegexToken('gggg',   match1to4, match4);
addRegexToken('GGGGG',  match1to6, match6);
addRegexToken('ggggg',  match1to6, match6);

addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) {
    week[token.substr(0, 2)] = toInt(input);
});

addWeekParseToken(['gg', 'GG'], function (input, week, config, token) {
    week[token] = hooks.parseTwoDigitYear(input);
});

// MOMENTS

function getSetWeekYear (input) {
    return getSetWeekYearHelper.call(this,
            input,
            this.week(),
            this.weekday(),
            this.localeData()._week.dow,
            this.localeData()._week.doy);
}

function getSetISOWeekYear (input) {
    return getSetWeekYearHelper.call(this,
            input, this.isoWeek(), this.isoWeekday(), 1, 4);
}

function getISOWeeksInYear () {
    return weeksInYear(this.year(), 1, 4);
}

function getWeeksInYear () {
    var weekInfo = this.localeData()._week;
    return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
}

function getSetWeekYearHelper(input, week, weekday, dow, doy) {
    var weeksTarget;
    if (input == null) {
        return weekOfYear(this, dow, doy).year;
    } else {
        weeksTarget = weeksInYear(input, dow, doy);
        if (week > weeksTarget) {
            week = weeksTarget;
        }
        return setWeekAll.call(this, input, week, weekday, dow, doy);
    }
}

function setWeekAll(weekYear, week, weekday, dow, doy) {
    var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy),
        date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear);

    this.year(date.getUTCFullYear());
    this.month(date.getUTCMonth());
    this.date(date.getUTCDate());
    return this;
}

// FORMATTING

addFormatToken('Q', 0, 'Qo', 'quarter');

// ALIASES

addUnitAlias('quarter', 'Q');

// PRIORITY

addUnitPriority('quarter', 7);

// PARSING

addRegexToken('Q', match1);
addParseToken('Q', function (input, array) {
    array[MONTH] = (toInt(input) - 1) * 3;
});

// MOMENTS

function getSetQuarter (input) {
    return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
}

// FORMATTING

addFormatToken('D', ['DD', 2], 'Do', 'date');

// ALIASES

addUnitAlias('date', 'D');

// PRIOROITY
addUnitPriority('date', 9);

// PARSING

addRegexToken('D',  match1to2);
addRegexToken('DD', match1to2, match2);
addRegexToken('Do', function (isStrict, locale) {
    // TODO: Remove "ordinalParse" fallback in next major release.
    return isStrict ?
      (locale._dayOfMonthOrdinalParse || locale._ordinalParse) :
      locale._dayOfMonthOrdinalParseLenient;
});

addParseToken(['D', 'DD'], DATE);
addParseToken('Do', function (input, array) {
    array[DATE] = toInt(input.match(match1to2)[0], 10);
});

// MOMENTS

var getSetDayOfMonth = makeGetSet('Date', true);

// FORMATTING

addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear');

// ALIASES

addUnitAlias('dayOfYear', 'DDD');

// PRIORITY
addUnitPriority('dayOfYear', 4);

// PARSING

addRegexToken('DDD',  match1to3);
addRegexToken('DDDD', match3);
addParseToken(['DDD', 'DDDD'], function (input, array, config) {
    config._dayOfYear = toInt(input);
});

// HELPERS

// MOMENTS

function getSetDayOfYear (input) {
    var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1;
    return input == null ? dayOfYear : this.add((input - dayOfYear), 'd');
}

// FORMATTING

addFormatToken('m', ['mm', 2], 0, 'minute');

// ALIASES

addUnitAlias('minute', 'm');

// PRIORITY

addUnitPriority('minute', 14);

// PARSING

addRegexToken('m',  match1to2);
addRegexToken('mm', match1to2, match2);
addParseToken(['m', 'mm'], MINUTE);

// MOMENTS

var getSetMinute = makeGetSet('Minutes', false);

// FORMATTING

addFormatToken('s', ['ss', 2], 0, 'second');

// ALIASES

addUnitAlias('second', 's');

// PRIORITY

addUnitPriority('second', 15);

// PARSING

addRegexToken('s',  match1to2);
addRegexToken('ss', match1to2, match2);
addParseToken(['s', 'ss'], SECOND);

// MOMENTS

var getSetSecond = makeGetSet('Seconds', false);

// FORMATTING

addFormatToken('S', 0, 0, function () {
    return ~~(this.millisecond() / 100);
});

addFormatToken(0, ['SS', 2], 0, function () {
    return ~~(this.millisecond() / 10);
});

addFormatToken(0, ['SSS', 3], 0, 'millisecond');
addFormatToken(0, ['SSSS', 4], 0, function () {
    return this.millisecond() * 10;
});
addFormatToken(0, ['SSSSS', 5], 0, function () {
    return this.millisecond() * 100;
});
addFormatToken(0, ['SSSSSS', 6], 0, function () {
    return this.millisecond() * 1000;
});
addFormatToken(0, ['SSSSSSS', 7], 0, function () {
    return this.millisecond() * 10000;
});
addFormatToken(0, ['SSSSSSSS', 8], 0, function () {
    return this.millisecond() * 100000;
});
addFormatToken(0, ['SSSSSSSSS', 9], 0, function () {
    return this.millisecond() * 1000000;
});


// ALIASES

addUnitAlias('millisecond', 'ms');

// PRIORITY

addUnitPriority('millisecond', 16);

// PARSING

addRegexToken('S',    match1to3, match1);
addRegexToken('SS',   match1to3, match2);
addRegexToken('SSS',  match1to3, match3);

var token;
for (token = 'SSSS'; token.length <= 9; token += 'S') {
    addRegexToken(token, matchUnsigned);
}

function parseMs(input, array) {
    array[MILLISECOND] = toInt(('0.' + input) * 1000);
}

for (token = 'S'; token.length <= 9; token += 'S') {
    addParseToken(token, parseMs);
}
// MOMENTS

var getSetMillisecond = makeGetSet('Milliseconds', false);

// FORMATTING

addFormatToken('z',  0, 0, 'zoneAbbr');
addFormatToken('zz', 0, 0, 'zoneName');

// MOMENTS

function getZoneAbbr () {
    return this._isUTC ? 'UTC' : '';
}

function getZoneName () {
    return this._isUTC ? 'Coordinated Universal Time' : '';
}

var proto = Moment.prototype;

proto.add               = add;
proto.calendar          = calendar$1;
proto.clone             = clone;
proto.diff              = diff;
proto.endOf             = endOf;
proto.format            = format;
proto.from              = from;
proto.fromNow           = fromNow;
proto.to                = to;
proto.toNow             = toNow;
proto.get               = stringGet;
proto.invalidAt         = invalidAt;
proto.isAfter           = isAfter;
proto.isBefore          = isBefore;
proto.isBetween         = isBetween;
proto.isSame            = isSame;
proto.isSameOrAfter     = isSameOrAfter;
proto.isSameOrBefore    = isSameOrBefore;
proto.isValid           = isValid$2;
proto.lang              = lang;
proto.locale            = locale;
proto.localeData        = localeData;
proto.max               = prototypeMax;
proto.min               = prototypeMin;
proto.parsingFlags      = parsingFlags;
proto.set               = stringSet;
proto.startOf           = startOf;
proto.subtract          = subtract;
proto.toArray           = toArray;
proto.toObject          = toObject;
proto.toDate            = toDate;
proto.toISOString       = toISOString;
proto.inspect           = inspect;
proto.toJSON            = toJSON;
proto.toString          = toString;
proto.unix              = unix;
proto.valueOf           = valueOf;
proto.creationData      = creationData;

// Year
proto.year       = getSetYear;
proto.isLeapYear = getIsLeapYear;

// Week Year
proto.weekYear    = getSetWeekYear;
proto.isoWeekYear = getSetISOWeekYear;

// Quarter
proto.quarter = proto.quarters = getSetQuarter;

// Month
proto.month       = getSetMonth;
proto.daysInMonth = getDaysInMonth;

// Week
proto.week           = proto.weeks        = getSetWeek;
proto.isoWeek        = proto.isoWeeks     = getSetISOWeek;
proto.weeksInYear    = getWeeksInYear;
proto.isoWeeksInYear = getISOWeeksInYear;

// Day
proto.date       = getSetDayOfMonth;
proto.day        = proto.days             = getSetDayOfWeek;
proto.weekday    = getSetLocaleDayOfWeek;
proto.isoWeekday = getSetISODayOfWeek;
proto.dayOfYear  = getSetDayOfYear;

// Hour
proto.hour = proto.hours = getSetHour;

// Minute
proto.minute = proto.minutes = getSetMinute;

// Second
proto.second = proto.seconds = getSetSecond;

// Millisecond
proto.millisecond = proto.milliseconds = getSetMillisecond;

// Offset
proto.utcOffset            = getSetOffset;
proto.utc                  = setOffsetToUTC;
proto.local                = setOffsetToLocal;
proto.parseZone            = setOffsetToParsedOffset;
proto.hasAlignedHourOffset = hasAlignedHourOffset;
proto.isDST                = isDaylightSavingTime;
proto.isLocal              = isLocal;
proto.isUtcOffset          = isUtcOffset;
proto.isUtc                = isUtc;
proto.isUTC                = isUtc;

// Timezone
proto.zoneAbbr = getZoneAbbr;
proto.zoneName = getZoneName;

// Deprecations
proto.dates  = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth);
proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth);
proto.years  = deprecate('years accessor is deprecated. Use year instead', getSetYear);
proto.zone   = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone);
proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted);

function createUnix (input) {
    return createLocal(input * 1000);
}

function createInZone () {
    return createLocal.apply(null, arguments).parseZone();
}

function preParsePostFormat (string) {
    return string;
}

var proto$1 = Locale.prototype;

proto$1.calendar        = calendar;
proto$1.longDateFormat  = longDateFormat;
proto$1.invalidDate     = invalidDate;
proto$1.ordinal         = ordinal;
proto$1.preparse        = preParsePostFormat;
proto$1.postformat      = preParsePostFormat;
proto$1.relativeTime    = relativeTime;
proto$1.pastFuture      = pastFuture;
proto$1.set             = set;

// Month
proto$1.months            =        localeMonths;
proto$1.monthsShort       =        localeMonthsShort;
proto$1.monthsParse       =        localeMonthsParse;
proto$1.monthsRegex       = monthsRegex;
proto$1.monthsShortRegex  = monthsShortRegex;

// Week
proto$1.week = localeWeek;
proto$1.firstDayOfYear = localeFirstDayOfYear;
proto$1.firstDayOfWeek = localeFirstDayOfWeek;

// Day of Week
proto$1.weekdays       =        localeWeekdays;
proto$1.weekdaysMin    =        localeWeekdaysMin;
proto$1.weekdaysShort  =        localeWeekdaysShort;
proto$1.weekdaysParse  =        localeWeekdaysParse;

proto$1.weekdaysRegex       =        weekdaysRegex;
proto$1.weekdaysShortRegex  =        weekdaysShortRegex;
proto$1.weekdaysMinRegex    =        weekdaysMinRegex;

// Hours
proto$1.isPM = localeIsPM;
proto$1.meridiem = localeMeridiem;

function get$1 (format, index, field, setter) {
    var locale = getLocale();
    var utc = createUTC().set(setter, index);
    return locale[field](utc, format);
}

function listMonthsImpl (format, index, field) {
    if (isNumber(format)) {
        index = format;
        format = undefined;
    }

    format = format || '';

    if (index != null) {
        return get$1(format, index, field, 'month');
    }

    var i;
    var out = [];
    for (i = 0; i < 12; i++) {
        out[i] = get$1(format, i, field, 'month');
    }
    return out;
}

// ()
// (5)
// (fmt, 5)
// (fmt)
// (true)
// (true, 5)
// (true, fmt, 5)
// (true, fmt)
function listWeekdaysImpl (localeSorted, format, index, field) {
    if (typeof localeSorted === 'boolean') {
        if (isNumber(format)) {
            index = format;
            format = undefined;
        }

        format = format || '';
    } else {
        format = localeSorted;
        index = format;
        localeSorted = false;

        if (isNumber(format)) {
            index = format;
            format = undefined;
        }

        format = format || '';
    }

    var locale = getLocale(),
        shift = localeSorted ? locale._week.dow : 0;

    if (index != null) {
        return get$1(format, (index + shift) % 7, field, 'day');
    }

    var i;
    var out = [];
    for (i = 0; i < 7; i++) {
        out[i] = get$1(format, (i + shift) % 7, field, 'day');
    }
    return out;
}

function listMonths (format, index) {
    return listMonthsImpl(format, index, 'months');
}

function listMonthsShort (format, index) {
    return listMonthsImpl(format, index, 'monthsShort');
}

function listWeekdays (localeSorted, format, index) {
    return listWeekdaysImpl(localeSorted, format, index, 'weekdays');
}

function listWeekdaysShort (localeSorted, format, index) {
    return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort');
}

function listWeekdaysMin (localeSorted, format, index) {
    return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin');
}

getSetGlobalLocale('en', {
    dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/,
    ordinal : function (number) {
        var b = number % 10,
            output = (toInt(number % 100 / 10) === 1) ? 'th' :
            (b === 1) ? 'st' :
            (b === 2) ? 'nd' :
            (b === 3) ? 'rd' : 'th';
        return number + output;
    }
});

// Side effect imports
hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale);
hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale);

var mathAbs = Math.abs;

function abs () {
    var data           = this._data;

    this._milliseconds = mathAbs(this._milliseconds);
    this._days         = mathAbs(this._days);
    this._months       = mathAbs(this._months);

    data.milliseconds  = mathAbs(data.milliseconds);
    data.seconds       = mathAbs(data.seconds);
    data.minutes       = mathAbs(data.minutes);
    data.hours         = mathAbs(data.hours);
    data.months        = mathAbs(data.months);
    data.years         = mathAbs(data.years);

    return this;
}

function addSubtract$1 (duration, input, value, direction) {
    var other = createDuration(input, value);

    duration._milliseconds += direction * other._milliseconds;
    duration._days         += direction * other._days;
    duration._months       += direction * other._months;

    return duration._bubble();
}

// supports only 2.0-style add(1, 's') or add(duration)
function add$1 (input, value) {
    return addSubtract$1(this, input, value, 1);
}

// supports only 2.0-style subtract(1, 's') or subtract(duration)
function subtract$1 (input, value) {
    return addSubtract$1(this, input, value, -1);
}

function absCeil (number) {
    if (number < 0) {
        return Math.floor(number);
    } else {
        return Math.ceil(number);
    }
}

function bubble () {
    var milliseconds = this._milliseconds;
    var days         = this._days;
    var months       = this._months;
    var data         = this._data;
    var seconds, minutes, hours, years, monthsFromDays;

    // if we have a mix of positive and negative values, bubble down first
    // check: https://github.com/moment/moment/issues/2166
    if (!((milliseconds >= 0 && days >= 0 && months >= 0) ||
            (milliseconds <= 0 && days <= 0 && months <= 0))) {
        milliseconds += absCeil(monthsToDays(months) + days) * 864e5;
        days = 0;
        months = 0;
    }

    // The following code bubbles up values, see the tests for
    // examples of what that means.
    data.milliseconds = milliseconds % 1000;

    seconds           = absFloor(milliseconds / 1000);
    data.seconds      = seconds % 60;

    minutes           = absFloor(seconds / 60);
    data.minutes      = minutes % 60;

    hours             = absFloor(minutes / 60);
    data.hours        = hours % 24;

    days += absFloor(hours / 24);

    // convert days to months
    monthsFromDays = absFloor(daysToMonths(days));
    months += monthsFromDays;
    days -= absCeil(monthsToDays(monthsFromDays));

    // 12 months -> 1 year
    years = absFloor(months / 12);
    months %= 12;

    data.days   = days;
    data.months = months;
    data.years  = years;

    return this;
}

function daysToMonths (days) {
    // 400 years have 146097 days (taking into account leap year rules)
    // 400 years have 12 months === 4800
    return days * 4800 / 146097;
}

function monthsToDays (months) {
    // the reverse of daysToMonths
    return months * 146097 / 4800;
}

function as (units) {
    if (!this.isValid()) {
        return NaN;
    }
    var days;
    var months;
    var milliseconds = this._milliseconds;

    units = normalizeUnits(units);

    if (units === 'month' || units === 'year') {
        days   = this._days   + milliseconds / 864e5;
        months = this._months + daysToMonths(days);
        return units === 'month' ? months : months / 12;
    } else {
        // handle milliseconds separately because of floating point math errors (issue #1867)
        days = this._days + Math.round(monthsToDays(this._months));
        switch (units) {
            case 'week'   : return days / 7     + milliseconds / 6048e5;
            case 'day'    : return days         + milliseconds / 864e5;
            case 'hour'   : return days * 24    + milliseconds / 36e5;
            case 'minute' : return days * 1440  + milliseconds / 6e4;
            case 'second' : return days * 86400 + milliseconds / 1000;
            // Math.floor prevents floating point math errors here
            case 'millisecond': return Math.floor(days * 864e5) + milliseconds;
            default: throw new Error('Unknown unit ' + units);
        }
    }
}

// TODO: Use this.as('ms')?
function valueOf$1 () {
    if (!this.isValid()) {
        return NaN;
    }
    return (
        this._milliseconds +
        this._days * 864e5 +
        (this._months % 12) * 2592e6 +
        toInt(this._months / 12) * 31536e6
    );
}

function makeAs (alias) {
    return function () {
        return this.as(alias);
    };
}

var asMilliseconds = makeAs('ms');
var asSeconds      = makeAs('s');
var asMinutes      = makeAs('m');
var asHours        = makeAs('h');
var asDays         = makeAs('d');
var asWeeks        = makeAs('w');
var asMonths       = makeAs('M');
var asYears        = makeAs('y');

function get$2 (units) {
    units = normalizeUnits(units);
    return this.isValid() ? this[units + 's']() : NaN;
}

function makeGetter(name) {
    return function () {
        return this.isValid() ? this._data[name] : NaN;
    };
}

var milliseconds = makeGetter('milliseconds');
var seconds      = makeGetter('seconds');
var minutes      = makeGetter('minutes');
var hours        = makeGetter('hours');
var days         = makeGetter('days');
var months       = makeGetter('months');
var years        = makeGetter('years');

function weeks () {
    return absFloor(this.days() / 7);
}

var round = Math.round;
var thresholds = {
    ss: 44,         // a few seconds to seconds
    s : 45,         // seconds to minute
    m : 45,         // minutes to hour
    h : 22,         // hours to day
    d : 26,         // days to month
    M : 11          // months to year
};

// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
    return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
}

function relativeTime$1 (posNegDuration, withoutSuffix, locale) {
    var duration = createDuration(posNegDuration).abs();
    var seconds  = round(duration.as('s'));
    var minutes  = round(duration.as('m'));
    var hours    = round(duration.as('h'));
    var days     = round(duration.as('d'));
    var months   = round(duration.as('M'));
    var years    = round(duration.as('y'));

    var a = seconds <= thresholds.ss && ['s', seconds]  ||
            seconds < thresholds.s   && ['ss', seconds] ||
            minutes <= 1             && ['m']           ||
            minutes < thresholds.m   && ['mm', minutes] ||
            hours   <= 1             && ['h']           ||
            hours   < thresholds.h   && ['hh', hours]   ||
            days    <= 1             && ['d']           ||
            days    < thresholds.d   && ['dd', days]    ||
            months  <= 1             && ['M']           ||
            months  < thresholds.M   && ['MM', months]  ||
            years   <= 1             && ['y']           || ['yy', years];

    a[2] = withoutSuffix;
    a[3] = +posNegDuration > 0;
    a[4] = locale;
    return substituteTimeAgo.apply(null, a);
}

// This function allows you to set the rounding function for relative time strings
function getSetRelativeTimeRounding (roundingFunction) {
    if (roundingFunction === undefined) {
        return round;
    }
    if (typeof(roundingFunction) === 'function') {
        round = roundingFunction;
        return true;
    }
    return false;
}

// This function allows you to set a threshold for relative time strings
function getSetRelativeTimeThreshold (threshold, limit) {
    if (thresholds[threshold] === undefined) {
        return false;
    }
    if (limit === undefined) {
        return thresholds[threshold];
    }
    thresholds[threshold] = limit;
    if (threshold === 's') {
        thresholds.ss = limit - 1;
    }
    return true;
}

function humanize (withSuffix) {
    if (!this.isValid()) {
        return this.localeData().invalidDate();
    }

    var locale = this.localeData();
    var output = relativeTime$1(this, !withSuffix, locale);

    if (withSuffix) {
        output = locale.pastFuture(+this, output);
    }

    return locale.postformat(output);
}

var abs$1 = Math.abs;

function toISOString$1() {
    // for ISO strings we do not use the normal bubbling rules:
    //  * milliseconds bubble up until they become hours
    //  * days do not bubble at all
    //  * months bubble up until they become years
    // This is because there is no context-free conversion between hours and days
    // (think of clock changes)
    // and also not between days and months (28-31 days per month)
    if (!this.isValid()) {
        return this.localeData().invalidDate();
    }

    var seconds = abs$1(this._milliseconds) / 1000;
    var days         = abs$1(this._days);
    var months       = abs$1(this._months);
    var minutes, hours, years;

    // 3600 seconds -> 60 minutes -> 1 hour
    minutes           = absFloor(seconds / 60);
    hours             = absFloor(minutes / 60);
    seconds %= 60;
    minutes %= 60;

    // 12 months -> 1 year
    years  = absFloor(months / 12);
    months %= 12;


    // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
    var Y = years;
    var M = months;
    var D = days;
    var h = hours;
    var m = minutes;
    var s = seconds;
    var total = this.asSeconds();

    if (!total) {
        // this is the same as C#'s (Noda) and python (isodate)...
        // but not other JS (goog.date)
        return 'P0D';
    }

    return (total < 0 ? '-' : '') +
        'P' +
        (Y ? Y + 'Y' : '') +
        (M ? M + 'M' : '') +
        (D ? D + 'D' : '') +
        ((h || m || s) ? 'T' : '') +
        (h ? h + 'H' : '') +
        (m ? m + 'M' : '') +
        (s ? s + 'S' : '');
}

var proto$2 = Duration.prototype;

proto$2.isValid        = isValid$1;
proto$2.abs            = abs;
proto$2.add            = add$1;
proto$2.subtract       = subtract$1;
proto$2.as             = as;
proto$2.asMilliseconds = asMilliseconds;
proto$2.asSeconds      = asSeconds;
proto$2.asMinutes      = asMinutes;
proto$2.asHours        = asHours;
proto$2.asDays         = asDays;
proto$2.asWeeks        = asWeeks;
proto$2.asMonths       = asMonths;
proto$2.asYears        = asYears;
proto$2.valueOf        = valueOf$1;
proto$2._bubble        = bubble;
proto$2.get            = get$2;
proto$2.milliseconds   = milliseconds;
proto$2.seconds        = seconds;
proto$2.minutes        = minutes;
proto$2.hours          = hours;
proto$2.days           = days;
proto$2.weeks          = weeks;
proto$2.months         = months;
proto$2.years          = years;
proto$2.humanize       = humanize;
proto$2.toISOString    = toISOString$1;
proto$2.toString       = toISOString$1;
proto$2.toJSON         = toISOString$1;
proto$2.locale         = locale;
proto$2.localeData     = localeData;

// Deprecations
proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1);
proto$2.lang = lang;

// Side effect imports

// FORMATTING

addFormatToken('X', 0, 0, 'unix');
addFormatToken('x', 0, 0, 'valueOf');

// PARSING

addRegexToken('x', matchSigned);
addRegexToken('X', matchTimestamp);
addParseToken('X', function (input, array, config) {
    config._d = new Date(parseFloat(input, 10) * 1000);
});
addParseToken('x', function (input, array, config) {
    config._d = new Date(toInt(input));
});

// Side effect imports


hooks.version = '2.18.1';

setHookCallback(createLocal);

hooks.fn                    = proto;
hooks.min                   = min;
hooks.max                   = max;
hooks.now                   = now;
hooks.utc                   = createUTC;
hooks.unix                  = createUnix;
hooks.months                = listMonths;
hooks.isDate                = isDate;
hooks.locale                = getSetGlobalLocale;
hooks.invalid               = createInvalid;
hooks.duration              = createDuration;
hooks.isMoment              = isMoment;
hooks.weekdays              = listWeekdays;
hooks.parseZone             = createInZone;
hooks.localeData            = getLocale;
hooks.isDuration            = isDuration;
hooks.monthsShort           = listMonthsShort;
hooks.weekdaysMin           = listWeekdaysMin;
hooks.defineLocale          = defineLocale;
hooks.updateLocale          = updateLocale;
hooks.locales               = listLocales;
hooks.weekdaysShort         = listWeekdaysShort;
hooks.normalizeUnits        = normalizeUnits;
hooks.relativeTimeRounding = getSetRelativeTimeRounding;
hooks.relativeTimeThreshold = getSetRelativeTimeThreshold;
hooks.calendarFormat        = getCalendarFormat;
hooks.prototype             = proto;

return hooks;

})));

},{}],7:[function(require,module,exports){
/**
 * @namespace Chart
 */
var Chart = require(29)();

Chart.helpers = require(45);

// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests!
require(27)(Chart);

Chart.defaults = require(25);
Chart.Element = require(26);
Chart.elements = require(40);
Chart.Interaction = require(28);
Chart.platform = require(48);

require(31)(Chart);
require(22)(Chart);
require(23)(Chart);
require(24)(Chart);
require(30)(Chart);
require(33)(Chart);
require(32)(Chart);
require(35)(Chart);

require(54)(Chart);
require(52)(Chart);
require(53)(Chart);
require(55)(Chart);
require(56)(Chart);
require(57)(Chart);

// Controllers must be loaded after elements
// See Chart.core.datasetController.dataElementType
require(15)(Chart);
require(16)(Chart);
require(17)(Chart);
require(18)(Chart);
require(19)(Chart);
require(20)(Chart);
require(21)(Chart);

require(8)(Chart);
require(9)(Chart);
require(10)(Chart);
require(11)(Chart);
require(12)(Chart);
require(13)(Chart);
require(14)(Chart);

// Loading built-it plugins
var plugins = [];

plugins.push(
	require(49)(Chart),
	require(50)(Chart),
	require(51)(Chart)
);

Chart.plugins.register(plugins);

Chart.platform.initialize();

module.exports = Chart;
if (typeof window !== 'undefined') {
	window.Chart = Chart;
}

// DEPRECATIONS

/**
 * Provided for backward compatibility, use Chart.helpers.canvas instead.
 * @namespace Chart.canvasHelpers
 * @deprecated since version 2.6.0
 * @todo remove at version 3
 * @private
 */
Chart.canvasHelpers = Chart.helpers.canvas;

},{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"35":35,"40":40,"45":45,"48":48,"49":49,"50":50,"51":51,"52":52,"53":53,"54":54,"55":55,"56":56,"57":57,"8":8,"9":9}],8:[function(require,module,exports){
'use strict';

module.exports = function(Chart) {

	Chart.Bar = function(context, config) {
		config.type = 'bar';

		return new Chart(context, config);
	};

};

},{}],9:[function(require,module,exports){
'use strict';

module.exports = function(Chart) {

	Chart.Bubble = function(context, config) {
		config.type = 'bubble';
		return new Chart(context, config);
	};

};

},{}],10:[function(require,module,exports){
'use strict';

module.exports = function(Chart) {

	Chart.Doughnut = function(context, config) {
		config.type = 'doughnut';

		return new Chart(context, config);
	};

};

},{}],11:[function(require,module,exports){
'use strict';

module.exports = function(Chart) {

	Chart.Line = function(context, config) {
		config.type = 'line';

		return new Chart(context, config);
	};

};

},{}],12:[function(require,module,exports){
'use strict';

module.exports = function(Chart) {

	Chart.PolarArea = function(context, config) {
		config.type = 'polarArea';

		return new Chart(context, config);
	};

};

},{}],13:[function(require,module,exports){
'use strict';

module.exports = function(Chart) {

	Chart.Radar = function(context, config) {
		config.type = 'radar';

		return new Chart(context, config);
	};

};

},{}],14:[function(require,module,exports){
'use strict';

module.exports = function(Chart) {
	Chart.Scatter = function(context, config) {
		config.type = 'scatter';
		return new Chart(context, config);
	};
};

},{}],15:[function(require,module,exports){
'use strict';

var defaults = require(25);
var elements = require(40);
var helpers = require(45);

defaults._set('bar', {
	hover: {
		mode: 'label'
	},

	scales: {
		xAxes: [{
			type: 'category',

			// Specific to Bar Controller
			categoryPercentage: 0.8,
			barPercentage: 0.9,

			// offset settings
			offset: true,

			// grid line settings
			gridLines: {
				offsetGridLines: true
			}
		}],

		yAxes: [{
			type: 'linear'
		}]
	}
});

defaults._set('horizontalBar', {
	hover: {
		mode: 'index',
		axis: 'y'
	},

	scales: {
		xAxes: [{
			type: 'linear',
			position: 'bottom'
		}],

		yAxes: [{
			position: 'left',
			type: 'category',

			// Specific to Horizontal Bar Controller
			categoryPercentage: 0.8,
			barPercentage: 0.9,

			// offset settings
			offset: true,

			// grid line settings
			gridLines: {
				offsetGridLines: true
			}
		}]
	},

	elements: {
		rectangle: {
			borderSkipped: 'left'
		}
	},

	tooltips: {
		callbacks: {
			title: function(item, data) {
				// Pick first xLabel for now
				var title = '';

				if (item.length > 0) {
					if (item[0].yLabel) {
						title = item[0].yLabel;
					} else if (data.labels.length > 0 && item[0].index < data.labels.length) {
						title = data.labels[item[0].index];
					}
				}

				return title;
			},

			label: function(item, data) {
				var datasetLabel = data.datasets[item.datasetIndex].label || '';
				return datasetLabel + ': ' + item.xLabel;
			}
		},
		mode: 'index',
		axis: 'y'
	}
});

module.exports = function(Chart) {

	Chart.controllers.bar = Chart.DatasetController.extend({

		dataElementType: elements.Rectangle,

		initialize: function() {
			var me = this;
			var meta;

			Chart.DatasetController.prototype.initialize.apply(me, arguments);

			meta = me.getMeta();
			meta.stack = me.getDataset().stack;
			meta.bar = true;
		},

		update: function(reset) {
			var me = this;
			var rects = me.getMeta().data;
			var i, ilen;

			me._ruler = me.getRuler();

			for (i = 0, ilen = rects.length; i < ilen; ++i) {
				me.updateElement(rects[i], i, reset);
			}
		},

		updateElement: function(rectangle, index, reset) {
			var me = this;
			var chart = me.chart;
			var meta = me.getMeta();
			var dataset = me.getDataset();
			var custom = rectangle.custom || {};
			var rectangleOptions = chart.options.elements.rectangle;

			rectangle._xScale = me.getScaleForId(meta.xAxisID);
			rectangle._yScale = me.getScaleForId(meta.yAxisID);
			rectangle._datasetIndex = me.index;
			rectangle._index = index;

			rectangle._model = {
				datasetLabel: dataset.label,
				label: chart.data.labels[index],
				borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped,
				backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor),
				borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor),
				borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth)
			};

			me.updateElementGeometry(rectangle, index, reset);

			rectangle.pivot();
		},

		/**
		 * @private
		 */
		updateElementGeometry: function(rectangle, index, reset) {
			var me = this;
			var model = rectangle._model;
			var vscale = me.getValueScale();
			var base = vscale.getBasePixel();
			var horizontal = vscale.isHorizontal();
			var ruler = me._ruler || me.getRuler();
			var vpixels = me.calculateBarValuePixels(me.index, index);
			var ipixels = me.calculateBarIndexPixels(me.index, index, ruler);

			model.horizontal = horizontal;
			model.base = reset ? base : vpixels.base;
			model.x = horizontal ? reset ? base : vpixels.head : ipixels.center;
			model.y = horizontal ? ipixels.center : reset ? base : vpixels.head;
			model.height = horizontal ? ipixels.size : undefined;
			model.width = horizontal ? undefined : ipixels.size;
		},

		/**
		 * @private
		 */
		getValueScaleId: function() {
			return this.getMeta().yAxisID;
		},

		/**
		 * @private
		 */
		getIndexScaleId: function() {
			return this.getMeta().xAxisID;
		},

		/**
		 * @private
		 */
		getValueScale: function() {
			return this.getScaleForId(this.getValueScaleId());
		},

		/**
		 * @private
		 */
		getIndexScale: function() {
			return this.getScaleForId(this.getIndexScaleId());
		},

		/**
		 * Returns the effective number of stacks based on groups and bar visibility.
		 * @private
		 */
		getStackCount: function(last) {
			var me = this;
			var chart = me.chart;
			var scale = me.getIndexScale();
			var stacked = scale.options.stacked;
			var ilen = last === undefined ? chart.data.datasets.length : last + 1;
			var stacks = [];
			var i, meta;

			for (i = 0; i < ilen; ++i) {
				meta = chart.getDatasetMeta(i);
				if (meta.bar && chart.isDatasetVisible(i) &&
					(stacked === false ||
					(stacked === true && stacks.indexOf(meta.stack) === -1) ||
					(stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) {
					stacks.push(meta.stack);
				}
			}

			return stacks.length;
		},

		/**
		 * Returns the stack index for the given dataset based on groups and bar visibility.
		 * @private
		 */
		getStackIndex: function(datasetIndex) {
			return this.getStackCount(datasetIndex) - 1;
		},

		/**
		 * @private
		 */
		getRuler: function() {
			var me = this;
			var scale = me.getIndexScale();
			var stackCount = me.getStackCount();
			var datasetIndex = me.index;
			var pixels = [];
			var isHorizontal = scale.isHorizontal();
			var start = isHorizontal ? scale.left : scale.top;
			var end = start + (isHorizontal ? scale.width : scale.height);
			var i, ilen;

			for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) {
				pixels.push(scale.getPixelForValue(null, i, datasetIndex));
			}

			return {
				pixels: pixels,
				start: start,
				end: end,
				stackCount: stackCount,
				scale: scale
			};
		},

		/**
		 * Note: pixel values are not clamped to the scale area.
		 * @private
		 */
		calculateBarValuePixels: function(datasetIndex, index) {
			var me = this;
			var chart = me.chart;
			var meta = me.getMeta();
			var scale = me.getValueScale();
			var datasets = chart.data.datasets;
			var value = scale.getRightValue(datasets[datasetIndex].data[index]);
			var stacked = scale.options.stacked;
			var stack = meta.stack;
			var start = 0;
			var i, imeta, ivalue, base, head, size;

			if (stacked || (stacked === undefined && stack !== undefined)) {
				for (i = 0; i < datasetIndex; ++i) {
					imeta = chart.getDatasetMeta(i);

					if (imeta.bar &&
						imeta.stack === stack &&
						imeta.controller.getValueScaleId() === scale.id &&
						chart.isDatasetVisible(i)) {

						ivalue = scale.getRightValue(datasets[i].data[index]);
						if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) {
							start += ivalue;
						}
					}
				}
			}

			base = scale.getPixelForValue(start);
			head = scale.getPixelForValue(start + value);
			size = (head - base) / 2;

			return {
				size: size,
				base: base,
				head: head,
				center: head + size / 2
			};
		},

		/**
		 * @private
		 */
		calculateBarIndexPixels: function(datasetIndex, index, ruler) {
			var me = this;
			var options = ruler.scale.options;
			var stackIndex = me.getStackIndex(datasetIndex);
			var pixels = ruler.pixels;
			var base = pixels[index];
			var length = pixels.length;
			var start = ruler.start;
			var end = ruler.end;
			var leftSampleSize, rightSampleSize, leftCategorySize, rightCategorySize, fullBarSize, size;

			if (length === 1) {
				leftSampleSize = base > start ? base - start : end - base;
				rightSampleSize = base < end ? end - base : base - start;
			} else {
				if (index > 0) {
					leftSampleSize = (base - pixels[index - 1]) / 2;
					if (index === length - 1) {
						rightSampleSize = leftSampleSize;
					}
				}
				if (index < length - 1) {
					rightSampleSize = (pixels[index + 1] - base) / 2;
					if (index === 0) {
						leftSampleSize = rightSampleSize;
					}
				}
			}

			leftCategorySize = leftSampleSize * options.categoryPercentage;
			rightCategorySize = rightSampleSize * options.categoryPercentage;
			fullBarSize = (leftCategorySize + rightCategorySize) / ruler.stackCount;
			size = fullBarSize * options.barPercentage;

			size = Math.min(
				helpers.valueOrDefault(options.barThickness, size),
				helpers.valueOrDefault(options.maxBarThickness, Infinity));

			base -= leftCategorySize;
			base += fullBarSize * stackIndex;
			base += (fullBarSize - size) / 2;

			return {
				size: size,
				base: base,
				head: base + size,
				center: base + size / 2
			};
		},

		draw: function() {
			var me = this;
			var chart = me.chart;
			var scale = me.getValueScale();
			var rects = me.getMeta().data;
			var dataset = me.getDataset();
			var ilen = rects.length;
			var i = 0;

			helpers.canvas.clipArea(chart.ctx, chart.chartArea);

			for (; i < ilen; ++i) {
				if (!isNaN(scale.getRightValue(dataset.data[i]))) {
					rects[i].draw();
				}
			}

			helpers.canvas.unclipArea(chart.ctx);
		},

		setHoverStyle: function(rectangle) {
			var dataset = this.chart.data.datasets[rectangle._datasetIndex];
			var index = rectangle._index;
			var custom = rectangle.custom || {};
			var model = rectangle._model;

			model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
			model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor));
			model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
		},

		removeHoverStyle: function(rectangle) {
			var dataset = this.chart.data.datasets[rectangle._datasetIndex];
			var index = rectangle._index;
			var custom = rectangle.custom || {};
			var model = rectangle._model;
			var rectangleElementOptions = this.chart.options.elements.rectangle;

			model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor);
			model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor);
			model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth);
		}
	});

	Chart.controllers.horizontalBar = Chart.controllers.bar.extend({
		/**
		 * @private
		 */
		getValueScaleId: function() {
			return this.getMeta().xAxisID;
		},

		/**
		 * @private
		 */
		getIndexScaleId: function() {
			return this.getMeta().yAxisID;
		}
	});
};

},{"25":25,"40":40,"45":45}],16:[function(require,module,exports){
'use strict';

var defaults = require(25);
var elements = require(40);
var helpers = require(45);

defaults._set('bubble', {
	hover: {
		mode: 'single'
	},

	scales: {
		xAxes: [{
			type: 'linear', // bubble should probably use a linear scale by default
			position: 'bottom',
			id: 'x-axis-0' // need an ID so datasets can reference the scale
		}],
		yAxes: [{
			type: 'linear',
			position: 'left',
			id: 'y-axis-0'
		}]
	},

	tooltips: {
		callbacks: {
			title: function() {
				// Title doesn't make sense for scatter since we format the data as a point
				return '';
			},
			label: function(item, data) {
				var datasetLabel = data.datasets[item.datasetIndex].label || '';
				var dataPoint = data.datasets[item.datasetIndex].data[item.index];
				return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')';
			}
		}
	}
});


module.exports = function(Chart) {

	Chart.controllers.bubble = Chart.DatasetController.extend({
		/**
		 * @protected
		 */
		dataElementType: elements.Point,

		/**
		 * @protected
		 */
		update: function(reset) {
			var me = this;
			var meta = me.getMeta();
			var points = meta.data;

			// Update Points
			helpers.each(points, function(point, index) {
				me.updateElement(point, index, reset);
			});
		},

		/**
		 * @protected
		 */
		updateElement: function(point, index, reset) {
			var me = this;
			var meta = me.getMeta();
			var custom = point.custom || {};
			var xScale = me.getScaleForId(meta.xAxisID);
			var yScale = me.getScaleForId(meta.yAxisID);
			var options = me._resolveElementOptions(point, index);
			var data = me.getDataset().data[index];
			var dsIndex = me.index;

			var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex);
			var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex);

			point._xScale = xScale;
			point._yScale = yScale;
			point._options = options;
			point._datasetIndex = dsIndex;
			point._index = index;
			point._model = {
				backgroundColor: options.backgroundColor,
				borderColor: options.borderColor,
				borderWidth: options.borderWidth,
				hitRadius: options.hitRadius,
				pointStyle: options.pointStyle,
				radius: reset ? 0 : options.radius,
				skip: custom.skip || isNaN(x) || isNaN(y),
				x: x,
				y: y,
			};

			point.pivot();
		},

		/**
		 * @protected
		 */
		setHoverStyle: function(point) {
			var model = point._model;
			var options = point._options;

			model.backgroundColor = helpers.valueOrDefault(options.hoverBackgroundColor, helpers.getHoverColor(options.backgroundColor));
			model.borderColor = helpers.valueOrDefault(options.hoverBorderColor, helpers.getHoverColor(options.borderColor));
			model.borderWidth = helpers.valueOrDefault(options.hoverBorderWidth, options.borderWidth);
			model.radius = options.radius + options.hoverRadius;
		},

		/**
		 * @protected
		 */
		removeHoverStyle: function(point) {
			var model = point._model;
			var options = point._options;

			model.backgroundColor = options.backgroundColor;
			model.borderColor = options.borderColor;
			model.borderWidth = options.borderWidth;
			model.radius = options.radius;
		},

		/**
		 * @private
		 */
		_resolveElementOptions: function(point, index) {
			var me = this;
			var chart = me.chart;
			var datasets = chart.data.datasets;
			var dataset = datasets[me.index];
			var custom = point.custom || {};
			var options = chart.options.elements.point;
			var resolve = helpers.options.resolve;
			var data = dataset.data[index];
			var values = {};
			var i, ilen, key;

			// Scriptable options
			var context = {
				chart: chart,
				dataIndex: index,
				dataset: dataset,
				datasetIndex: me.index
			};

			var keys = [
				'backgroundColor',
				'borderColor',
				'borderWidth',
				'hoverBackgroundColor',
				'hoverBorderColor',
				'hoverBorderWidth',
				'hoverRadius',
				'hitRadius',
				'pointStyle'
			];

			for (i = 0, ilen = keys.length; i < ilen; ++i) {
				key = keys[i];
				values[key] = resolve([
					custom[key],
					dataset[key],
					options[key]
				], context, index);
			}

			// Custom radius resolution
			values.radius = resolve([
				custom.radius,
				data ? data.r : undefined,
				dataset.radius,
				options.radius
			], context, index);

			return values;
		}
	});
};

},{"25":25,"40":40,"45":45}],17:[function(require,module,exports){
'use strict';

var defaults = require(25);
var elements = require(40);
var helpers = require(45);

defaults._set('doughnut', {
	animation: {
		// Boolean - Whether we animate the rotation of the Doughnut
		animateRotate: true,
		// Boolean - Whether we animate scaling the Doughnut from the centre
		animateScale: false
	},
	hover: {
		mode: 'single'
	},
	legendCallback: function(chart) {
		var text = [];
		text.push('<ul class="' + chart.id + '-legend">');

		var data = chart.data;
		var datasets = data.datasets;
		var labels = data.labels;

		if (datasets.length) {
			for (var i = 0; i < datasets[0].data.length; ++i) {
				text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
				if (labels[i]) {
					text.push(labels[i]);
				}
				text.push('</li>');
			}
		}

		text.push('</ul>');
		return text.join('');
	},
	legend: {
		labels: {
			generateLabels: function(chart) {
				var data = chart.data;
				if (data.labels.length && data.datasets.length) {
					return data.labels.map(function(label, i) {
						var meta = chart.getDatasetMeta(0);
						var ds = data.datasets[0];
						var arc = meta.data[i];
						var custom = arc && arc.custom || {};
						var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
						var arcOpts = chart.options.elements.arc;
						var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
						var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
						var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);

						return {
							text: label,
							fillStyle: fill,
							strokeStyle: stroke,
							lineWidth: bw,
							hidden: isNaN(ds.data[i]) || meta.data[i].hidden,

							// Extra data used for toggling the correct item
							index: i
						};
					});
				}
				return [];
			}
		},

		onClick: function(e, legendItem) {
			var index = legendItem.index;
			var chart = this.chart;
			var i, ilen, meta;

			for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
				meta = chart.getDatasetMeta(i);
				// toggle visibility of index if exists
				if (meta.data[index]) {
					meta.data[index].hidden = !meta.data[index].hidden;
				}
			}

			chart.update();
		}
	},

	// The percentage of the chart that we cut out of the middle.
	cutoutPercentage: 50,

	// The rotation of the chart, where the first data arc begins.
	rotation: Math.PI * -0.5,

	// The total circumference of the chart.
	circumference: Math.PI * 2.0,

	// Need to override these to give a nice default
	tooltips: {
		callbacks: {
			title: function() {
				return '';
			},
			label: function(tooltipItem, data) {
				var dataLabel = data.labels[tooltipItem.index];
				var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];

				if (helpers.isArray(dataLabel)) {
					// show value on first line of multiline label
					// need to clone because we are changing the value
					dataLabel = dataLabel.slice();
					dataLabel[0] += value;
				} else {
					dataLabel += value;
				}

				return dataLabel;
			}
		}
	}
});

defaults._set('pie', helpers.clone(defaults.doughnut));
defaults._set('pie', {
	cutoutPercentage: 0
});

module.exports = function(Chart) {

	Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({

		dataElementType: elements.Arc,

		linkScales: helpers.noop,

		// Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
		getRingIndex: function(datasetIndex) {
			var ringIndex = 0;

			for (var j = 0; j < datasetIndex; ++j) {
				if (this.chart.isDatasetVisible(j)) {
					++ringIndex;
				}
			}

			return ringIndex;
		},

		update: function(reset) {
			var me = this;
			var chart = me.chart;
			var chartArea = chart.chartArea;
			var opts = chart.options;
			var arcOpts = opts.elements.arc;
			var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth;
			var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth;
			var minSize = Math.min(availableWidth, availableHeight);
			var offset = {x: 0, y: 0};
			var meta = me.getMeta();
			var cutoutPercentage = opts.cutoutPercentage;
			var circumference = opts.circumference;

			// If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
			if (circumference < Math.PI * 2.0) {
				var startAngle = opts.rotation % (Math.PI * 2.0);
				startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
				var endAngle = startAngle + circumference;
				var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)};
				var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)};
				var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);
				var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);
				var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);
				var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);
				var cutout = cutoutPercentage / 100.0;
				var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))};
				var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))};
				var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};
				minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
				offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
			}

			chart.borderWidth = me.getMaxBorderWidth(meta.data);
			chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);
			chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
			chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
			chart.offsetX = offset.x * chart.outerRadius;
			chart.offsetY = offset.y * chart.outerRadius;

			meta.total = me.calculateTotal();

			me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index));
			me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0);

			helpers.each(meta.data, function(arc, index) {
				me.updateElement(arc, index, reset);
			});
		},

		updateElement: function(arc, index, reset) {
			var me = this;
			var chart = me.chart;
			var chartArea = chart.chartArea;
			var opts = chart.options;
			var animationOpts = opts.animation;
			var centerX = (chartArea.left + chartArea.right) / 2;
			var centerY = (chartArea.top + chartArea.bottom) / 2;
			var startAngle = opts.rotation; // non reset case handled later
			var endAngle = opts.rotation; // non reset case handled later
			var dataset = me.getDataset();
			var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI));
			var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
			var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
			var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;

			helpers.extend(arc, {
				// Utility
				_datasetIndex: me.index,
				_index: index,

				// Desired view properties
				_model: {
					x: centerX + chart.offsetX,
					y: centerY + chart.offsetY,
					startAngle: startAngle,
					endAngle: endAngle,
					circumference: circumference,
					outerRadius: outerRadius,
					innerRadius: innerRadius,
					label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
				}
			});

			var model = arc._model;
			// Resets the visual styles
			this.removeHoverStyle(arc);

			// Set correct angles if not resetting
			if (!reset || !animationOpts.animateRotate) {
				if (index === 0) {
					model.startAngle = opts.rotation;
				} else {
					model.startAngle = me.getMeta().data[index - 1]._model.endAngle;
				}

				model.endAngle = model.startAngle + model.circumference;
			}

			arc.pivot();
		},

		removeHoverStyle: function(arc) {
			Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc);
		},

		calculateTotal: function() {
			var dataset = this.getDataset();
			var meta = this.getMeta();
			var total = 0;
			var value;

			helpers.each(meta.data, function(element, index) {
				value = dataset.data[index];
				if (!isNaN(value) && !element.hidden) {
					total += Math.abs(value);
				}
			});

			/* if (total === 0) {
				total = NaN;
			}*/

			return total;
		},

		calculateCircumference: function(value) {
			var total = this.getMeta().total;
			if (total > 0 && !isNaN(value)) {
				return (Math.PI * 2.0) * (value / total);
			}
			return 0;
		},

		// gets the max border or hover width to properly scale pie charts
		getMaxBorderWidth: function(arcs) {
			var max = 0;
			var index = this.index;
			var length = arcs.length;
			var borderWidth;
			var hoverWidth;

			for (var i = 0; i < length; i++) {
				borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0;
				hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0;

				max = borderWidth > max ? borderWidth : max;
				max = hoverWidth > max ? hoverWidth : max;
			}
			return max;
		}
	});
};

},{"25":25,"40":40,"45":45}],18:[function(require,module,exports){
'use strict';

var defaults = require(25);
var elements = require(40);
var helpers = require(45);

defaults._set('line', {
	showLines: true,
	spanGaps: false,

	hover: {
		mode: 'label'
	},

	scales: {
		xAxes: [{
			type: 'category',
			id: 'x-axis-0'
		}],
		yAxes: [{
			type: 'linear',
			id: 'y-axis-0'
		}]
	}
});

module.exports = function(Chart) {

	function lineEnabled(dataset, options) {
		return helpers.valueOrDefault(dataset.showLine, options.showLines);
	}

	Chart.controllers.line = Chart.DatasetController.extend({

		datasetElementType: elements.Line,

		dataElementType: elements.Point,

		update: function(reset) {
			var me = this;
			var meta = me.getMeta();
			var line = meta.dataset;
			var points = meta.data || [];
			var options = me.chart.options;
			var lineElementOptions = options.elements.line;
			var scale = me.getScaleForId(meta.yAxisID);
			var i, ilen, custom;
			var dataset = me.getDataset();
			var showLine = lineEnabled(dataset, options);

			// Update Line
			if (showLine) {
				custom = line.custom || {};

				// Compatibility: If the properties are defined with only the old name, use those values
				if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
					dataset.lineTension = dataset.tension;
				}

				// Utility
				line._scale = scale;
				line._datasetIndex = me.index;
				// Data
				line._children = points;
				// Model
				line._model = {
					// Appearance
					// The default behavior of lines is to break at null values, according
					// to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158
					// This option gives lines the ability to span gaps
					spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps,
					tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension),
					backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
					borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
					borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
					borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
					borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
					borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
					borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
					fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
					steppedLine: custom.steppedLine ? custom.steppedLine : helpers.valueOrDefault(dataset.steppedLine, lineElementOptions.stepped),
					cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.valueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode),
				};

				line.pivot();
			}

			// Update Points
			for (i = 0, ilen = points.length; i < ilen; ++i) {
				me.updateElement(points[i], i, reset);
			}

			if (showLine && line._model.tension !== 0) {
				me.updateBezierControlPoints();
			}

			// Now pivot the point for animation
			for (i = 0, ilen = points.length; i < ilen; ++i) {
				points[i].pivot();
			}
		},

		getPointBackgroundColor: function(point, index) {
			var backgroundColor = this.chart.options.elements.point.backgroundColor;
			var dataset = this.getDataset();
			var custom = point.custom || {};

			if (custom.backgroundColor) {
				backgroundColor = custom.backgroundColor;
			} else if (dataset.pointBackgroundColor) {
				backgroundColor = helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor);
			} else if (dataset.backgroundColor) {
				backgroundColor = dataset.backgroundColor;
			}

			return backgroundColor;
		},

		getPointBorderColor: function(point, index) {
			var borderColor = this.chart.options.elements.point.borderColor;
			var dataset = this.getDataset();
			var custom = point.custom || {};

			if (custom.borderColor) {
				borderColor = custom.borderColor;
			} else if (dataset.pointBorderColor) {
				borderColor = helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor);
			} else if (dataset.borderColor) {
				borderColor = dataset.borderColor;
			}

			return borderColor;
		},

		getPointBorderWidth: function(point, index) {
			var borderWidth = this.chart.options.elements.point.borderWidth;
			var dataset = this.getDataset();
			var custom = point.custom || {};

			if (!isNaN(custom.borderWidth)) {
				borderWidth = custom.borderWidth;
			} else if (!isNaN(dataset.pointBorderWidth) || helpers.isArray(dataset.pointBorderWidth)) {
				borderWidth = helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth);
			} else if (!isNaN(dataset.borderWidth)) {
				borderWidth = dataset.borderWidth;
			}

			return borderWidth;
		},

		updateElement: function(point, index, reset) {
			var me = this;
			var meta = me.getMeta();
			var custom = point.custom || {};
			var dataset = me.getDataset();
			var datasetIndex = me.index;
			var value = dataset.data[index];
			var yScale = me.getScaleForId(meta.yAxisID);
			var xScale = me.getScaleForId(meta.xAxisID);
			var pointOptions = me.chart.options.elements.point;
			var x, y;

			// Compatibility: If the properties are defined with only the old name, use those values
			if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
				dataset.pointRadius = dataset.radius;
			}
			if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
				dataset.pointHitRadius = dataset.hitRadius;
			}

			x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex);
			y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex);

			// Utility
			point._xScale = xScale;
			point._yScale = yScale;
			point._datasetIndex = datasetIndex;
			point._index = index;

			// Desired view properties
			point._model = {
				x: x,
				y: y,
				skip: custom.skip || isNaN(x) || isNaN(y),
				// Appearance
				radius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius),
				pointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),
				backgroundColor: me.getPointBackgroundColor(point, index),
				borderColor: me.getPointBorderColor(point, index),
				borderWidth: me.getPointBorderWidth(point, index),
				tension: meta.dataset._model ? meta.dataset._model.tension : 0,
				steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false,
				// Tooltip
				hitRadius: custom.hitRadius || helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius)
			};
		},

		calculatePointY: function(value, index, datasetIndex) {
			var me = this;
			var chart = me.chart;
			var meta = me.getMeta();
			var yScale = me.getScaleForId(meta.yAxisID);
			var sumPos = 0;
			var sumNeg = 0;
			var i, ds, dsMeta;

			if (yScale.options.stacked) {
				for (i = 0; i < datasetIndex; i++) {
					ds = chart.data.datasets[i];
					dsMeta = chart.getDatasetMeta(i);
					if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) {
						var stackedRightValue = Number(yScale.getRightValue(ds.data[index]));
						if (stackedRightValue < 0) {
							sumNeg += stackedRightValue || 0;
						} else {
							sumPos += stackedRightValue || 0;
						}
					}
				}

				var rightValue = Number(yScale.getRightValue(value));
				if (rightValue < 0) {
					return yScale.getPixelForValue(sumNeg + rightValue);
				}
				return yScale.getPixelForValue(sumPos + rightValue);
			}

			return yScale.getPixelForValue(value);
		},

		updateBezierControlPoints: function() {
			var me = this;
			var meta = me.getMeta();
			var area = me.chart.chartArea;
			var points = (meta.data || []);
			var i, ilen, point, model, controlPoints;

			// Only consider points that are drawn in case the spanGaps option is used
			if (meta.dataset._model.spanGaps) {
				points = points.filter(function(pt) {
					return !pt._model.skip;
				});
			}

			function capControlPoint(pt, min, max) {
				return Math.max(Math.min(pt, max), min);
			}

			if (meta.dataset._model.cubicInterpolationMode === 'monotone') {
				helpers.splineCurveMonotone(points);
			} else {
				for (i = 0, ilen = points.length; i < ilen; ++i) {
					point = points[i];
					model = point._model;
					controlPoints = helpers.splineCurve(
						helpers.previousItem(points, i)._model,
						model,
						helpers.nextItem(points, i)._model,
						meta.dataset._model.tension
					);
					model.controlPointPreviousX = controlPoints.previous.x;
					model.controlPointPreviousY = controlPoints.previous.y;
					model.controlPointNextX = controlPoints.next.x;
					model.controlPointNextY = controlPoints.next.y;
				}
			}

			if (me.chart.options.elements.line.capBezierPoints) {
				for (i = 0, ilen = points.length; i < ilen; ++i) {
					model = points[i]._model;
					model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);
					model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
					model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);
					model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);
				}
			}
		},

		draw: function() {
			var me = this;
			var chart = me.chart;
			var meta = me.getMeta();
			var points = meta.data || [];
			var area = chart.chartArea;
			var ilen = points.length;
			var i = 0;

			helpers.canvas.clipArea(chart.ctx, area);

			if (lineEnabled(me.getDataset(), chart.options)) {
				meta.dataset.draw();
			}

			helpers.canvas.unclipArea(chart.ctx);

			// Draw the points
			for (; i < ilen; ++i) {
				points[i].draw(area);
			}
		},

		setHoverStyle: function(point) {
			// Point
			var dataset = this.chart.data.datasets[point._datasetIndex];
			var index = point._index;
			var custom = point.custom || {};
			var model = point._model;

			model.radius = custom.hoverRadius || helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
			model.backgroundColor = custom.hoverBackgroundColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
			model.borderColor = custom.hoverBorderColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
			model.borderWidth = custom.hoverBorderWidth || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
		},

		removeHoverStyle: function(point) {
			var me = this;
			var dataset = me.chart.data.datasets[point._datasetIndex];
			var index = point._index;
			var custom = point.custom || {};
			var model = point._model;

			// Compatibility: If the properties are defined with only the old name, use those values
			if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
				dataset.pointRadius = dataset.radius;
			}

			model.radius = custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius);
			model.backgroundColor = me.getPointBackgroundColor(point, index);
			model.borderColor = me.getPointBorderColor(point, index);
			model.borderWidth = me.getPointBorderWidth(point, index);
		}
	});
};

},{"25":25,"40":40,"45":45}],19:[function(require,module,exports){
'use strict';

var defaults = require(25);
var elements = require(40);
var helpers = require(45);

defaults._set('polarArea', {
	scale: {
		type: 'radialLinear',
		angleLines: {
			display: false
		},
		gridLines: {
			circular: true
		},
		pointLabels: {
			display: false
		},
		ticks: {
			beginAtZero: true
		}
	},

	// Boolean - Whether to animate the rotation of the chart
	animation: {
		animateRotate: true,
		animateScale: true
	},

	startAngle: -0.5 * Math.PI,
	legendCallback: function(chart) {
		var text = [];
		text.push('<ul class="' + chart.id + '-legend">');

		var data = chart.data;
		var datasets = data.datasets;
		var labels = data.labels;

		if (datasets.length) {
			for (var i = 0; i < datasets[0].data.length; ++i) {
				text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
				if (labels[i]) {
					text.push(labels[i]);
				}
				text.push('</li>');
			}
		}

		text.push('</ul>');
		return text.join('');
	},
	legend: {
		labels: {
			generateLabels: function(chart) {
				var data = chart.data;
				if (data.labels.length && data.datasets.length) {
					return data.labels.map(function(label, i) {
						var meta = chart.getDatasetMeta(0);
						var ds = data.datasets[0];
						var arc = meta.data[i];
						var custom = arc.custom || {};
						var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
						var arcOpts = chart.options.elements.arc;
						var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
						var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
						var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);

						return {
							text: label,
							fillStyle: fill,
							strokeStyle: stroke,
							lineWidth: bw,
							hidden: isNaN(ds.data[i]) || meta.data[i].hidden,

							// Extra data used for toggling the correct item
							index: i
						};
					});
				}
				return [];
			}
		},

		onClick: function(e, legendItem) {
			var index = legendItem.index;
			var chart = this.chart;
			var i, ilen, meta;

			for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
				meta = chart.getDatasetMeta(i);
				meta.data[index].hidden = !meta.data[index].hidden;
			}

			chart.update();
		}
	},

	// Need to override these to give a nice default
	tooltips: {
		callbacks: {
			title: function() {
				return '';
			},
			label: function(item, data) {
				return data.labels[item.index] + ': ' + item.yLabel;
			}
		}
	}
});

module.exports = function(Chart) {

	Chart.controllers.polarArea = Chart.DatasetController.extend({

		dataElementType: elements.Arc,

		linkScales: helpers.noop,

		update: function(reset) {
			var me = this;
			var chart = me.chart;
			var chartArea = chart.chartArea;
			var meta = me.getMeta();
			var opts = chart.options;
			var arcOpts = opts.elements.arc;
			var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
			chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0);
			chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
			chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();

			me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);
			me.innerRadius = me.outerRadius - chart.radiusLength;

			meta.count = me.countVisibleElements();

			helpers.each(meta.data, function(arc, index) {
				me.updateElement(arc, index, reset);
			});
		},

		updateElement: function(arc, index, reset) {
			var me = this;
			var chart = me.chart;
			var dataset = me.getDataset();
			var opts = chart.options;
			var animationOpts = opts.animation;
			var scale = chart.scale;
			var labels = chart.data.labels;

			var circumference = me.calculateCircumference(dataset.data[index]);
			var centerX = scale.xCenter;
			var centerY = scale.yCenter;

			// If there is NaN data before us, we need to calculate the starting angle correctly.
			// We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data
			var visibleCount = 0;
			var meta = me.getMeta();
			for (var i = 0; i < index; ++i) {
				if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) {
					++visibleCount;
				}
			}

			// var negHalfPI = -0.5 * Math.PI;
			var datasetStartAngle = opts.startAngle;
			var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
			var startAngle = datasetStartAngle + (circumference * visibleCount);
			var endAngle = startAngle + (arc.hidden ? 0 : circumference);

			var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);

			helpers.extend(arc, {
				// Utility
				_datasetIndex: me.index,
				_index: index,
				_scale: scale,

				// Desired view properties
				_model: {
					x: centerX,
					y: centerY,
					innerRadius: 0,
					outerRadius: reset ? resetRadius : distance,
					startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle,
					endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle,
					label: helpers.valueAtIndexOrDefault(labels, index, labels[index])
				}
			});

			// Apply border and fill style
			me.removeHoverStyle(arc);

			arc.pivot();
		},

		removeHoverStyle: function(arc) {
			Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc);
		},

		countVisibleElements: function() {
			var dataset = this.getDataset();
			var meta = this.getMeta();
			var count = 0;

			helpers.each(meta.data, function(element, index) {
				if (!isNaN(dataset.data[index]) && !element.hidden) {
					count++;
				}
			});

			return count;
		},

		calculateCircumference: function(value) {
			var count = this.getMeta().count;
			if (count > 0 && !isNaN(value)) {
				return (2 * Math.PI) / count;
			}
			return 0;
		}
	});
};

},{"25":25,"40":40,"45":45}],20:[function(require,module,exports){
'use strict';

var defaults = require(25);
var elements = require(40);
var helpers = require(45);

defaults._set('radar', {
	scale: {
		type: 'radialLinear'
	},
	elements: {
		line: {
			tension: 0 // no bezier in radar
		}
	}
});

module.exports = function(Chart) {

	Chart.controllers.radar = Chart.DatasetController.extend({

		datasetElementType: elements.Line,

		dataElementType: elements.Point,

		linkScales: helpers.noop,

		update: function(reset) {
			var me = this;
			var meta = me.getMeta();
			var line = meta.dataset;
			var points = meta.data;
			var custom = line.custom || {};
			var dataset = me.getDataset();
			var lineElementOptions = me.chart.options.elements.line;
			var scale = me.chart.scale;

			// Compatibility: If the properties are defined with only the old name, use those values
			if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
				dataset.lineTension = dataset.tension;
			}

			helpers.extend(meta.dataset, {
				// Utility
				_datasetIndex: me.index,
				_scale: scale,
				// Data
				_children: points,
				_loop: true,
				// Model
				_model: {
					// Appearance
					tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension),
					backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
					borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
					borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
					fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
					borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
					borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
					borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
					borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
				}
			});

			meta.dataset.pivot();

			// Update Points
			helpers.each(points, function(point, index) {
				me.updateElement(point, index, reset);
			}, me);

			// Update bezier control points
			me.updateBezierControlPoints();
		},
		updateElement: function(point, index, reset) {
			var me = this;
			var custom = point.custom || {};
			var dataset = me.getDataset();
			var scale = me.chart.scale;
			var pointElementOptions = me.chart.options.elements.point;
			var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]);

			// Compatibility: If the properties are defined with only the old name, use those values
			if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
				dataset.pointRadius = dataset.radius;
			}
			if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
				dataset.pointHitRadius = dataset.hitRadius;
			}

			helpers.extend(point, {
				// Utility
				_datasetIndex: me.index,
				_index: index,
				_scale: scale,

				// Desired view properties
				_model: {
					x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
					y: reset ? scale.yCenter : pointPosition.y,

					// Appearance
					tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension),
					radius: custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius),
					backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor),
					borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor),
					borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth),
					pointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle),

					// Tooltip
					hitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius)
				}
			});

			point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y));
		},
		updateBezierControlPoints: function() {
			var chartArea = this.chart.chartArea;
			var meta = this.getMeta();

			helpers.each(meta.data, function(point, index) {
				var model = point._model;
				var controlPoints = helpers.splineCurve(
					helpers.previousItem(meta.data, index, true)._model,
					model,
					helpers.nextItem(meta.data, index, true)._model,
					model.tension
				);

				// Prevent the bezier going outside of the bounds of the graph
				model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left);
				model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top);

				model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left);
				model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top);

				// Now pivot the point for animation
				point.pivot();
			});
		},

		setHoverStyle: function(point) {
			// Point
			var dataset = this.chart.data.datasets[point._datasetIndex];
			var custom = point.custom || {};
			var index = point._index;
			var model = point._model;

			model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
			model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
			model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
			model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
		},

		removeHoverStyle: function(point) {
			var dataset = this.chart.data.datasets[point._datasetIndex];
			var custom = point.custom || {};
			var index = point._index;
			var model = point._model;
			var pointElementOptions = this.chart.options.elements.point;

			model.radius = custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius);
			model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor);
			model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor);
			model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth);
		}
	});
};

},{"25":25,"40":40,"45":45}],21:[function(require,module,exports){
'use strict';

var defaults = require(25);

defaults._set('scatter', {
	hover: {
		mode: 'single'
	},

	scales: {
		xAxes: [{
			id: 'x-axis-1',    // need an ID so datasets can reference the scale
			type: 'linear',    // scatter should not use a category axis
			position: 'bottom'
		}],
		yAxes: [{
			id: 'y-axis-1',
			type: 'linear',
			position: 'left'
		}]
	},

	showLines: false,

	tooltips: {
		callbacks: {
			title: function() {
				return '';     // doesn't make sense for scatter since data are formatted as a point
			},
			label: function(item) {
				return '(' + item.xLabel + ', ' + item.yLabel + ')';
			}
		}
	}
});

module.exports = function(Chart) {

	// Scatter charts use line controllers
	Chart.controllers.scatter = Chart.controllers.line;

};

},{"25":25}],22:[function(require,module,exports){
/* global window: false */
'use strict';

var defaults = require(25);
var Element = require(26);
var helpers = require(45);

defaults._set('global', {
	animation: {
		duration: 1000,
		easing: 'easeOutQuart',
		onProgress: helpers.noop,
		onComplete: helpers.noop
	}
});

module.exports = function(Chart) {

	Chart.Animation = Element.extend({
		chart: null, // the animation associated chart instance
		currentStep: 0, // the current animation step
		numSteps: 60, // default number of steps
		easing: '', // the easing to use for this animation
		render: null, // render function used by the animation service

		onAnimationProgress: null, // user specified callback to fire on each step of the animation
		onAnimationComplete: null, // user specified callback to fire when the animation finishes
	});

	Chart.animationService = {
		frameDuration: 17,
		animations: [],
		dropFrames: 0,
		request: null,

		/**
		 * @param {Chart} chart - The chart to animate.
		 * @param {Chart.Animation} animation - The animation that we will animate.
		 * @param {Number} duration - The animation duration in ms.
		 * @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions
		 */
		addAnimation: function(chart, animation, duration, lazy) {
			var animations = this.animations;
			var i, ilen;

			animation.chart = chart;

			if (!lazy) {
				chart.animating = true;
			}

			for (i = 0, ilen = animations.length; i < ilen; ++i) {
				if (animations[i].chart === chart) {
					animations[i] = animation;
					return;
				}
			}

			animations.push(animation);

			// If there are no animations queued, manually kickstart a digest, for lack of a better word
			if (animations.length === 1) {
				this.requestAnimationFrame();
			}
		},

		cancelAnimation: function(chart) {
			var index = helpers.findIndex(this.animations, function(animation) {
				return animation.chart === chart;
			});

			if (index !== -1) {
				this.animations.splice(index, 1);
				chart.animating = false;
			}
		},

		requestAnimationFrame: function() {
			var me = this;
			if (me.request === null) {
				// Skip animation frame requests until the active one is executed.
				// This can happen when processing mouse events, e.g. 'mousemove'
				// and 'mouseout' events will trigger multiple renders.
				me.request = helpers.requestAnimFrame.call(window, function() {
					me.request = null;
					me.startDigest();
				});
			}
		},

		/**
		 * @private
		 */
		startDigest: function() {
			var me = this;
			var startTime = Date.now();
			var framesToDrop = 0;

			if (me.dropFrames > 1) {
				framesToDrop = Math.floor(me.dropFrames);
				me.dropFrames = me.dropFrames % 1;
			}

			me.advance(1 + framesToDrop);

			var endTime = Date.now();

			me.dropFrames += (endTime - startTime) / me.frameDuration;

			// Do we have more stuff to animate?
			if (me.animations.length > 0) {
				me.requestAnimationFrame();
			}
		},

		/**
		 * @private
		 */
		advance: function(count) {
			var animations = this.animations;
			var animation, chart;
			var i = 0;

			while (i < animations.length) {
				animation = animations[i];
				chart = animation.chart;

				animation.currentStep = (animation.currentStep || 0) + count;
				animation.currentStep = Math.min(animation.currentStep, animation.numSteps);

				helpers.callback(animation.render, [chart, animation], chart);
				helpers.callback(animation.onAnimationProgress, [animation], chart);

				if (animation.currentStep >= animation.numSteps) {
					helpers.callback(animation.onAnimationComplete, [animation], chart);
					chart.animating = false;
					animations.splice(i, 1);
				} else {
					++i;
				}
			}
		}
	};

	/**
	 * Provided for backward compatibility, use Chart.Animation instead
	 * @prop Chart.Animation#animationObject
	 * @deprecated since version 2.6.0
	 * @todo remove at version 3
	 */
	Object.defineProperty(Chart.Animation.prototype, 'animationObject', {
		get: function() {
			return this;
		}
	});

	/**
	 * Provided for backward compatibility, use Chart.Animation#chart instead
	 * @prop Chart.Animation#chartInstance
	 * @deprecated since version 2.6.0
	 * @todo remove at version 3
	 */
	Object.defineProperty(Chart.Animation.prototype, 'chartInstance', {
		get: function() {
			return this.chart;
		},
		set: function(value) {
			this.chart = value;
		}
	});

};

},{"25":25,"26":26,"45":45}],23:[function(require,module,exports){
'use strict';

var defaults = require(25);
var helpers = require(45);
var Interaction = require(28);
var platform = require(48);

module.exports = function(Chart) {
	var plugins = Chart.plugins;

	// Create a dictionary of chart types, to allow for extension of existing types
	Chart.types = {};

	// Store a reference to each instance - allowing us to globally resize chart instances on window resize.
	// Destroy method on the chart will remove the instance of the chart from this reference.
	Chart.instances = {};

	// Controllers available for dataset visualization eg. bar, line, slice, etc.
	Chart.controllers = {};

	/**
	 * Initializes the given config with global and chart default values.
	 */
	function initConfig(config) {
		config = config || {};

		// Do NOT use configMerge() for the data object because this method merges arrays
		// and so would change references to labels and datasets, preventing data updates.
		var data = config.data = config.data || {};
		data.datasets = data.datasets || [];
		data.labels = data.labels || [];

		config.options = helpers.configMerge(
			defaults.global,
			defaults[config.type],
			config.options || {});

		return config;
	}

	/**
	 * Updates the config of the chart
	 * @param chart {Chart} chart to update the options for
	 */
	function updateConfig(chart) {
		var newOptions = chart.options;

		// Update Scale(s) with options
		if (newOptions.scale) {
			chart.scale.options = newOptions.scale;
		} else if (newOptions.scales) {
			newOptions.scales.xAxes.concat(newOptions.scales.yAxes).forEach(function(scaleOptions) {
				chart.scales[scaleOptions.id].options = scaleOptions;
			});
		}

		// Tooltip
		chart.tooltip._options = newOptions.tooltips;
	}

	function positionIsHorizontal(position) {
		return position === 'top' || position === 'bottom';
	}

	helpers.extend(Chart.prototype, /** @lends Chart */ {
		/**
		 * @private
		 */
		construct: function(item, config) {
			var me = this;

			config = initConfig(config);

			var context = platform.acquireContext(item, config);
			var canvas = context && context.canvas;
			var height = canvas && canvas.height;
			var width = canvas && canvas.width;

			me.id = helpers.uid();
			me.ctx = context;
			me.canvas = canvas;
			me.config = config;
			me.width = width;
			me.height = height;
			me.aspectRatio = height ? width / height : null;
			me.options = config.options;
			me._bufferedRender = false;

			/**
			 * Provided for backward compatibility, Chart and Chart.Controller have been merged,
			 * the "instance" still need to be defined since it might be called from plugins.
			 * @prop Chart#chart
			 * @deprecated since version 2.6.0
			 * @todo remove at version 3
			 * @private
			 */
			me.chart = me;
			me.controller = me; // chart.chart.controller #inception

			// Add the chart instance to the global namespace
			Chart.instances[me.id] = me;

			// Define alias to the config data: `chart.data === chart.config.data`
			Object.defineProperty(me, 'data', {
				get: function() {
					return me.config.data;
				},
				set: function(value) {
					me.config.data = value;
				}
			});

			if (!context || !canvas) {
				// The given item is not a compatible context2d element, let's return before finalizing
				// the chart initialization but after setting basic chart / controller properties that
				// can help to figure out that the chart is not valid (e.g chart.canvas !== null);
				// https://github.com/chartjs/Chart.js/issues/2807
				console.error("Failed to create chart: can't acquire context from the given item");
				return;
			}

			me.initialize();
			me.update();
		},

		/**
		 * @private
		 */
		initialize: function() {
			var me = this;

			// Before init plugin notification
			plugins.notify(me, 'beforeInit');

			helpers.retinaScale(me, me.options.devicePixelRatio);

			me.bindEvents();

			if (me.options.responsive) {
				// Initial resize before chart draws (must be silent to preserve initial animations).
				me.resize(true);
			}

			// Make sure scales have IDs and are built before we build any controllers.
			me.ensureScalesHaveIDs();
			me.buildScales();
			me.initToolTip();

			// After init plugin notification
			plugins.notify(me, 'afterInit');

			return me;
		},

		clear: function() {
			helpers.canvas.clear(this);
			return this;
		},

		stop: function() {
			// Stops any current animation loop occurring
			Chart.animationService.cancelAnimation(this);
			return this;
		},

		resize: function(silent) {
			var me = this;
			var options = me.options;
			var canvas = me.canvas;
			var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null;

			// the canvas render width and height will be casted to integers so make sure that
			// the canvas display style uses the same integer values to avoid blurring effect.

			// Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collased
			var newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas)));
			var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas)));

			if (me.width === newWidth && me.height === newHeight) {
				return;
			}

			canvas.width = me.width = newWidth;
			canvas.height = me.height = newHeight;
			canvas.style.width = newWidth + 'px';
			canvas.style.height = newHeight + 'px';

			helpers.retinaScale(me, options.devicePixelRatio);

			if (!silent) {
				// Notify any plugins about the resize
				var newSize = {width: newWidth, height: newHeight};
				plugins.notify(me, 'resize', [newSize]);

				// Notify of resize
				if (me.options.onResize) {
					me.options.onResize(me, newSize);
				}

				me.stop();
				me.update(me.options.responsiveAnimationDuration);
			}
		},

		ensureScalesHaveIDs: function() {
			var options = this.options;
			var scalesOptions = options.scales || {};
			var scaleOptions = options.scale;

			helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) {
				xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index);
			});

			helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) {
				yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index);
			});

			if (scaleOptions) {
				scaleOptions.id = scaleOptions.id || 'scale';
			}
		},

		/**
		 * Builds a map of scale ID to scale object for future lookup.
		 */
		buildScales: function() {
			var me = this;
			var options = me.options;
			var scales = me.scales = {};
			var items = [];

			if (options.scales) {
				items = items.concat(
					(options.scales.xAxes || []).map(function(xAxisOptions) {
						return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'};
					}),
					(options.scales.yAxes || []).map(function(yAxisOptions) {
						return {options: yAxisOptions, dtype: 'linear', dposition: 'left'};
					})
				);
			}

			if (options.scale) {
				items.push({
					options: options.scale,
					dtype: 'radialLinear',
					isDefault: true,
					dposition: 'chartArea'
				});
			}

			helpers.each(items, function(item) {
				var scaleOptions = item.options;
				var scaleType = helpers.valueOrDefault(scaleOptions.type, item.dtype);
				var scaleClass = Chart.scaleService.getScaleConstructor(scaleType);
				if (!scaleClass) {
					return;
				}

				if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) {
					scaleOptions.position = item.dposition;
				}

				var scale = new scaleClass({
					id: scaleOptions.id,
					options: scaleOptions,
					ctx: me.ctx,
					chart: me
				});

				scales[scale.id] = scale;
				scale.mergeTicksOptions();

				// TODO(SB): I think we should be able to remove this custom case (options.scale)
				// and consider it as a regular scale part of the "scales"" map only! This would
				// make the logic easier and remove some useless? custom code.
				if (item.isDefault) {
					me.scale = scale;
				}
			});

			Chart.scaleService.addScalesToLayout(this);
		},

		buildOrUpdateControllers: function() {
			var me = this;
			var types = [];
			var newControllers = [];

			helpers.each(me.data.datasets, function(dataset, datasetIndex) {
				var meta = me.getDatasetMeta(datasetIndex);
				var type = dataset.type || me.config.type;

				if (meta.type && meta.type !== type) {
					me.destroyDatasetMeta(datasetIndex);
					meta = me.getDatasetMeta(datasetIndex);
				}
				meta.type = type;

				types.push(meta.type);

				if (meta.controller) {
					meta.controller.updateIndex(datasetIndex);
				} else {
					var ControllerClass = Chart.controllers[meta.type];
					if (ControllerClass === undefined) {
						throw new Error('"' + meta.type + '" is not a chart type.');
					}

					meta.controller = new ControllerClass(me, datasetIndex);
					newControllers.push(meta.controller);
				}
			}, me);

			return newControllers;
		},

		/**
		 * Reset the elements of all datasets
		 * @private
		 */
		resetElements: function() {
			var me = this;
			helpers.each(me.data.datasets, function(dataset, datasetIndex) {
				me.getDatasetMeta(datasetIndex).controller.reset();
			}, me);
		},

		/**
		* Resets the chart back to it's state before the initial animation
		*/
		reset: function() {
			this.resetElements();
			this.tooltip.initialize();
		},

		update: function(config) {
			var me = this;

			if (!config || typeof config !== 'object') {
				// backwards compatibility
				config = {
					duration: config,
					lazy: arguments[1]
				};
			}

			updateConfig(me);

			if (plugins.notify(me, 'beforeUpdate') === false) {
				return;
			}

			// In case the entire data object changed
			me.tooltip._data = me.data;

			// Make sure dataset controllers are updated and new controllers are reset
			var newControllers = me.buildOrUpdateControllers();

			// Make sure all dataset controllers have correct meta data counts
			helpers.each(me.data.datasets, function(dataset, datasetIndex) {
				me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements();
			}, me);

			me.updateLayout();

			// Can only reset the new controllers after the scales have been updated
			helpers.each(newControllers, function(controller) {
				controller.reset();
			});

			me.updateDatasets();

			// Do this before render so that any plugins that need final scale updates can use it
			plugins.notify(me, 'afterUpdate');

			if (me._bufferedRender) {
				me._bufferedRequest = {
					duration: config.duration,
					easing: config.easing,
					lazy: config.lazy
				};
			} else {
				me.render(config);
			}
		},

		/**
		 * Updates the chart layout unless a plugin returns `false` to the `beforeLayout`
		 * hook, in which case, plugins will not be called on `afterLayout`.
		 * @private
		 */
		updateLayout: function() {
			var me = this;

			if (plugins.notify(me, 'beforeLayout') === false) {
				return;
			}

			Chart.layoutService.update(this, this.width, this.height);

			/**
			 * Provided for backward compatibility, use `afterLayout` instead.
			 * @method IPlugin#afterScaleUpdate
			 * @deprecated since version 2.5.0
			 * @todo remove at version 3
			 * @private
			 */
			plugins.notify(me, 'afterScaleUpdate');
			plugins.notify(me, 'afterLayout');
		},

		/**
		 * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate`
		 * hook, in which case, plugins will not be called on `afterDatasetsUpdate`.
		 * @private
		 */
		updateDatasets: function() {
			var me = this;

			if (plugins.notify(me, 'beforeDatasetsUpdate') === false) {
				return;
			}

			for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
				me.updateDataset(i);
			}

			plugins.notify(me, 'afterDatasetsUpdate');
		},

		/**
		 * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate`
		 * hook, in which case, plugins will not be called on `afterDatasetUpdate`.
		 * @private
		 */
		updateDataset: function(index) {
			var me = this;
			var meta = me.getDatasetMeta(index);
			var args = {
				meta: meta,
				index: index
			};

			if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) {
				return;
			}

			meta.controller.update();

			plugins.notify(me, 'afterDatasetUpdate', [args]);
		},

		render: function(config) {
			var me = this;

			if (!config || typeof config !== 'object') {
				// backwards compatibility
				config = {
					duration: config,
					lazy: arguments[1]
				};
			}

			var duration = config.duration;
			var lazy = config.lazy;

			if (plugins.notify(me, 'beforeRender') === false) {
				return;
			}

			var animationOptions = me.options.animation;
			var onComplete = function(animation) {
				plugins.notify(me, 'afterRender');
				helpers.callback(animationOptions && animationOptions.onComplete, [animation], me);
			};

			if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) {
				var animation = new Chart.Animation({
					numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps
					easing: config.easing || animationOptions.easing,

					render: function(chart, animationObject) {
						var easingFunction = helpers.easing.effects[animationObject.easing];
						var currentStep = animationObject.currentStep;
						var stepDecimal = currentStep / animationObject.numSteps;

						chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep);
					},

					onAnimationProgress: animationOptions.onProgress,
					onAnimationComplete: onComplete
				});

				Chart.animationService.addAnimation(me, animation, duration, lazy);
			} else {
				me.draw();

				// See https://github.com/chartjs/Chart.js/issues/3781
				onComplete(new Chart.Animation({numSteps: 0, chart: me}));
			}

			return me;
		},

		draw: function(easingValue) {
			var me = this;

			me.clear();

			if (helpers.isNullOrUndef(easingValue)) {
				easingValue = 1;
			}

			me.transition(easingValue);

			if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) {
				return;
			}

			// Draw all the scales
			helpers.each(me.boxes, function(box) {
				box.draw(me.chartArea);
			}, me);

			if (me.scale) {
				me.scale.draw();
			}

			me.drawDatasets(easingValue);

			// Finally draw the tooltip
			me.tooltip.draw();

			plugins.notify(me, 'afterDraw', [easingValue]);
		},

		/**
		 * @private
		 */
		transition: function(easingValue) {
			var me = this;

			for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) {
				if (me.isDatasetVisible(i)) {
					me.getDatasetMeta(i).controller.transition(easingValue);
				}
			}

			me.tooltip.transition(easingValue);
		},

		/**
		 * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw`
		 * hook, in which case, plugins will not be called on `afterDatasetsDraw`.
		 * @private
		 */
		drawDatasets: function(easingValue) {
			var me = this;

			if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) {
				return;
			}

			// Draw datasets reversed to support proper line stacking
			for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) {
				if (me.isDatasetVisible(i)) {
					me.drawDataset(i, easingValue);
				}
			}

			plugins.notify(me, 'afterDatasetsDraw', [easingValue]);
		},

		/**
		 * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw`
		 * hook, in which case, plugins will not be called on `afterDatasetDraw`.
		 * @private
		 */
		drawDataset: function(index, easingValue) {
			var me = this;
			var meta = me.getDatasetMeta(index);
			var args = {
				meta: meta,
				index: index,
				easingValue: easingValue
			};

			if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) {
				return;
			}

			meta.controller.draw(easingValue);

			plugins.notify(me, 'afterDatasetDraw', [args]);
		},

		// Get the single element that was clicked on
		// @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
		getElementAtEvent: function(e) {
			return Interaction.modes.single(this, e);
		},

		getElementsAtEvent: function(e) {
			return Interaction.modes.label(this, e, {intersect: true});
		},

		getElementsAtXAxis: function(e) {
			return Interaction.modes['x-axis'](this, e, {intersect: true});
		},

		getElementsAtEventForMode: function(e, mode, options) {
			var method = Interaction.modes[mode];
			if (typeof method === 'function') {
				return method(this, e, options);
			}

			return [];
		},

		getDatasetAtEvent: function(e) {
			return Interaction.modes.dataset(this, e, {intersect: true});
		},

		getDatasetMeta: function(datasetIndex) {
			var me = this;
			var dataset = me.data.datasets[datasetIndex];
			if (!dataset._meta) {
				dataset._meta = {};
			}

			var meta = dataset._meta[me.id];
			if (!meta) {
				meta = dataset._meta[me.id] = {
					type: null,
					data: [],
					dataset: null,
					controller: null,
					hidden: null,			// See isDatasetVisible() comment
					xAxisID: null,
					yAxisID: null
				};
			}

			return meta;
		},

		getVisibleDatasetCount: function() {
			var count = 0;
			for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
				if (this.isDatasetVisible(i)) {
					count++;
				}
			}
			return count;
		},

		isDatasetVisible: function(datasetIndex) {
			var meta = this.getDatasetMeta(datasetIndex);

			// meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false,
			// the dataset.hidden value is ignored, else if null, the dataset hidden state is returned.
			return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden;
		},

		generateLegend: function() {
			return this.options.legendCallback(this);
		},

		/**
		 * @private
		 */
		destroyDatasetMeta: function(datasetIndex) {
			var id = this.id;
			var dataset = this.data.datasets[datasetIndex];
			var meta = dataset._meta && dataset._meta[id];

			if (meta) {
				meta.controller.destroy();
				delete dataset._meta[id];
			}
		},

		destroy: function() {
			var me = this;
			var canvas = me.canvas;
			var i, ilen;

			me.stop();

			// dataset controllers need to cleanup associated data
			for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
				me.destroyDatasetMeta(i);
			}

			if (canvas) {
				me.unbindEvents();
				helpers.canvas.clear(me);
				platform.releaseContext(me.ctx);
				me.canvas = null;
				me.ctx = null;
			}

			plugins.notify(me, 'destroy');

			delete Chart.instances[me.id];
		},

		toBase64Image: function() {
			return this.canvas.toDataURL.apply(this.canvas, arguments);
		},

		initToolTip: function() {
			var me = this;
			me.tooltip = new Chart.Tooltip({
				_chart: me,
				_chartInstance: me, // deprecated, backward compatibility
				_data: me.data,
				_options: me.options.tooltips
			}, me);
		},

		/**
		 * @private
		 */
		bindEvents: function() {
			var me = this;
			var listeners = me._listeners = {};
			var listener = function() {
				me.eventHandler.apply(me, arguments);
			};

			helpers.each(me.options.events, function(type) {
				platform.addEventListener(me, type, listener);
				listeners[type] = listener;
			});

			// Elements used to detect size change should not be injected for non responsive charts.
			// See https://github.com/chartjs/Chart.js/issues/2210
			if (me.options.responsive) {
				listener = function() {
					me.resize();
				};

				platform.addEventListener(me, 'resize', listener);
				listeners.resize = listener;
			}
		},

		/**
		 * @private
		 */
		unbindEvents: function() {
			var me = this;
			var listeners = me._listeners;
			if (!listeners) {
				return;
			}

			delete me._listeners;
			helpers.each(listeners, function(listener, type) {
				platform.removeEventListener(me, type, listener);
			});
		},

		updateHoverStyle: function(elements, mode, enabled) {
			var method = enabled ? 'setHoverStyle' : 'removeHoverStyle';
			var element, i, ilen;

			for (i = 0, ilen = elements.length; i < ilen; ++i) {
				element = elements[i];
				if (element) {
					this.getDatasetMeta(element._datasetIndex).controller[method](element);
				}
			}
		},

		/**
		 * @private
		 */
		eventHandler: function(e) {
			var me = this;
			var tooltip = me.tooltip;

			if (plugins.notify(me, 'beforeEvent', [e]) === false) {
				return;
			}

			// Buffer any update calls so that renders do not occur
			me._bufferedRender = true;
			me._bufferedRequest = null;

			var changed = me.handleEvent(e);
			changed |= tooltip && tooltip.handleEvent(e);

			plugins.notify(me, 'afterEvent', [e]);

			var bufferedRequest = me._bufferedRequest;
			if (bufferedRequest) {
				// If we have an update that was triggered, we need to do a normal render
				me.render(bufferedRequest);
			} else if (changed && !me.animating) {
				// If entering, leaving, or changing elements, animate the change via pivot
				me.stop();

				// We only need to render at this point. Updating will cause scales to be
				// recomputed generating flicker & using more memory than necessary.
				me.render(me.options.hover.animationDuration, true);
			}

			me._bufferedRender = false;
			me._bufferedRequest = null;

			return me;
		},

		/**
		 * Handle an event
		 * @private
		 * @param {IEvent} event the event to handle
		 * @return {Boolean} true if the chart needs to re-render
		 */
		handleEvent: function(e) {
			var me = this;
			var options = me.options || {};
			var hoverOptions = options.hover;
			var changed = false;

			me.lastActive = me.lastActive || [];

			// Find Active Elements for hover and tooltips
			if (e.type === 'mouseout') {
				me.active = [];
			} else {
				me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions);
			}

			// Invoke onHover hook
			// Need to call with native event here to not break backwards compatibility
			helpers.callback(options.onHover || options.hover.onHover, [e.native, me.active], me);

			if (e.type === 'mouseup' || e.type === 'click') {
				if (options.onClick) {
					// Use e.native here for backwards compatibility
					options.onClick.call(me, e.native, me.active);
				}
			}

			// Remove styling for last active (even if it may still be active)
			if (me.lastActive.length) {
				me.updateHoverStyle(me.lastActive, hoverOptions.mode, false);
			}

			// Built in hover styling
			if (me.active.length && hoverOptions.mode) {
				me.updateHoverStyle(me.active, hoverOptions.mode, true);
			}

			changed = !helpers.arrayEquals(me.active, me.lastActive);

			// Remember Last Actives
			me.lastActive = me.active;

			return changed;
		}
	});

	/**
	 * Provided for backward compatibility, use Chart instead.
	 * @class Chart.Controller
	 * @deprecated since version 2.6.0
	 * @todo remove at version 3
	 * @private
	 */
	Chart.Controller = Chart;
};

},{"25":25,"28":28,"45":45,"48":48}],24:[function(require,module,exports){
'use strict';

var helpers = require(45);

module.exports = function(Chart) {

	var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];

	/**
	 * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',
	 * 'unshift') and notify the listener AFTER the array has been altered. Listeners are
	 * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments.
	 */
	function listenArrayEvents(array, listener) {
		if (array._chartjs) {
			array._chartjs.listeners.push(listener);
			return;
		}

		Object.defineProperty(array, '_chartjs', {
			configurable: true,
			enumerable: false,
			value: {
				listeners: [listener]
			}
		});

		arrayEvents.forEach(function(key) {
			var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1);
			var base = array[key];

			Object.defineProperty(array, key, {
				configurable: true,
				enumerable: false,
				value: function() {
					var args = Array.prototype.slice.call(arguments);
					var res = base.apply(this, args);

					helpers.each(array._chartjs.listeners, function(object) {
						if (typeof object[method] === 'function') {
							object[method].apply(object, args);
						}
					});

					return res;
				}
			});
		});
	}

	/**
	 * Removes the given array event listener and cleanup extra attached properties (such as
	 * the _chartjs stub and overridden methods) if array doesn't have any more listeners.
	 */
	function unlistenArrayEvents(array, listener) {
		var stub = array._chartjs;
		if (!stub) {
			return;
		}

		var listeners = stub.listeners;
		var index = listeners.indexOf(listener);
		if (index !== -1) {
			listeners.splice(index, 1);
		}

		if (listeners.length > 0) {
			return;
		}

		arrayEvents.forEach(function(key) {
			delete array[key];
		});

		delete array._chartjs;
	}

	// Base class for all dataset controllers (line, bar, etc)
	Chart.DatasetController = function(chart, datasetIndex) {
		this.initialize(chart, datasetIndex);
	};

	helpers.extend(Chart.DatasetController.prototype, {

		/**
		 * Element type used to generate a meta dataset (e.g. Chart.element.Line).
		 * @type {Chart.core.element}
		 */
		datasetElementType: null,

		/**
		 * Element type used to generate a meta data (e.g. Chart.element.Point).
		 * @type {Chart.core.element}
		 */
		dataElementType: null,

		initialize: function(chart, datasetIndex) {
			var me = this;
			me.chart = chart;
			me.index = datasetIndex;
			me.linkScales();
			me.addElements();
		},

		updateIndex: function(datasetIndex) {
			this.index = datasetIndex;
		},

		linkScales: function() {
			var me = this;
			var meta = me.getMeta();
			var dataset = me.getDataset();

			if (meta.xAxisID === null) {
				meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id;
			}
			if (meta.yAxisID === null) {
				meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id;
			}
		},

		getDataset: function() {
			return this.chart.data.datasets[this.index];
		},

		getMeta: function() {
			return this.chart.getDatasetMeta(this.index);
		},

		getScaleForId: function(scaleID) {
			return this.chart.scales[scaleID];
		},

		reset: function() {
			this.update(true);
		},

		/**
		 * @private
		 */
		destroy: function() {
			if (this._data) {
				unlistenArrayEvents(this._data, this);
			}
		},

		createMetaDataset: function() {
			var me = this;
			var type = me.datasetElementType;
			return type && new type({
				_chart: me.chart,
				_datasetIndex: me.index
			});
		},

		createMetaData: function(index) {
			var me = this;
			var type = me.dataElementType;
			return type && new type({
				_chart: me.chart,
				_datasetIndex: me.index,
				_index: index
			});
		},

		addElements: function() {
			var me = this;
			var meta = me.getMeta();
			var data = me.getDataset().data || [];
			var metaData = meta.data;
			var i, ilen;

			for (i = 0, ilen = data.length; i < ilen; ++i) {
				metaData[i] = metaData[i] || me.createMetaData(i);
			}

			meta.dataset = meta.dataset || me.createMetaDataset();
		},

		addElementAndReset: function(index) {
			var element = this.createMetaData(index);
			this.getMeta().data.splice(index, 0, element);
			this.updateElement(element, index, true);
		},

		buildOrUpdateElements: function() {
			var me = this;
			var dataset = me.getDataset();
			var data = dataset.data || (dataset.data = []);

			// In order to correctly handle data addition/deletion animation (an thus simulate
			// real-time charts), we need to monitor these data modifications and synchronize
			// the internal meta data accordingly.
			if (me._data !== data) {
				if (me._data) {
					// This case happens when the user replaced the data array instance.
					unlistenArrayEvents(me._data, me);
				}

				listenArrayEvents(data, me);
				me._data = data;
			}

			// Re-sync meta data in case the user replaced the data array or if we missed
			// any updates and so make sure that we handle number of datapoints changing.
			me.resyncElements();
		},

		update: helpers.noop,

		transition: function(easingValue) {
			var meta = this.getMeta();
			var elements = meta.data || [];
			var ilen = elements.length;
			var i = 0;

			for (; i < ilen; ++i) {
				elements[i].transition(easingValue);
			}

			if (meta.dataset) {
				meta.dataset.transition(easingValue);
			}
		},

		draw: function() {
			var meta = this.getMeta();
			var elements = meta.data || [];
			var ilen = elements.length;
			var i = 0;

			if (meta.dataset) {
				meta.dataset.draw();
			}

			for (; i < ilen; ++i) {
				elements[i].draw();
			}
		},

		removeHoverStyle: function(element, elementOpts) {
			var dataset = this.chart.data.datasets[element._datasetIndex];
			var index = element._index;
			var custom = element.custom || {};
			var valueOrDefault = helpers.valueAtIndexOrDefault;
			var model = element._model;

			model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
			model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
			model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
		},

		setHoverStyle: function(element) {
			var dataset = this.chart.data.datasets[element._datasetIndex];
			var index = element._index;
			var custom = element.custom || {};
			var valueOrDefault = helpers.valueAtIndexOrDefault;
			var getHoverColor = helpers.getHoverColor;
			var model = element._model;

			model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor));
			model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor));
			model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
		},

		/**
		 * @private
		 */
		resyncElements: function() {
			var me = this;
			var meta = me.getMeta();
			var data = me.getDataset().data;
			var numMeta = meta.data.length;
			var numData = data.length;

			if (numData < numMeta) {
				meta.data.splice(numData, numMeta - numData);
			} else if (numData > numMeta) {
				me.insertElements(numMeta, numData - numMeta);
			}
		},

		/**
		 * @private
		 */
		insertElements: function(start, count) {
			for (var i = 0; i < count; ++i) {
				this.addElementAndReset(start + i);
			}
		},

		/**
		 * @private
		 */
		onDataPush: function() {
			this.insertElements(this.getDataset().data.length - 1, arguments.length);
		},

		/**
		 * @private
		 */
		onDataPop: function() {
			this.getMeta().data.pop();
		},

		/**
		 * @private
		 */
		onDataShift: function() {
			this.getMeta().data.shift();
		},

		/**
		 * @private
		 */
		onDataSplice: function(start, count) {
			this.getMeta().data.splice(start, count);
			this.insertElements(start, arguments.length - 2);
		},

		/**
		 * @private
		 */
		onDataUnshift: function() {
			this.insertElements(0, arguments.length);
		}
	});

	Chart.DatasetController.extend = helpers.inherits;
};

},{"45":45}],25:[function(require,module,exports){
'use strict';

var helpers = require(45);

module.exports = {
	/**
	 * @private
	 */
	_set: function(scope, values) {
		return helpers.merge(this[scope] || (this[scope] = {}), values);
	}
};

},{"45":45}],26:[function(require,module,exports){
'use strict';

var color = require(2);
var helpers = require(45);

function interpolate(start, view, model, ease) {
	var keys = Object.keys(model);
	var i, ilen, key, actual, origin, target, type, c0, c1;

	for (i = 0, ilen = keys.length; i < ilen; ++i) {
		key = keys[i];

		target = model[key];

		// if a value is added to the model after pivot() has been called, the view
		// doesn't contain it, so let's initialize the view to the target value.
		if (!view.hasOwnProperty(key)) {
			view[key] = target;
		}

		actual = view[key];

		if (actual === target || key[0] === '_') {
			continue;
		}

		if (!start.hasOwnProperty(key)) {
			start[key] = actual;
		}

		origin = start[key];

		type = typeof target;

		if (type === typeof origin) {
			if (type === 'string') {
				c0 = color(origin);
				if (c0.valid) {
					c1 = color(target);
					if (c1.valid) {
						view[key] = c1.mix(c0, ease).rgbString();
						continue;
					}
				}
			} else if (type === 'number' && isFinite(origin) && isFinite(target)) {
				view[key] = origin + (target - origin) * ease;
				continue;
			}
		}

		view[key] = target;
	}
}

var Element = function(configuration) {
	helpers.extend(this, configuration);
	this.initialize.apply(this, arguments);
};

helpers.extend(Element.prototype, {

	initialize: function() {
		this.hidden = false;
	},

	pivot: function() {
		var me = this;
		if (!me._view) {
			me._view = helpers.clone(me._model);
		}
		me._start = {};
		return me;
	},

	transition: function(ease) {
		var me = this;
		var model = me._model;
		var start = me._start;
		var view = me._view;

		// No animation -> No Transition
		if (!model || ease === 1) {
			me._view = model;
			me._start = null;
			return me;
		}

		if (!view) {
			view = me._view = {};
		}

		if (!start) {
			start = me._start = {};
		}

		interpolate(start, view, model, ease);

		return me;
	},

	tooltipPosition: function() {
		return {
			x: this._model.x,
			y: this._model.y
		};
	},

	hasValue: function() {
		return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y);
	}
});

Element.extend = helpers.inherits;

module.exports = Element;

},{"2":2,"45":45}],27:[function(require,module,exports){
/* global window: false */
/* global document: false */
'use strict';

var color = require(2);
var defaults = require(25);
var helpers = require(45);

module.exports = function(Chart) {

	// -- Basic js utility methods

	helpers.extend = function(base) {
		var setFn = function(value, key) {
			base[key] = value;
		};
		for (var i = 1, ilen = arguments.length; i < ilen; i++) {
			helpers.each(arguments[i], setFn);
		}
		return base;
	};

	helpers.configMerge = function(/* objects ... */) {
		return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), {
			merger: function(key, target, source, options) {
				var tval = target[key] || {};
				var sval = source[key];

				if (key === 'scales') {
					// scale config merging is complex. Add our own function here for that
					target[key] = helpers.scaleMerge(tval, sval);
				} else if (key === 'scale') {
					// used in polar area & radar charts since there is only one scale
					target[key] = helpers.merge(tval, [Chart.scaleService.getScaleDefaults(sval.type), sval]);
				} else {
					helpers._merger(key, target, source, options);
				}
			}
		});
	};

	helpers.scaleMerge = function(/* objects ... */) {
		return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), {
			merger: function(key, target, source, options) {
				if (key === 'xAxes' || key === 'yAxes') {
					var slen = source[key].length;
					var i, type, scale;

					if (!target[key]) {
						target[key] = [];
					}

					for (i = 0; i < slen; ++i) {
						scale = source[key][i];
						type = helpers.valueOrDefault(scale.type, key === 'xAxes' ? 'category' : 'linear');

						if (i >= target[key].length) {
							target[key].push({});
						}

						if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) {
							// new/untyped scale or type changed: let's apply the new defaults
							// then merge source scale to correctly overwrite the defaults.
							helpers.merge(target[key][i], [Chart.scaleService.getScaleDefaults(type), scale]);
						} else {
							// scales type are the same
							helpers.merge(target[key][i], scale);
						}
					}
				} else {
					helpers._merger(key, target, source, options);
				}
			}
		});
	};

	helpers.where = function(collection, filterCallback) {
		if (helpers.isArray(collection) && Array.prototype.filter) {
			return collection.filter(filterCallback);
		}
		var filtered = [];

		helpers.each(collection, function(item) {
			if (filterCallback(item)) {
				filtered.push(item);
			}
		});

		return filtered;
	};
	helpers.findIndex = Array.prototype.findIndex ?
		function(array, callback, scope) {
			return array.findIndex(callback, scope);
		} :
		function(array, callback, scope) {
			scope = scope === undefined ? array : scope;
			for (var i = 0, ilen = array.length; i < ilen; ++i) {
				if (callback.call(scope, array[i], i, array)) {
					return i;
				}
			}
			return -1;
		};
	helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {
		// Default to start of the array
		if (helpers.isNullOrUndef(startIndex)) {
			startIndex = -1;
		}
		for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
			var currentItem = arrayToSearch[i];
			if (filterCallback(currentItem)) {
				return currentItem;
			}
		}
	};
	helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {
		// Default to end of the array
		if (helpers.isNullOrUndef(startIndex)) {
			startIndex = arrayToSearch.length;
		}
		for (var i = startIndex - 1; i >= 0; i--) {
			var currentItem = arrayToSearch[i];
			if (filterCallback(currentItem)) {
				return currentItem;
			}
		}
	};
	helpers.inherits = function(extensions) {
		// Basic javascript inheritance based on the model created in Backbone.js
		var me = this;
		var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() {
			return me.apply(this, arguments);
		};

		var Surrogate = function() {
			this.constructor = ChartElement;
		};
		Surrogate.prototype = me.prototype;
		ChartElement.prototype = new Surrogate();

		ChartElement.extend = helpers.inherits;

		if (extensions) {
			helpers.extend(ChartElement.prototype, extensions);
		}

		ChartElement.__super__ = me.prototype;

		return ChartElement;
	};
	// -- Math methods
	helpers.isNumber = function(n) {
		return !isNaN(parseFloat(n)) && isFinite(n);
	};
	helpers.almostEquals = function(x, y, epsilon) {
		return Math.abs(x - y) < epsilon;
	};
	helpers.almostWhole = function(x, epsilon) {
		var rounded = Math.round(x);
		return (((rounded - epsilon) < x) && ((rounded + epsilon) > x));
	};
	helpers.max = function(array) {
		return array.reduce(function(max, value) {
			if (!isNaN(value)) {
				return Math.max(max, value);
			}
			return max;
		}, Number.NEGATIVE_INFINITY);
	};
	helpers.min = function(array) {
		return array.reduce(function(min, value) {
			if (!isNaN(value)) {
				return Math.min(min, value);
			}
			return min;
		}, Number.POSITIVE_INFINITY);
	};
	helpers.sign = Math.sign ?
		function(x) {
			return Math.sign(x);
		} :
		function(x) {
			x = +x; // convert to a number
			if (x === 0 || isNaN(x)) {
				return x;
			}
			return x > 0 ? 1 : -1;
		};
	helpers.log10 = Math.log10 ?
		function(x) {
			return Math.log10(x);
		} :
		function(x) {
			return Math.log(x) / Math.LN10;
		};
	helpers.toRadians = function(degrees) {
		return degrees * (Math.PI / 180);
	};
	helpers.toDegrees = function(radians) {
		return radians * (180 / Math.PI);
	};
	// Gets the angle from vertical upright to the point about a centre.
	helpers.getAngleFromPoint = function(centrePoint, anglePoint) {
		var distanceFromXCenter = anglePoint.x - centrePoint.x;
		var distanceFromYCenter = anglePoint.y - centrePoint.y;
		var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);

		var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);

		if (angle < (-0.5 * Math.PI)) {
			angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
		}

		return {
			angle: angle,
			distance: radialDistanceFromCenter
		};
	};
	helpers.distanceBetweenPoints = function(pt1, pt2) {
		return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
	};
	helpers.aliasPixel = function(pixelWidth) {
		return (pixelWidth % 2 === 0) ? 0 : 0.5;
	};
	helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) {
		// Props to Rob Spencer at scaled innovation for his post on splining between points
		// http://scaledinnovation.com/analytics/splines/aboutSplines.html

		// This function must also respect "skipped" points

		var previous = firstPoint.skip ? middlePoint : firstPoint;
		var current = middlePoint;
		var next = afterPoint.skip ? middlePoint : afterPoint;

		var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2));
		var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2));

		var s01 = d01 / (d01 + d12);
		var s12 = d12 / (d01 + d12);

		// If all points are the same, s01 & s02 will be inf
		s01 = isNaN(s01) ? 0 : s01;
		s12 = isNaN(s12) ? 0 : s12;

		var fa = t * s01; // scaling factor for triangle Ta
		var fb = t * s12;

		return {
			previous: {
				x: current.x - fa * (next.x - previous.x),
				y: current.y - fa * (next.y - previous.y)
			},
			next: {
				x: current.x + fb * (next.x - previous.x),
				y: current.y + fb * (next.y - previous.y)
			}
		};
	};
	helpers.EPSILON = Number.EPSILON || 1e-14;
	helpers.splineCurveMonotone = function(points) {
		// This function calculates Bézier control points in a similar way than |splineCurve|,
		// but preserves monotonicity of the provided data and ensures no local extremums are added
		// between the dataset discrete points due to the interpolation.
		// See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation

		var pointsWithTangents = (points || []).map(function(point) {
			return {
				model: point._model,
				deltaK: 0,
				mK: 0
			};
		});

		// Calculate slopes (deltaK) and initialize tangents (mK)
		var pointsLen = pointsWithTangents.length;
		var i, pointBefore, pointCurrent, pointAfter;
		for (i = 0; i < pointsLen; ++i) {
			pointCurrent = pointsWithTangents[i];
			if (pointCurrent.model.skip) {
				continue;
			}

			pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
			pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
			if (pointAfter && !pointAfter.model.skip) {
				var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x);

				// In the case of two points that appear at the same x pixel, slopeDeltaX is 0
				pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0;
			}

			if (!pointBefore || pointBefore.model.skip) {
				pointCurrent.mK = pointCurrent.deltaK;
			} else if (!pointAfter || pointAfter.model.skip) {
				pointCurrent.mK = pointBefore.deltaK;
			} else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) {
				pointCurrent.mK = 0;
			} else {
				pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2;
			}
		}

		// Adjust tangents to ensure monotonic properties
		var alphaK, betaK, tauK, squaredMagnitude;
		for (i = 0; i < pointsLen - 1; ++i) {
			pointCurrent = pointsWithTangents[i];
			pointAfter = pointsWithTangents[i + 1];
			if (pointCurrent.model.skip || pointAfter.model.skip) {
				continue;
			}

			if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) {
				pointCurrent.mK = pointAfter.mK = 0;
				continue;
			}

			alphaK = pointCurrent.mK / pointCurrent.deltaK;
			betaK = pointAfter.mK / pointCurrent.deltaK;
			squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
			if (squaredMagnitude <= 9) {
				continue;
			}

			tauK = 3 / Math.sqrt(squaredMagnitude);
			pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK;
			pointAfter.mK = betaK * tauK * pointCurrent.deltaK;
		}

		// Compute control points
		var deltaX;
		for (i = 0; i < pointsLen; ++i) {
			pointCurrent = pointsWithTangents[i];
			if (pointCurrent.model.skip) {
				continue;
			}

			pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
			pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
			if (pointBefore && !pointBefore.model.skip) {
				deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3;
				pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX;
				pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK;
			}
			if (pointAfter && !pointAfter.model.skip) {
				deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3;
				pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX;
				pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK;
			}
		}
	};
	helpers.nextItem = function(collection, index, loop) {
		if (loop) {
			return index >= collection.length - 1 ? collection[0] : collection[index + 1];
		}
		return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1];
	};
	helpers.previousItem = function(collection, index, loop) {
		if (loop) {
			return index <= 0 ? collection[collection.length - 1] : collection[index - 1];
		}
		return index <= 0 ? collection[0] : collection[index - 1];
	};
	// Implementation of the nice number algorithm used in determining where axis labels will go
	helpers.niceNum = function(range, round) {
		var exponent = Math.floor(helpers.log10(range));
		var fraction = range / Math.pow(10, exponent);
		var niceFraction;

		if (round) {
			if (fraction < 1.5) {
				niceFraction = 1;
			} else if (fraction < 3) {
				niceFraction = 2;
			} else if (fraction < 7) {
				niceFraction = 5;
			} else {
				niceFraction = 10;
			}
		} else if (fraction <= 1.0) {
			niceFraction = 1;
		} else if (fraction <= 2) {
			niceFraction = 2;
		} else if (fraction <= 5) {
			niceFraction = 5;
		} else {
			niceFraction = 10;
		}

		return niceFraction * Math.pow(10, exponent);
	};
	// Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
	helpers.requestAnimFrame = (function() {
		if (typeof window === 'undefined') {
			return function(callback) {
				callback();
			};
		}
		return window.requestAnimationFrame ||
			window.webkitRequestAnimationFrame ||
			window.mozRequestAnimationFrame ||
			window.oRequestAnimationFrame ||
			window.msRequestAnimationFrame ||
			function(callback) {
				return window.setTimeout(callback, 1000 / 60);
			};
	}());
	// -- DOM methods
	helpers.getRelativePosition = function(evt, chart) {
		var mouseX, mouseY;
		var e = evt.originalEvent || evt;
		var canvas = evt.currentTarget || evt.srcElement;
		var boundingRect = canvas.getBoundingClientRect();

		var touches = e.touches;
		if (touches && touches.length > 0) {
			mouseX = touches[0].clientX;
			mouseY = touches[0].clientY;

		} else {
			mouseX = e.clientX;
			mouseY = e.clientY;
		}

		// Scale mouse coordinates into canvas coordinates
		// by following the pattern laid out by 'jerryj' in the comments of
		// http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
		var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left'));
		var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top'));
		var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right'));
		var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom'));
		var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight;
		var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom;

		// We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However
		// the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here
		mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio);
		mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio);

		return {
			x: mouseX,
			y: mouseY
		};

	};

	// Private helper function to convert max-width/max-height values that may be percentages into a number
	function parseMaxStyle(styleValue, node, parentProperty) {
		var valueInPixels;
		if (typeof styleValue === 'string') {
			valueInPixels = parseInt(styleValue, 10);

			if (styleValue.indexOf('%') !== -1) {
				// percentage * size in dimension
				valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
			}
		} else {
			valueInPixels = styleValue;
		}

		return valueInPixels;
	}

	/**
	 * Returns if the given value contains an effective constraint.
	 * @private
	 */
	function isConstrainedValue(value) {
		return value !== undefined && value !== null && value !== 'none';
	}

	// Private helper to get a constraint dimension
	// @param domNode : the node to check the constraint on
	// @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight)
	// @param percentageProperty : property of parent to use when calculating width as a percentage
	// @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser
	function getConstraintDimension(domNode, maxStyle, percentageProperty) {
		var view = document.defaultView;
		var parentNode = domNode.parentNode;
		var constrainedNode = view.getComputedStyle(domNode)[maxStyle];
		var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle];
		var hasCNode = isConstrainedValue(constrainedNode);
		var hasCContainer = isConstrainedValue(constrainedContainer);
		var infinity = Number.POSITIVE_INFINITY;

		if (hasCNode || hasCContainer) {
			return Math.min(
				hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity,
				hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity);
		}

		return 'none';
	}
	// returns Number or undefined if no constraint
	helpers.getConstraintWidth = function(domNode) {
		return getConstraintDimension(domNode, 'max-width', 'clientWidth');
	};
	// returns Number or undefined if no constraint
	helpers.getConstraintHeight = function(domNode) {
		return getConstraintDimension(domNode, 'max-height', 'clientHeight');
	};
	helpers.getMaximumWidth = function(domNode) {
		var container = domNode.parentNode;
		if (!container) {
			return domNode.clientWidth;
		}

		var paddingLeft = parseInt(helpers.getStyle(container, 'padding-left'), 10);
		var paddingRight = parseInt(helpers.getStyle(container, 'padding-right'), 10);
		var w = container.clientWidth - paddingLeft - paddingRight;
		var cw = helpers.getConstraintWidth(domNode);
		return isNaN(cw) ? w : Math.min(w, cw);
	};
	helpers.getMaximumHeight = function(domNode) {
		var container = domNode.parentNode;
		if (!container) {
			return domNode.clientHeight;
		}

		var paddingTop = parseInt(helpers.getStyle(container, 'padding-top'), 10);
		var paddingBottom = parseInt(helpers.getStyle(container, 'padding-bottom'), 10);
		var h = container.clientHeight - paddingTop - paddingBottom;
		var ch = helpers.getConstraintHeight(domNode);
		return isNaN(ch) ? h : Math.min(h, ch);
	};
	helpers.getStyle = function(el, property) {
		return el.currentStyle ?
			el.currentStyle[property] :
			document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
	};
	helpers.retinaScale = function(chart, forceRatio) {
		var pixelRatio = chart.currentDevicePixelRatio = forceRatio || window.devicePixelRatio || 1;
		if (pixelRatio === 1) {
			return;
		}

		var canvas = chart.canvas;
		var height = chart.height;
		var width = chart.width;

		canvas.height = height * pixelRatio;
		canvas.width = width * pixelRatio;
		chart.ctx.scale(pixelRatio, pixelRatio);

		// If no style has been set on the canvas, the render size is used as display size,
		// making the chart visually bigger, so let's enforce it to the "correct" values.
		// See https://github.com/chartjs/Chart.js/issues/3575
		canvas.style.height = height + 'px';
		canvas.style.width = width + 'px';
	};
	// -- Canvas methods
	helpers.fontString = function(pixelSize, fontStyle, fontFamily) {
		return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
	};
	helpers.longestText = function(ctx, font, arrayOfThings, cache) {
		cache = cache || {};
		var data = cache.data = cache.data || {};
		var gc = cache.garbageCollect = cache.garbageCollect || [];

		if (cache.font !== font) {
			data = cache.data = {};
			gc = cache.garbageCollect = [];
			cache.font = font;
		}

		ctx.font = font;
		var longest = 0;
		helpers.each(arrayOfThings, function(thing) {
			// Undefined strings and arrays should not be measured
			if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) {
				longest = helpers.measureText(ctx, data, gc, longest, thing);
			} else if (helpers.isArray(thing)) {
				// if it is an array lets measure each element
				// to do maybe simplify this function a bit so we can do this more recursively?
				helpers.each(thing, function(nestedThing) {
					// Undefined strings and arrays should not be measured
					if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) {
						longest = helpers.measureText(ctx, data, gc, longest, nestedThing);
					}
				});
			}
		});

		var gcLen = gc.length / 2;
		if (gcLen > arrayOfThings.length) {
			for (var i = 0; i < gcLen; i++) {
				delete data[gc[i]];
			}
			gc.splice(0, gcLen);
		}
		return longest;
	};
	helpers.measureText = function(ctx, data, gc, longest, string) {
		var textWidth = data[string];
		if (!textWidth) {
			textWidth = data[string] = ctx.measureText(string).width;
			gc.push(string);
		}
		if (textWidth > longest) {
			longest = textWidth;
		}
		return longest;
	};
	helpers.numberOfLabelLines = function(arrayOfThings) {
		var numberOfLines = 1;
		helpers.each(arrayOfThings, function(thing) {
			if (helpers.isArray(thing)) {
				if (thing.length > numberOfLines) {
					numberOfLines = thing.length;
				}
			}
		});
		return numberOfLines;
	};

	helpers.color = !color ?
		function(value) {
			console.error('Color.js not found!');
			return value;
		} :
		function(value) {
			/* global CanvasGradient */
			if (value instanceof CanvasGradient) {
				value = defaults.global.defaultColor;
			}

			return color(value);
		};

	helpers.getHoverColor = function(colorValue) {
		/* global CanvasPattern */
		return (colorValue instanceof CanvasPattern) ?
			colorValue :
			helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString();
	};
};

},{"2":2,"25":25,"45":45}],28:[function(require,module,exports){
'use strict';

var helpers = require(45);

/**
 * Helper function to get relative position for an event
 * @param {Event|IEvent} event - The event to get the position for
 * @param {Chart} chart - The chart
 * @returns {Point} the event position
 */
function getRelativePosition(e, chart) {
	if (e.native) {
		return {
			x: e.x,
			y: e.y
		};
	}

	return helpers.getRelativePosition(e, chart);
}

/**
 * Helper function to traverse all of the visible elements in the chart
 * @param chart {chart} the chart
 * @param handler {Function} the callback to execute for each visible item
 */
function parseVisibleItems(chart, handler) {
	var datasets = chart.data.datasets;
	var meta, i, j, ilen, jlen;

	for (i = 0, ilen = datasets.length; i < ilen; ++i) {
		if (!chart.isDatasetVisible(i)) {
			continue;
		}

		meta = chart.getDatasetMeta(i);
		for (j = 0, jlen = meta.data.length; j < jlen; ++j) {
			var element = meta.data[j];
			if (!element._view.skip) {
				handler(element);
			}
		}
	}
}

/**
 * Helper function to get the items that intersect the event position
 * @param items {ChartElement[]} elements to filter
 * @param position {Point} the point to be nearest to
 * @return {ChartElement[]} the nearest items
 */
function getIntersectItems(chart, position) {
	var elements = [];

	parseVisibleItems(chart, function(element) {
		if (element.inRange(position.x, position.y)) {
			elements.push(element);
		}
	});

	return elements;
}

/**
 * Helper function to get the items nearest to the event position considering all visible items in teh chart
 * @param chart {Chart} the chart to look at elements from
 * @param position {Point} the point to be nearest to
 * @param intersect {Boolean} if true, only consider items that intersect the position
 * @param distanceMetric {Function} function to provide the distance between points
 * @return {ChartElement[]} the nearest items
 */
function getNearestItems(chart, position, intersect, distanceMetric) {
	var minDistance = Number.POSITIVE_INFINITY;
	var nearestItems = [];

	parseVisibleItems(chart, function(element) {
		if (intersect && !element.inRange(position.x, position.y)) {
			return;
		}

		var center = element.getCenterPoint();
		var distance = distanceMetric(position, center);

		if (distance < minDistance) {
			nearestItems = [element];
			minDistance = distance;
		} else if (distance === minDistance) {
			// Can have multiple items at the same distance in which case we sort by size
			nearestItems.push(element);
		}
	});

	return nearestItems;
}

/**
 * Get a distance metric function for two points based on the
 * axis mode setting
 * @param {String} axis the axis mode. x|y|xy
 */
function getDistanceMetricForAxis(axis) {
	var useX = axis.indexOf('x') !== -1;
	var useY = axis.indexOf('y') !== -1;

	return function(pt1, pt2) {
		var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;
		var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;
		return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
	};
}

function indexMode(chart, e, options) {
	var position = getRelativePosition(e, chart);
	// Default axis for index mode is 'x' to match old behaviour
	options.axis = options.axis || 'x';
	var distanceMetric = getDistanceMetricForAxis(options.axis);
	var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
	var elements = [];

	if (!items.length) {
		return [];
	}

	chart.data.datasets.forEach(function(dataset, datasetIndex) {
		if (chart.isDatasetVisible(datasetIndex)) {
			var meta = chart.getDatasetMeta(datasetIndex);
			var element = meta.data[items[0]._index];

			// don't count items that are skipped (null data)
			if (element && !element._view.skip) {
				elements.push(element);
			}
		}
	});

	return elements;
}

/**
 * @interface IInteractionOptions
 */
/**
 * If true, only consider items that intersect the point
 * @name IInterfaceOptions#boolean
 * @type Boolean
 */

/**
 * Contains interaction related functions
 * @namespace Chart.Interaction
 */
module.exports = {
	// Helper function for different modes
	modes: {
		single: function(chart, e) {
			var position = getRelativePosition(e, chart);
			var elements = [];

			parseVisibleItems(chart, function(element) {
				if (element.inRange(position.x, position.y)) {
					elements.push(element);
					return elements;
				}
			});

			return elements.slice(0, 1);
		},

		/**
		 * @function Chart.Interaction.modes.label
		 * @deprecated since version 2.4.0
		 * @todo remove at version 3
		 * @private
		 */
		label: indexMode,

		/**
		 * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something
		 * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item
		 * @function Chart.Interaction.modes.index
		 * @since v2.4.0
		 * @param chart {chart} the chart we are returning items from
		 * @param e {Event} the event we are find things at
		 * @param options {IInteractionOptions} options to use during interaction
		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
		 */
		index: indexMode,

		/**
		 * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something
		 * If the options.intersect is false, we find the nearest item and return the items in that dataset
		 * @function Chart.Interaction.modes.dataset
		 * @param chart {chart} the chart we are returning items from
		 * @param e {Event} the event we are find things at
		 * @param options {IInteractionOptions} options to use during interaction
		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
		 */
		dataset: function(chart, e, options) {
			var position = getRelativePosition(e, chart);
			options.axis = options.axis || 'xy';
			var distanceMetric = getDistanceMetricForAxis(options.axis);
			var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);

			if (items.length > 0) {
				items = chart.getDatasetMeta(items[0]._datasetIndex).data;
			}

			return items;
		},

		/**
		 * @function Chart.Interaction.modes.x-axis
		 * @deprecated since version 2.4.0. Use index mode and intersect == true
		 * @todo remove at version 3
		 * @private
		 */
		'x-axis': function(chart, e) {
			return indexMode(chart, e, {intersect: true});
		},

		/**
		 * Point mode returns all elements that hit test based on the event position
		 * of the event
		 * @function Chart.Interaction.modes.intersect
		 * @param chart {chart} the chart we are returning items from
		 * @param e {Event} the event we are find things at
		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
		 */
		point: function(chart, e) {
			var position = getRelativePosition(e, chart);
			return getIntersectItems(chart, position);
		},

		/**
		 * nearest mode returns the element closest to the point
		 * @function Chart.Interaction.modes.intersect
		 * @param chart {chart} the chart we are returning items from
		 * @param e {Event} the event we are find things at
		 * @param options {IInteractionOptions} options to use
		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
		 */
		nearest: function(chart, e, options) {
			var position = getRelativePosition(e, chart);
			options.axis = options.axis || 'xy';
			var distanceMetric = getDistanceMetricForAxis(options.axis);
			var nearestItems = getNearestItems(chart, position, options.intersect, distanceMetric);

			// We have multiple items at the same distance from the event. Now sort by smallest
			if (nearestItems.length > 1) {
				nearestItems.sort(function(a, b) {
					var sizeA = a.getArea();
					var sizeB = b.getArea();
					var ret = sizeA - sizeB;

					if (ret === 0) {
						// if equal sort by dataset index
						ret = a._datasetIndex - b._datasetIndex;
					}

					return ret;
				});
			}

			// Return only 1 item
			return nearestItems.slice(0, 1);
		},

		/**
		 * x mode returns the elements that hit-test at the current x coordinate
		 * @function Chart.Interaction.modes.x
		 * @param chart {chart} the chart we are returning items from
		 * @param e {Event} the event we are find things at
		 * @param options {IInteractionOptions} options to use
		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
		 */
		x: function(chart, e, options) {
			var position = getRelativePosition(e, chart);
			var items = [];
			var intersectsItem = false;

			parseVisibleItems(chart, function(element) {
				if (element.inXRange(position.x)) {
					items.push(element);
				}

				if (element.inRange(position.x, position.y)) {
					intersectsItem = true;
				}
			});

			// If we want to trigger on an intersect and we don't have any items
			// that intersect the position, return nothing
			if (options.intersect && !intersectsItem) {
				items = [];
			}
			return items;
		},

		/**
		 * y mode returns the elements that hit-test at the current y coordinate
		 * @function Chart.Interaction.modes.y
		 * @param chart {chart} the chart we are returning items from
		 * @param e {Event} the event we are find things at
		 * @param options {IInteractionOptions} options to use
		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
		 */
		y: function(chart, e, options) {
			var position = getRelativePosition(e, chart);
			var items = [];
			var intersectsItem = false;

			parseVisibleItems(chart, function(element) {
				if (element.inYRange(position.y)) {
					items.push(element);
				}

				if (element.inRange(position.x, position.y)) {
					intersectsItem = true;
				}
			});

			// If we want to trigger on an intersect and we don't have any items
			// that intersect the position, return nothing
			if (options.intersect && !intersectsItem) {
				items = [];
			}
			return items;
		}
	}
};

},{"45":45}],29:[function(require,module,exports){
'use strict';

var defaults = require(25);

defaults._set('global', {
	responsive: true,
	responsiveAnimationDuration: 0,
	maintainAspectRatio: true,
	events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
	hover: {
		onHover: null,
		mode: 'nearest',
		intersect: true,
		animationDuration: 400
	},
	onClick: null,
	defaultColor: 'rgba(0,0,0,0.1)',
	defaultFontColor: '#666',
	defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
	defaultFontSize: 12,
	defaultFontStyle: 'normal',
	showLines: true,

	// Element defaults defined in element extensions
	elements: {},

	// Layout options such as padding
	layout: {
		padding: {
			top: 0,
			right: 0,
			bottom: 0,
			left: 0
		}
	}
});

module.exports = function() {

	// Occupy the global variable of Chart, and create a simple base class
	var Chart = function(item, config) {
		this.construct(item, config);
		return this;
	};

	Chart.Chart = Chart;

	return Chart;
};

},{"25":25}],30:[function(require,module,exports){
'use strict';

var helpers = require(45);

module.exports = function(Chart) {

	function filterByPosition(array, position) {
		return helpers.where(array, function(v) {
			return v.position === position;
		});
	}

	function sortByWeight(array, reverse) {
		array.forEach(function(v, i) {
			v._tmpIndex_ = i;
			return v;
		});
		array.sort(function(a, b) {
			var v0 = reverse ? b : a;
			var v1 = reverse ? a : b;
			return v0.weight === v1.weight ?
				v0._tmpIndex_ - v1._tmpIndex_ :
				v0.weight - v1.weight;
		});
		array.forEach(function(v) {
			delete v._tmpIndex_;
		});
	}

	/**
	 * @interface ILayoutItem
	 * @prop {String} position - The position of the item in the chart layout. Possible values are
	 * 'left', 'top', 'right', 'bottom', and 'chartArea'
	 * @prop {Number} weight - The weight used to sort the item. Higher weights are further away from the chart area
	 * @prop {Boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down
	 * @prop {Function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom)
	 * @prop {Function} update - Takes two parameters: width and height. Returns size of item
	 * @prop {Function} getPadding -  Returns an object with padding on the edges
	 * @prop {Number} width - Width of item. Must be valid after update()
	 * @prop {Number} height - Height of item. Must be valid after update()
	 * @prop {Number} left - Left edge of the item. Set by layout system and cannot be used in update
	 * @prop {Number} top - Top edge of the item. Set by layout system and cannot be used in update
	 * @prop {Number} right - Right edge of the item. Set by layout system and cannot be used in update
	 * @prop {Number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update
	 */

	// The layout service is very self explanatory.  It's responsible for the layout within a chart.
	// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need
	// It is this service's responsibility of carrying out that layout.
	Chart.layoutService = {
		defaults: {},

		/**
		 * Register a box to a chart.
		 * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title.
		 * @param {Chart} chart - the chart to use
		 * @param {ILayoutItem} item - the item to add to be layed out
		 */
		addBox: function(chart, item) {
			if (!chart.boxes) {
				chart.boxes = [];
			}

			// initialize item with default values
			item.fullWidth = item.fullWidth || false;
			item.position = item.position || 'top';
			item.weight = item.weight || 0;

			chart.boxes.push(item);
		},

		/**
		 * Remove a layoutItem from a chart
		 * @param {Chart} chart - the chart to remove the box from
		 * @param {Object} layoutItem - the item to remove from the layout
		 */
		removeBox: function(chart, layoutItem) {
			var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;
			if (index !== -1) {
				chart.boxes.splice(index, 1);
			}
		},

		/**
		 * Sets (or updates) options on the given `item`.
		 * @param {Chart} chart - the chart in which the item lives (or will be added to)
		 * @param {Object} item - the item to configure with the given options
		 * @param {Object} options - the new item options.
		 */
		configure: function(chart, item, options) {
			var props = ['fullWidth', 'position', 'weight'];
			var ilen = props.length;
			var i = 0;
			var prop;

			for (; i < ilen; ++i) {
				prop = props[i];
				if (options.hasOwnProperty(prop)) {
					item[prop] = options[prop];
				}
			}
		},

		/**
		 * Fits boxes of the given chart into the given size by having each box measure itself
		 * then running a fitting algorithm
		 * @param {Chart} chart - the chart
		 * @param {Number} width - the width to fit into
		 * @param {Number} height - the height to fit into
		 */
		update: function(chart, width, height) {
			if (!chart) {
				return;
			}

			var layoutOptions = chart.options.layout || {};
			var padding = helpers.options.toPadding(layoutOptions.padding);
			var leftPadding = padding.left;
			var rightPadding = padding.right;
			var topPadding = padding.top;
			var bottomPadding = padding.bottom;

			var leftBoxes = filterByPosition(chart.boxes, 'left');
			var rightBoxes = filterByPosition(chart.boxes, 'right');
			var topBoxes = filterByPosition(chart.boxes, 'top');
			var bottomBoxes = filterByPosition(chart.boxes, 'bottom');
			var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea');

			// Sort boxes by weight. A higher weight is further away from the chart area
			sortByWeight(leftBoxes, true);
			sortByWeight(rightBoxes, false);
			sortByWeight(topBoxes, true);
			sortByWeight(bottomBoxes, false);

			// Essentially we now have any number of boxes on each of the 4 sides.
			// Our canvas looks like the following.
			// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
			// B1 is the bottom axis
			// There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays
			// These locations are single-box locations only, when trying to register a chartArea location that is already taken,
			// an error will be thrown.
			//
			// |----------------------------------------------------|
			// |                  T1 (Full Width)                   |
			// |----------------------------------------------------|
			// |    |    |                 T2                  |    |
			// |    |----|-------------------------------------|----|
			// |    |    | C1 |                           | C2 |    |
			// |    |    |----|                           |----|    |
			// |    |    |                                     |    |
			// | L1 | L2 |           ChartArea (C0)            | R1 |
			// |    |    |                                     |    |
			// |    |    |----|                           |----|    |
			// |    |    | C3 |                           | C4 |    |
			// |    |----|-------------------------------------|----|
			// |    |    |                 B1                  |    |
			// |----------------------------------------------------|
			// |                  B2 (Full Width)                   |
			// |----------------------------------------------------|
			//
			// What we do to find the best sizing, we do the following
			// 1. Determine the minimum size of the chart area.
			// 2. Split the remaining width equally between each vertical axis
			// 3. Split the remaining height equally between each horizontal axis
			// 4. Give each layout the maximum size it can be. The layout will return it's minimum size
			// 5. Adjust the sizes of each axis based on it's minimum reported size.
			// 6. Refit each axis
			// 7. Position each axis in the final location
			// 8. Tell the chart the final location of the chart area
			// 9. Tell any axes that overlay the chart area the positions of the chart area

			// Step 1
			var chartWidth = width - leftPadding - rightPadding;
			var chartHeight = height - topPadding - bottomPadding;
			var chartAreaWidth = chartWidth / 2; // min 50%
			var chartAreaHeight = chartHeight / 2; // min 50%

			// Step 2
			var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length);

			// Step 3
			var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length);

			// Step 4
			var maxChartAreaWidth = chartWidth;
			var maxChartAreaHeight = chartHeight;
			var minBoxSizes = [];

			function getMinimumBoxSize(box) {
				var minSize;
				var isHorizontal = box.isHorizontal();

				if (isHorizontal) {
					minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight);
					maxChartAreaHeight -= minSize.height;
				} else {
					minSize = box.update(verticalBoxWidth, chartAreaHeight);
					maxChartAreaWidth -= minSize.width;
				}

				minBoxSizes.push({
					horizontal: isHorizontal,
					minSize: minSize,
					box: box,
				});
			}

			helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize);

			// If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478)
			var maxHorizontalLeftPadding = 0;
			var maxHorizontalRightPadding = 0;
			var maxVerticalTopPadding = 0;
			var maxVerticalBottomPadding = 0;

			helpers.each(topBoxes.concat(bottomBoxes), function(horizontalBox) {
				if (horizontalBox.getPadding) {
					var boxPadding = horizontalBox.getPadding();
					maxHorizontalLeftPadding = Math.max(maxHorizontalLeftPadding, boxPadding.left);
					maxHorizontalRightPadding = Math.max(maxHorizontalRightPadding, boxPadding.right);
				}
			});

			helpers.each(leftBoxes.concat(rightBoxes), function(verticalBox) {
				if (verticalBox.getPadding) {
					var boxPadding = verticalBox.getPadding();
					maxVerticalTopPadding = Math.max(maxVerticalTopPadding, boxPadding.top);
					maxVerticalBottomPadding = Math.max(maxVerticalBottomPadding, boxPadding.bottom);
				}
			});

			// At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could
			// be if the axes are drawn at their minimum sizes.
			// Steps 5 & 6
			var totalLeftBoxesWidth = leftPadding;
			var totalRightBoxesWidth = rightPadding;
			var totalTopBoxesHeight = topPadding;
			var totalBottomBoxesHeight = bottomPadding;

			// Function to fit a box
			function fitBox(box) {
				var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) {
					return minBox.box === box;
				});

				if (minBoxSize) {
					if (box.isHorizontal()) {
						var scaleMargin = {
							left: Math.max(totalLeftBoxesWidth, maxHorizontalLeftPadding),
							right: Math.max(totalRightBoxesWidth, maxHorizontalRightPadding),
							top: 0,
							bottom: 0
						};

						// Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends
						// on the margin. Sometimes they need to increase in size slightly
						box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin);
					} else {
						box.update(minBoxSize.minSize.width, maxChartAreaHeight);
					}
				}
			}

			// Update, and calculate the left and right margins for the horizontal boxes
			helpers.each(leftBoxes.concat(rightBoxes), fitBox);

			helpers.each(leftBoxes, function(box) {
				totalLeftBoxesWidth += box.width;
			});

			helpers.each(rightBoxes, function(box) {
				totalRightBoxesWidth += box.width;
			});

			// Set the Left and Right margins for the horizontal boxes
			helpers.each(topBoxes.concat(bottomBoxes), fitBox);

			// Figure out how much margin is on the top and bottom of the vertical boxes
			helpers.each(topBoxes, function(box) {
				totalTopBoxesHeight += box.height;
			});

			helpers.each(bottomBoxes, function(box) {
				totalBottomBoxesHeight += box.height;
			});

			function finalFitVerticalBox(box) {
				var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) {
					return minSize.box === box;
				});

				var scaleMargin = {
					left: 0,
					right: 0,
					top: totalTopBoxesHeight,
					bottom: totalBottomBoxesHeight
				};

				if (minBoxSize) {
					box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin);
				}
			}

			// Let the left layout know the final margin
			helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox);

			// Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance)
			totalLeftBoxesWidth = leftPadding;
			totalRightBoxesWidth = rightPadding;
			totalTopBoxesHeight = topPadding;
			totalBottomBoxesHeight = bottomPadding;

			helpers.each(leftBoxes, function(box) {
				totalLeftBoxesWidth += box.width;
			});

			helpers.each(rightBoxes, function(box) {
				totalRightBoxesWidth += box.width;
			});

			helpers.each(topBoxes, function(box) {
				totalTopBoxesHeight += box.height;
			});
			helpers.each(bottomBoxes, function(box) {
				totalBottomBoxesHeight += box.height;
			});

			// We may be adding some padding to account for rotated x axis labels
			var leftPaddingAddition = Math.max(maxHorizontalLeftPadding - totalLeftBoxesWidth, 0);
			totalLeftBoxesWidth += leftPaddingAddition;
			totalRightBoxesWidth += Math.max(maxHorizontalRightPadding - totalRightBoxesWidth, 0);

			var topPaddingAddition = Math.max(maxVerticalTopPadding - totalTopBoxesHeight, 0);
			totalTopBoxesHeight += topPaddingAddition;
			totalBottomBoxesHeight += Math.max(maxVerticalBottomPadding - totalBottomBoxesHeight, 0);

			// Figure out if our chart area changed. This would occur if the dataset layout label rotation
			// changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
			// without calling `fit` again
			var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight;
			var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth;

			if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) {
				helpers.each(leftBoxes, function(box) {
					box.height = newMaxChartAreaHeight;
				});

				helpers.each(rightBoxes, function(box) {
					box.height = newMaxChartAreaHeight;
				});

				helpers.each(topBoxes, function(box) {
					if (!box.fullWidth) {
						box.width = newMaxChartAreaWidth;
					}
				});

				helpers.each(bottomBoxes, function(box) {
					if (!box.fullWidth) {
						box.width = newMaxChartAreaWidth;
					}
				});

				maxChartAreaHeight = newMaxChartAreaHeight;
				maxChartAreaWidth = newMaxChartAreaWidth;
			}

			// Step 7 - Position the boxes
			var left = leftPadding + leftPaddingAddition;
			var top = topPadding + topPaddingAddition;

			function placeBox(box) {
				if (box.isHorizontal()) {
					box.left = box.fullWidth ? leftPadding : totalLeftBoxesWidth;
					box.right = box.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth;
					box.top = top;
					box.bottom = top + box.height;

					// Move to next point
					top = box.bottom;

				} else {

					box.left = left;
					box.right = left + box.width;
					box.top = totalTopBoxesHeight;
					box.bottom = totalTopBoxesHeight + maxChartAreaHeight;

					// Move to next point
					left = box.right;
				}
			}

			helpers.each(leftBoxes.concat(topBoxes), placeBox);

			// Account for chart width and height
			left += maxChartAreaWidth;
			top += maxChartAreaHeight;

			helpers.each(rightBoxes, placeBox);
			helpers.each(bottomBoxes, placeBox);

			// Step 8
			chart.chartArea = {
				left: totalLeftBoxesWidth,
				top: totalTopBoxesHeight,
				right: totalLeftBoxesWidth + maxChartAreaWidth,
				bottom: totalTopBoxesHeight + maxChartAreaHeight
			};

			// Step 9
			helpers.each(chartAreaBoxes, function(box) {
				box.left = chart.chartArea.left;
				box.top = chart.chartArea.top;
				box.right = chart.chartArea.right;
				box.bottom = chart.chartArea.bottom;

				box.update(maxChartAreaWidth, maxChartAreaHeight);
			});
		}
	};
};

},{"45":45}],31:[function(require,module,exports){
'use strict';

var defaults = require(25);
var Element = require(26);
var helpers = require(45);

defaults._set('global', {
	plugins: {}
});

module.exports = function(Chart) {

	/**
	 * The plugin service singleton
	 * @namespace Chart.plugins
	 * @since 2.1.0
	 */
	Chart.plugins = {
		/**
		 * Globally registered plugins.
		 * @private
		 */
		_plugins: [],

		/**
		 * This identifier is used to invalidate the descriptors cache attached to each chart
		 * when a global plugin is registered or unregistered. In this case, the cache ID is
		 * incremented and descriptors are regenerated during following API calls.
		 * @private
		 */
		_cacheId: 0,

		/**
		 * Registers the given plugin(s) if not already registered.
		 * @param {Array|Object} plugins plugin instance(s).
		 */
		register: function(plugins) {
			var p = this._plugins;
			([]).concat(plugins).forEach(function(plugin) {
				if (p.indexOf(plugin) === -1) {
					p.push(plugin);
				}
			});

			this._cacheId++;
		},

		/**
		 * Unregisters the given plugin(s) only if registered.
		 * @param {Array|Object} plugins plugin instance(s).
		 */
		unregister: function(plugins) {
			var p = this._plugins;
			([]).concat(plugins).forEach(function(plugin) {
				var idx = p.indexOf(plugin);
				if (idx !== -1) {
					p.splice(idx, 1);
				}
			});

			this._cacheId++;
		},

		/**
		 * Remove all registered plugins.
		 * @since 2.1.5
		 */
		clear: function() {
			this._plugins = [];
			this._cacheId++;
		},

		/**
		 * Returns the number of registered plugins?
		 * @returns {Number}
		 * @since 2.1.5
		 */
		count: function() {
			return this._plugins.length;
		},

		/**
		 * Returns all registered plugin instances.
		 * @returns {Array} array of plugin objects.
		 * @since 2.1.5
		 */
		getAll: function() {
			return this._plugins;
		},

		/**
		 * Calls enabled plugins for `chart` on the specified hook and with the given args.
		 * This method immediately returns as soon as a plugin explicitly returns false. The
		 * returned value can be used, for instance, to interrupt the current action.
		 * @param {Object} chart - The chart instance for which plugins should be called.
		 * @param {String} hook - The name of the plugin method to call (e.g. 'beforeUpdate').
		 * @param {Array} [args] - Extra arguments to apply to the hook call.
		 * @returns {Boolean} false if any of the plugins return false, else returns true.
		 */
		notify: function(chart, hook, args) {
			var descriptors = this.descriptors(chart);
			var ilen = descriptors.length;
			var i, descriptor, plugin, params, method;

			for (i = 0; i < ilen; ++i) {
				descriptor = descriptors[i];
				plugin = descriptor.plugin;
				method = plugin[hook];
				if (typeof method === 'function') {
					params = [chart].concat(args || []);
					params.push(descriptor.options);
					if (method.apply(plugin, params) === false) {
						return false;
					}
				}
			}

			return true;
		},

		/**
		 * Returns descriptors of enabled plugins for the given chart.
		 * @returns {Array} [{ plugin, options }]
		 * @private
		 */
		descriptors: function(chart) {
			var cache = chart._plugins || (chart._plugins = {});
			if (cache.id === this._cacheId) {
				return cache.descriptors;
			}

			var plugins = [];
			var descriptors = [];
			var config = (chart && chart.config) || {};
			var options = (config.options && config.options.plugins) || {};

			this._plugins.concat(config.plugins || []).forEach(function(plugin) {
				var idx = plugins.indexOf(plugin);
				if (idx !== -1) {
					return;
				}

				var id = plugin.id;
				var opts = options[id];
				if (opts === false) {
					return;
				}

				if (opts === true) {
					opts = helpers.clone(defaults.global.plugins[id]);
				}

				plugins.push(plugin);
				descriptors.push({
					plugin: plugin,
					options: opts || {}
				});
			});

			cache.descriptors = descriptors;
			cache.id = this._cacheId;
			return descriptors;
		}
	};

	/**
	 * Plugin extension hooks.
	 * @interface IPlugin
	 * @since 2.1.0
	 */
	/**
	 * @method IPlugin#beforeInit
	 * @desc Called before initializing `chart`.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#afterInit
	 * @desc Called after `chart` has been initialized and before the first update.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#beforeUpdate
	 * @desc Called before updating `chart`. If any plugin returns `false`, the update
	 * is cancelled (and thus subsequent render(s)) until another `update` is triggered.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 * @returns {Boolean} `false` to cancel the chart update.
	 */
	/**
	 * @method IPlugin#afterUpdate
	 * @desc Called after `chart` has been updated and before rendering. Note that this
	 * hook will not be called if the chart update has been previously cancelled.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#beforeDatasetsUpdate
 	 * @desc Called before updating the `chart` datasets. If any plugin returns `false`,
	 * the datasets update is cancelled until another `update` is triggered.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 * @returns {Boolean} false to cancel the datasets update.
	 * @since version 2.1.5
	 */
	/**
	 * @method IPlugin#afterDatasetsUpdate
	 * @desc Called after the `chart` datasets have been updated. Note that this hook
	 * will not be called if the datasets update has been previously cancelled.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 * @since version 2.1.5
	 */
	/**
	 * @method IPlugin#beforeDatasetUpdate
 	 * @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin
	 * returns `false`, the datasets update is cancelled until another `update` is triggered.
	 * @param {Chart} chart - The chart instance.
	 * @param {Object} args - The call arguments.
	 * @param {Number} args.index - The dataset index.
	 * @param {Object} args.meta - The dataset metadata.
	 * @param {Object} options - The plugin options.
	 * @returns {Boolean} `false` to cancel the chart datasets drawing.
	 */
	/**
	 * @method IPlugin#afterDatasetUpdate
 	 * @desc Called after the `chart` datasets at the given `args.index` has been updated. Note
	 * that this hook will not be called if the datasets update has been previously cancelled.
	 * @param {Chart} chart - The chart instance.
	 * @param {Object} args - The call arguments.
	 * @param {Number} args.index - The dataset index.
	 * @param {Object} args.meta - The dataset metadata.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#beforeLayout
	 * @desc Called before laying out `chart`. If any plugin returns `false`,
	 * the layout update is cancelled until another `update` is triggered.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 * @returns {Boolean} `false` to cancel the chart layout.
	 */
	/**
	 * @method IPlugin#afterLayout
	 * @desc Called after the `chart` has been layed out. Note that this hook will not
	 * be called if the layout update has been previously cancelled.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#beforeRender
	 * @desc Called before rendering `chart`. If any plugin returns `false`,
	 * the rendering is cancelled until another `render` is triggered.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 * @returns {Boolean} `false` to cancel the chart rendering.
	 */
	/**
	 * @method IPlugin#afterRender
	 * @desc Called after the `chart` has been fully rendered (and animation completed). Note
	 * that this hook will not be called if the rendering has been previously cancelled.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#beforeDraw
	 * @desc Called before drawing `chart` at every animation frame specified by the given
	 * easing value. If any plugin returns `false`, the frame drawing is cancelled until
	 * another `render` is triggered.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
	 * @param {Object} options - The plugin options.
	 * @returns {Boolean} `false` to cancel the chart drawing.
	 */
	/**
	 * @method IPlugin#afterDraw
	 * @desc Called after the `chart` has been drawn for the specific easing value. Note
	 * that this hook will not be called if the drawing has been previously cancelled.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#beforeDatasetsDraw
 	 * @desc Called before drawing the `chart` datasets. If any plugin returns `false`,
	 * the datasets drawing is cancelled until another `render` is triggered.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
	 * @param {Object} options - The plugin options.
	 * @returns {Boolean} `false` to cancel the chart datasets drawing.
	 */
	/**
	 * @method IPlugin#afterDatasetsDraw
	 * @desc Called after the `chart` datasets have been drawn. Note that this hook
	 * will not be called if the datasets drawing has been previously cancelled.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#beforeDatasetDraw
 	 * @desc Called before drawing the `chart` dataset at the given `args.index` (datasets
	 * are drawn in the reverse order). If any plugin returns `false`, the datasets drawing
	 * is cancelled until another `render` is triggered.
	 * @param {Chart} chart - The chart instance.
	 * @param {Object} args - The call arguments.
	 * @param {Number} args.index - The dataset index.
	 * @param {Object} args.meta - The dataset metadata.
	 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
	 * @param {Object} options - The plugin options.
	 * @returns {Boolean} `false` to cancel the chart datasets drawing.
	 */
	/**
	 * @method IPlugin#afterDatasetDraw
 	 * @desc Called after the `chart` datasets at the given `args.index` have been drawn
	 * (datasets are drawn in the reverse order). Note that this hook will not be called
	 * if the datasets drawing has been previously cancelled.
	 * @param {Chart} chart - The chart instance.
	 * @param {Object} args - The call arguments.
	 * @param {Number} args.index - The dataset index.
	 * @param {Object} args.meta - The dataset metadata.
	 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#beforeEvent
 	 * @desc Called before processing the specified `event`. If any plugin returns `false`,
	 * the event will be discarded.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {IEvent} event - The event object.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#afterEvent
	 * @desc Called after the `event` has been consumed. Note that this hook
	 * will not be called if the `event` has been previously discarded.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {IEvent} event - The event object.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#resize
	 * @desc Called after the chart as been resized.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Number} size - The new canvas display size (eq. canvas.style width & height).
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#destroy
	 * @desc Called after the chart as been destroyed.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 */

	/**
	 * Provided for backward compatibility, use Chart.plugins instead
	 * @namespace Chart.pluginService
	 * @deprecated since version 2.1.5
	 * @todo remove at version 3
	 * @private
	 */
	Chart.pluginService = Chart.plugins;

	/**
	 * Provided for backward compatibility, inheriting from Chart.PlugingBase has no
	 * effect, instead simply create/register plugins via plain JavaScript objects.
	 * @interface Chart.PluginBase
	 * @deprecated since version 2.5.0
	 * @todo remove at version 3
	 * @private
	 */
	Chart.PluginBase = Element.extend({});
};

},{"25":25,"26":26,"45":45}],32:[function(require,module,exports){
'use strict';

var defaults = require(25);
var Element = require(26);
var helpers = require(45);
var Ticks = require(34);

defaults._set('scale', {
	display: true,
	position: 'left',
	offset: false,

	// grid line settings
	gridLines: {
		display: true,
		color: 'rgba(0, 0, 0, 0.1)',
		lineWidth: 1,
		drawBorder: true,
		drawOnChartArea: true,
		drawTicks: true,
		tickMarkLength: 10,
		zeroLineWidth: 1,
		zeroLineColor: 'rgba(0,0,0,0.25)',
		zeroLineBorderDash: [],
		zeroLineBorderDashOffset: 0.0,
		offsetGridLines: false,
		borderDash: [],
		borderDashOffset: 0.0
	},

	// scale label
	scaleLabel: {
		// display property
		display: false,

		// actual label
		labelString: '',

		// line height
		lineHeight: 1.2,

		// top/bottom padding
		padding: {
			top: 4,
			bottom: 4
		}
	},

	// label settings
	ticks: {
		beginAtZero: false,
		minRotation: 0,
		maxRotation: 50,
		mirror: false,
		padding: 0,
		reverse: false,
		display: true,
		autoSkip: true,
		autoSkipPadding: 0,
		labelOffset: 0,
		// We pass through arrays to be rendered as multiline labels, we convert Others to strings here.
		callback: Ticks.formatters.values,
		minor: {},
		major: {}
	}
});

function labelsFromTicks(ticks) {
	var labels = [];
	var i, ilen;

	for (i = 0, ilen = ticks.length; i < ilen; ++i) {
		labels.push(ticks[i].label);
	}

	return labels;
}

function getLineValue(scale, index, offsetGridLines) {
	var lineValue = scale.getPixelForTick(index);

	if (offsetGridLines) {
		if (index === 0) {
			lineValue -= (scale.getPixelForTick(1) - lineValue) / 2;
		} else {
			lineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2;
		}
	}
	return lineValue;
}

module.exports = function(Chart) {

	function computeTextSize(context, tick, font) {
		return helpers.isArray(tick) ?
			helpers.longestText(context, font, tick) :
			context.measureText(tick).width;
	}

	function parseFontOptions(options) {
		var valueOrDefault = helpers.valueOrDefault;
		var globalDefaults = defaults.global;
		var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize);
		var style = valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle);
		var family = valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily);

		return {
			size: size,
			style: style,
			family: family,
			font: helpers.fontString(size, style, family)
		};
	}

	function parseLineHeight(options) {
		return helpers.options.toLineHeight(
			helpers.valueOrDefault(options.lineHeight, 1.2),
			helpers.valueOrDefault(options.fontSize, defaults.global.defaultFontSize));
	}

	Chart.Scale = Element.extend({
		/**
		 * Get the padding needed for the scale
		 * @method getPadding
		 * @private
		 * @returns {Padding} the necessary padding
		 */
		getPadding: function() {
			var me = this;
			return {
				left: me.paddingLeft || 0,
				top: me.paddingTop || 0,
				right: me.paddingRight || 0,
				bottom: me.paddingBottom || 0
			};
		},

		/**
		 * Returns the scale tick objects ({label, major})
		 * @since 2.7
		 */
		getTicks: function() {
			return this._ticks;
		},

		// These methods are ordered by lifecyle. Utilities then follow.
		// Any function defined here is inherited by all scale types.
		// Any function can be extended by the scale type

		mergeTicksOptions: function() {
			var ticks = this.options.ticks;
			if (ticks.minor === false) {
				ticks.minor = {
					display: false
				};
			}
			if (ticks.major === false) {
				ticks.major = {
					display: false
				};
			}
			for (var key in ticks) {
				if (key !== 'major' && key !== 'minor') {
					if (typeof ticks.minor[key] === 'undefined') {
						ticks.minor[key] = ticks[key];
					}
					if (typeof ticks.major[key] === 'undefined') {
						ticks.major[key] = ticks[key];
					}
				}
			}
		},
		beforeUpdate: function() {
			helpers.callback(this.options.beforeUpdate, [this]);
		},
		update: function(maxWidth, maxHeight, margins) {
			var me = this;
			var i, ilen, labels, label, ticks, tick;

			// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
			me.beforeUpdate();

			// Absorb the master measurements
			me.maxWidth = maxWidth;
			me.maxHeight = maxHeight;
			me.margins = helpers.extend({
				left: 0,
				right: 0,
				top: 0,
				bottom: 0
			}, margins);
			me.longestTextCache = me.longestTextCache || {};

			// Dimensions
			me.beforeSetDimensions();
			me.setDimensions();
			me.afterSetDimensions();

			// Data min/max
			me.beforeDataLimits();
			me.determineDataLimits();
			me.afterDataLimits();

			// Ticks - `this.ticks` is now DEPRECATED!
			// Internal ticks are now stored as objects in the PRIVATE `this._ticks` member
			// and must not be accessed directly from outside this class. `this.ticks` being
			// around for long time and not marked as private, we can't change its structure
			// without unexpected breaking changes. If you need to access the scale ticks,
			// use scale.getTicks() instead.

			me.beforeBuildTicks();

			// New implementations should return an array of objects but for BACKWARD COMPAT,
			// we still support no return (`this.ticks` internally set by calling this method).
			ticks = me.buildTicks() || [];

			me.afterBuildTicks();

			me.beforeTickToLabelConversion();

			// New implementations should return the formatted tick labels but for BACKWARD
			// COMPAT, we still support no return (`this.ticks` internally changed by calling
			// this method and supposed to contain only string values).
			labels = me.convertTicksToLabels(ticks) || me.ticks;

			me.afterTickToLabelConversion();

			me.ticks = labels;   // BACKWARD COMPATIBILITY

			// IMPORTANT: from this point, we consider that `this.ticks` will NEVER change!

			// BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`)
			for (i = 0, ilen = labels.length; i < ilen; ++i) {
				label = labels[i];
				tick = ticks[i];
				if (!tick) {
					ticks.push(tick = {
						label: label,
						major: false
					});
				} else {
					tick.label = label;
				}
			}

			me._ticks = ticks;

			// Tick Rotation
			me.beforeCalculateTickRotation();
			me.calculateTickRotation();
			me.afterCalculateTickRotation();
			// Fit
			me.beforeFit();
			me.fit();
			me.afterFit();
			//
			me.afterUpdate();

			return me.minSize;

		},
		afterUpdate: function() {
			helpers.callback(this.options.afterUpdate, [this]);
		},

		//

		beforeSetDimensions: function() {
			helpers.callback(this.options.beforeSetDimensions, [this]);
		},
		setDimensions: function() {
			var me = this;
			// Set the unconstrained dimension before label rotation
			if (me.isHorizontal()) {
				// Reset position before calculating rotation
				me.width = me.maxWidth;
				me.left = 0;
				me.right = me.width;
			} else {
				me.height = me.maxHeight;

				// Reset position before calculating rotation
				me.top = 0;
				me.bottom = me.height;
			}

			// Reset padding
			me.paddingLeft = 0;
			me.paddingTop = 0;
			me.paddingRight = 0;
			me.paddingBottom = 0;
		},
		afterSetDimensions: function() {
			helpers.callback(this.options.afterSetDimensions, [this]);
		},

		// Data limits
		beforeDataLimits: function() {
			helpers.callback(this.options.beforeDataLimits, [this]);
		},
		determineDataLimits: helpers.noop,
		afterDataLimits: function() {
			helpers.callback(this.options.afterDataLimits, [this]);
		},

		//
		beforeBuildTicks: function() {
			helpers.callback(this.options.beforeBuildTicks, [this]);
		},
		buildTicks: helpers.noop,
		afterBuildTicks: function() {
			helpers.callback(this.options.afterBuildTicks, [this]);
		},

		beforeTickToLabelConversion: function() {
			helpers.callback(this.options.beforeTickToLabelConversion, [this]);
		},
		convertTicksToLabels: function() {
			var me = this;
			// Convert ticks to strings
			var tickOpts = me.options.ticks;
			me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this);
		},
		afterTickToLabelConversion: function() {
			helpers.callback(this.options.afterTickToLabelConversion, [this]);
		},

		//

		beforeCalculateTickRotation: function() {
			helpers.callback(this.options.beforeCalculateTickRotation, [this]);
		},
		calculateTickRotation: function() {
			var me = this;
			var context = me.ctx;
			var tickOpts = me.options.ticks;
			var labels = labelsFromTicks(me._ticks);

			// Get the width of each grid by calculating the difference
			// between x offsets between 0 and 1.
			var tickFont = parseFontOptions(tickOpts);
			context.font = tickFont.font;

			var labelRotation = tickOpts.minRotation || 0;

			if (labels.length && me.options.display && me.isHorizontal()) {
				var originalLabelWidth = helpers.longestText(context, tickFont.font, labels, me.longestTextCache);
				var labelWidth = originalLabelWidth;
				var cosRotation, sinRotation;

				// Allow 3 pixels x2 padding either side for label readability
				var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6;

				// Max label rotation can be set or default to 90 - also act as a loop counter
				while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) {
					var angleRadians = helpers.toRadians(labelRotation);
					cosRotation = Math.cos(angleRadians);
					sinRotation = Math.sin(angleRadians);

					if (sinRotation * originalLabelWidth > me.maxHeight) {
						// go back one step
						labelRotation--;
						break;
					}

					labelRotation++;
					labelWidth = cosRotation * originalLabelWidth;
				}
			}

			me.labelRotation = labelRotation;
		},
		afterCalculateTickRotation: function() {
			helpers.callback(this.options.afterCalculateTickRotation, [this]);
		},

		//

		beforeFit: function() {
			helpers.callback(this.options.beforeFit, [this]);
		},
		fit: function() {
			var me = this;
			// Reset
			var minSize = me.minSize = {
				width: 0,
				height: 0
			};

			var labels = labelsFromTicks(me._ticks);

			var opts = me.options;
			var tickOpts = opts.ticks;
			var scaleLabelOpts = opts.scaleLabel;
			var gridLineOpts = opts.gridLines;
			var display = opts.display;
			var isHorizontal = me.isHorizontal();

			var tickFont = parseFontOptions(tickOpts);
			var tickMarkLength = opts.gridLines.tickMarkLength;

			// Width
			if (isHorizontal) {
				// subtract the margins to line up with the chartArea if we are a full width scale
				minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth;
			} else {
				minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
			}

			// height
			if (isHorizontal) {
				minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
			} else {
				minSize.height = me.maxHeight; // fill all the height
			}

			// Are we showing a title for the scale?
			if (scaleLabelOpts.display && display) {
				var scaleLabelLineHeight = parseLineHeight(scaleLabelOpts);
				var scaleLabelPadding = helpers.options.toPadding(scaleLabelOpts.padding);
				var deltaHeight = scaleLabelLineHeight + scaleLabelPadding.height;

				if (isHorizontal) {
					minSize.height += deltaHeight;
				} else {
					minSize.width += deltaHeight;
				}
			}

			// Don't bother fitting the ticks if we are not showing them
			if (tickOpts.display && display) {
				var largestTextWidth = helpers.longestText(me.ctx, tickFont.font, labels, me.longestTextCache);
				var tallestLabelHeightInLines = helpers.numberOfLabelLines(labels);
				var lineSpace = tickFont.size * 0.5;
				var tickPadding = me.options.ticks.padding;

				if (isHorizontal) {
					// A horizontal axis is more constrained by the height.
					me.longestLabelWidth = largestTextWidth;

					var angleRadians = helpers.toRadians(me.labelRotation);
					var cosRotation = Math.cos(angleRadians);
					var sinRotation = Math.sin(angleRadians);

					// TODO - improve this calculation
					var labelHeight = (sinRotation * largestTextWidth)
						+ (tickFont.size * tallestLabelHeightInLines)
						+ (lineSpace * (tallestLabelHeightInLines - 1))
						+ lineSpace; // padding

					minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding);

					me.ctx.font = tickFont.font;
					var firstLabelWidth = computeTextSize(me.ctx, labels[0], tickFont.font);
					var lastLabelWidth = computeTextSize(me.ctx, labels[labels.length - 1], tickFont.font);

					// Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned
					// which means that the right padding is dominated by the font height
					if (me.labelRotation !== 0) {
						me.paddingLeft = opts.position === 'bottom' ? (cosRotation * firstLabelWidth) + 3 : (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges
						me.paddingRight = opts.position === 'bottom' ? (cosRotation * lineSpace) + 3 : (cosRotation * lastLabelWidth) + 3;
					} else {
						me.paddingLeft = firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges
						me.paddingRight = lastLabelWidth / 2 + 3;
					}
				} else {
					// A vertical axis is more constrained by the width. Labels are the
					// dominant factor here, so get that length first and account for padding
					if (tickOpts.mirror) {
						largestTextWidth = 0;
					} else {
						// use lineSpace for consistency with horizontal axis
						// tickPadding is not implemented for horizontal
						largestTextWidth += tickPadding + lineSpace;
					}

					minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth);

					me.paddingTop = tickFont.size / 2;
					me.paddingBottom = tickFont.size / 2;
				}
			}

			me.handleMargins();

			me.width = minSize.width;
			me.height = minSize.height;
		},

		/**
		 * Handle margins and padding interactions
		 * @private
		 */
		handleMargins: function() {
			var me = this;
			if (me.margins) {
				me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0);
				me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0);
				me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0);
				me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0);
			}
		},

		afterFit: function() {
			helpers.callback(this.options.afterFit, [this]);
		},

		// Shared Methods
		isHorizontal: function() {
			return this.options.position === 'top' || this.options.position === 'bottom';
		},
		isFullWidth: function() {
			return (this.options.fullWidth);
		},

		// Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not
		getRightValue: function(rawValue) {
			// Null and undefined values first
			if (helpers.isNullOrUndef(rawValue)) {
				return NaN;
			}
			// isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values
			if (typeof rawValue === 'number' && !isFinite(rawValue)) {
				return NaN;
			}
			// If it is in fact an object, dive in one more level
			if (rawValue) {
				if (this.isHorizontal()) {
					if (rawValue.x !== undefined) {
						return this.getRightValue(rawValue.x);
					}
				} else if (rawValue.y !== undefined) {
					return this.getRightValue(rawValue.y);
				}
			}

			// Value is good, return it
			return rawValue;
		},

		// Used to get the value to display in the tooltip for the data at the given index
		// function getLabelForIndex(index, datasetIndex)
		getLabelForIndex: helpers.noop,

		// Used to get data value locations.  Value can either be an index or a numerical value
		getPixelForValue: helpers.noop,

		// Used to get the data value from a given pixel. This is the inverse of getPixelForValue
		getValueForPixel: helpers.noop,

		// Used for tick location, should
		getPixelForTick: function(index) {
			var me = this;
			var offset = me.options.offset;
			if (me.isHorizontal()) {
				var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
				var tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
				var pixel = (tickWidth * index) + me.paddingLeft;

				if (offset) {
					pixel += tickWidth / 2;
				}

				var finalVal = me.left + Math.round(pixel);
				finalVal += me.isFullWidth() ? me.margins.left : 0;
				return finalVal;
			}
			var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
			return me.top + (index * (innerHeight / (me._ticks.length - 1)));
		},

		// Utility for getting the pixel location of a percentage of scale
		getPixelForDecimal: function(decimal) {
			var me = this;
			if (me.isHorizontal()) {
				var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
				var valueOffset = (innerWidth * decimal) + me.paddingLeft;

				var finalVal = me.left + Math.round(valueOffset);
				finalVal += me.isFullWidth() ? me.margins.left : 0;
				return finalVal;
			}
			return me.top + (decimal * me.height);
		},

		getBasePixel: function() {
			return this.getPixelForValue(this.getBaseValue());
		},

		getBaseValue: function() {
			var me = this;
			var min = me.min;
			var max = me.max;

			return me.beginAtZero ? 0 :
				min < 0 && max < 0 ? max :
				min > 0 && max > 0 ? min :
				0;
		},

		/**
		 * Returns a subset of ticks to be plotted to avoid overlapping labels.
		 * @private
		 */
		_autoSkip: function(ticks) {
			var skipRatio;
			var me = this;
			var isHorizontal = me.isHorizontal();
			var optionTicks = me.options.ticks.minor;
			var tickCount = ticks.length;
			var labelRotationRadians = helpers.toRadians(me.labelRotation);
			var cosRotation = Math.cos(labelRotationRadians);
			var longestRotatedLabel = me.longestLabelWidth * cosRotation;
			var result = [];
			var i, tick, shouldSkip;

			// figure out the maximum number of gridlines to show
			var maxTicks;
			if (optionTicks.maxTicksLimit) {
				maxTicks = optionTicks.maxTicksLimit;
			}

			if (isHorizontal) {
				skipRatio = false;

				if ((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount > (me.width - (me.paddingLeft + me.paddingRight))) {
					skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount) / (me.width - (me.paddingLeft + me.paddingRight)));
				}

				// if they defined a max number of optionTicks,
				// increase skipRatio until that number is met
				if (maxTicks && tickCount > maxTicks) {
					skipRatio = Math.max(skipRatio, Math.floor(tickCount / maxTicks));
				}
			}

			for (i = 0; i < tickCount; i++) {
				tick = ticks[i];

				// Since we always show the last tick,we need may need to hide the last shown one before
				shouldSkip = (skipRatio > 1 && i % skipRatio > 0) || (i % skipRatio === 0 && i + skipRatio >= tickCount);
				if (shouldSkip && i !== tickCount - 1 || helpers.isNullOrUndef(tick.label)) {
					// leave tick in place but make sure it's not displayed (#4635)
					delete tick.label;
				}
				result.push(tick);
			}
			return result;
		},

		// Actually draw the scale on the canvas
		// @param {rectangle} chartArea : the area of the chart to draw full grid lines on
		draw: function(chartArea) {
			var me = this;
			var options = me.options;
			if (!options.display) {
				return;
			}

			var context = me.ctx;
			var globalDefaults = defaults.global;
			var optionTicks = options.ticks.minor;
			var optionMajorTicks = options.ticks.major || optionTicks;
			var gridLines = options.gridLines;
			var scaleLabel = options.scaleLabel;

			var isRotated = me.labelRotation !== 0;
			var isHorizontal = me.isHorizontal();

			var ticks = optionTicks.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks();
			var tickFontColor = helpers.valueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor);
			var tickFont = parseFontOptions(optionTicks);
			var majorTickFontColor = helpers.valueOrDefault(optionMajorTicks.fontColor, globalDefaults.defaultFontColor);
			var majorTickFont = parseFontOptions(optionMajorTicks);

			var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0;

			var scaleLabelFontColor = helpers.valueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor);
			var scaleLabelFont = parseFontOptions(scaleLabel);
			var scaleLabelPadding = helpers.options.toPadding(scaleLabel.padding);
			var labelRotationRadians = helpers.toRadians(me.labelRotation);

			var itemsToDraw = [];

			var xTickStart = options.position === 'right' ? me.left : me.right - tl;
			var xTickEnd = options.position === 'right' ? me.left + tl : me.right;
			var yTickStart = options.position === 'bottom' ? me.top : me.bottom - tl;
			var yTickEnd = options.position === 'bottom' ? me.top + tl : me.bottom;

			helpers.each(ticks, function(tick, index) {
				// autoskipper skipped this tick (#4635)
				if (tick.label === undefined) {
					return;
				}

				var label = tick.label;
				var lineWidth, lineColor, borderDash, borderDashOffset;
				if (index === me.zeroLineIndex && options.offset === gridLines.offsetGridLines) {
					// Draw the first index specially
					lineWidth = gridLines.zeroLineWidth;
					lineColor = gridLines.zeroLineColor;
					borderDash = gridLines.zeroLineBorderDash;
					borderDashOffset = gridLines.zeroLineBorderDashOffset;
				} else {
					lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, index);
					lineColor = helpers.valueAtIndexOrDefault(gridLines.color, index);
					borderDash = helpers.valueOrDefault(gridLines.borderDash, globalDefaults.borderDash);
					borderDashOffset = helpers.valueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset);
				}

				// Common properties
				var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY;
				var textAlign = 'middle';
				var textBaseline = 'middle';
				var tickPadding = optionTicks.padding;

				if (isHorizontal) {
					var labelYOffset = tl + tickPadding;

					if (options.position === 'bottom') {
						// bottom
						textBaseline = !isRotated ? 'top' : 'middle';
						textAlign = !isRotated ? 'center' : 'right';
						labelY = me.top + labelYOffset;
					} else {
						// top
						textBaseline = !isRotated ? 'bottom' : 'middle';
						textAlign = !isRotated ? 'center' : 'left';
						labelY = me.bottom - labelYOffset;
					}

					var xLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1);
					if (xLineValue < me.left) {
						lineColor = 'rgba(0,0,0,0)';
					}
					xLineValue += helpers.aliasPixel(lineWidth);

					labelX = me.getPixelForTick(index) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option)

					tx1 = tx2 = x1 = x2 = xLineValue;
					ty1 = yTickStart;
					ty2 = yTickEnd;
					y1 = chartArea.top;
					y2 = chartArea.bottom;
				} else {
					var isLeft = options.position === 'left';
					var labelXOffset;

					if (optionTicks.mirror) {
						textAlign = isLeft ? 'left' : 'right';
						labelXOffset = tickPadding;
					} else {
						textAlign = isLeft ? 'right' : 'left';
						labelXOffset = tl + tickPadding;
					}

					labelX = isLeft ? me.right - labelXOffset : me.left + labelXOffset;

					var yLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1);
					if (yLineValue < me.top) {
						lineColor = 'rgba(0,0,0,0)';
					}
					yLineValue += helpers.aliasPixel(lineWidth);

					labelY = me.getPixelForTick(index) + optionTicks.labelOffset;

					tx1 = xTickStart;
					tx2 = xTickEnd;
					x1 = chartArea.left;
					x2 = chartArea.right;
					ty1 = ty2 = y1 = y2 = yLineValue;
				}

				itemsToDraw.push({
					tx1: tx1,
					ty1: ty1,
					tx2: tx2,
					ty2: ty2,
					x1: x1,
					y1: y1,
					x2: x2,
					y2: y2,
					labelX: labelX,
					labelY: labelY,
					glWidth: lineWidth,
					glColor: lineColor,
					glBorderDash: borderDash,
					glBorderDashOffset: borderDashOffset,
					rotation: -1 * labelRotationRadians,
					label: label,
					major: tick.major,
					textBaseline: textBaseline,
					textAlign: textAlign
				});
			});

			// Draw all of the tick labels, tick marks, and grid lines at the correct places
			helpers.each(itemsToDraw, function(itemToDraw) {
				if (gridLines.display) {
					context.save();
					context.lineWidth = itemToDraw.glWidth;
					context.strokeStyle = itemToDraw.glColor;
					if (context.setLineDash) {
						context.setLineDash(itemToDraw.glBorderDash);
						context.lineDashOffset = itemToDraw.glBorderDashOffset;
					}

					context.beginPath();

					if (gridLines.drawTicks) {
						context.moveTo(itemToDraw.tx1, itemToDraw.ty1);
						context.lineTo(itemToDraw.tx2, itemToDraw.ty2);
					}

					if (gridLines.drawOnChartArea) {
						context.moveTo(itemToDraw.x1, itemToDraw.y1);
						context.lineTo(itemToDraw.x2, itemToDraw.y2);
					}

					context.stroke();
					context.restore();
				}

				if (optionTicks.display) {
					// Make sure we draw text in the correct color and font
					context.save();
					context.translate(itemToDraw.labelX, itemToDraw.labelY);
					context.rotate(itemToDraw.rotation);
					context.font = itemToDraw.major ? majorTickFont.font : tickFont.font;
					context.fillStyle = itemToDraw.major ? majorTickFontColor : tickFontColor;
					context.textBaseline = itemToDraw.textBaseline;
					context.textAlign = itemToDraw.textAlign;

					var label = itemToDraw.label;
					if (helpers.isArray(label)) {
						for (var i = 0, y = 0; i < label.length; ++i) {
							// We just make sure the multiline element is a string here..
							context.fillText('' + label[i], 0, y);
							// apply same lineSpacing as calculated @ L#320
							y += (tickFont.size * 1.5);
						}
					} else {
						context.fillText(label, 0, 0);
					}
					context.restore();
				}
			});

			if (scaleLabel.display) {
				// Draw the scale label
				var scaleLabelX;
				var scaleLabelY;
				var rotation = 0;
				var halfLineHeight = parseLineHeight(scaleLabel) / 2;

				if (isHorizontal) {
					scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width
					scaleLabelY = options.position === 'bottom'
						? me.bottom - halfLineHeight - scaleLabelPadding.bottom
						: me.top + halfLineHeight + scaleLabelPadding.top;
				} else {
					var isLeft = options.position === 'left';
					scaleLabelX = isLeft
						? me.left + halfLineHeight + scaleLabelPadding.top
						: me.right - halfLineHeight - scaleLabelPadding.top;
					scaleLabelY = me.top + ((me.bottom - me.top) / 2);
					rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
				}

				context.save();
				context.translate(scaleLabelX, scaleLabelY);
				context.rotate(rotation);
				context.textAlign = 'center';
				context.textBaseline = 'middle';
				context.fillStyle = scaleLabelFontColor; // render in correct colour
				context.font = scaleLabelFont.font;
				context.fillText(scaleLabel.labelString, 0, 0);
				context.restore();
			}

			if (gridLines.drawBorder) {
				// Draw the line at the edge of the axis
				context.lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, 0);
				context.strokeStyle = helpers.valueAtIndexOrDefault(gridLines.color, 0);
				var x1 = me.left;
				var x2 = me.right;
				var y1 = me.top;
				var y2 = me.bottom;

				var aliasPixel = helpers.aliasPixel(context.lineWidth);
				if (isHorizontal) {
					y1 = y2 = options.position === 'top' ? me.bottom : me.top;
					y1 += aliasPixel;
					y2 += aliasPixel;
				} else {
					x1 = x2 = options.position === 'left' ? me.right : me.left;
					x1 += aliasPixel;
					x2 += aliasPixel;
				}

				context.beginPath();
				context.moveTo(x1, y1);
				context.lineTo(x2, y2);
				context.stroke();
			}
		}
	});
};

},{"25":25,"26":26,"34":34,"45":45}],33:[function(require,module,exports){
'use strict';

var defaults = require(25);
var helpers = require(45);

module.exports = function(Chart) {

	Chart.scaleService = {
		// Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
		// use the new chart options to grab the correct scale
		constructors: {},
		// Use a registration function so that we can move to an ES6 map when we no longer need to support
		// old browsers

		// Scale config defaults
		defaults: {},
		registerScaleType: function(type, scaleConstructor, scaleDefaults) {
			this.constructors[type] = scaleConstructor;
			this.defaults[type] = helpers.clone(scaleDefaults);
		},
		getScaleConstructor: function(type) {
			return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
		},
		getScaleDefaults: function(type) {
			// Return the scale defaults merged with the global settings so that we always use the latest ones
			return this.defaults.hasOwnProperty(type) ? helpers.merge({}, [defaults.scale, this.defaults[type]]) : {};
		},
		updateScaleDefaults: function(type, additions) {
			var me = this;
			if (me.defaults.hasOwnProperty(type)) {
				me.defaults[type] = helpers.extend(me.defaults[type], additions);
			}
		},
		addScalesToLayout: function(chart) {
			// Adds each scale to the chart.boxes array to be sized accordingly
			helpers.each(chart.scales, function(scale) {
				// Set ILayoutItem parameters for backwards compatibility
				scale.fullWidth = scale.options.fullWidth;
				scale.position = scale.options.position;
				scale.weight = scale.options.weight;
				Chart.layoutService.addBox(chart, scale);
			});
		}
	};
};

},{"25":25,"45":45}],34:[function(require,module,exports){
'use strict';

var helpers = require(45);

/**
 * Namespace to hold static tick generation functions
 * @namespace Chart.Ticks
 */
module.exports = {
	/**
	 * Namespace to hold generators for different types of ticks
	 * @namespace Chart.Ticks.generators
	 */
	generators: {
		/**
		 * Interface for the options provided to the numeric tick generator
		 * @interface INumericTickGenerationOptions
		 */
		/**
		 * The maximum number of ticks to display
		 * @name INumericTickGenerationOptions#maxTicks
		 * @type Number
		 */
		/**
		 * The distance between each tick.
		 * @name INumericTickGenerationOptions#stepSize
		 * @type Number
		 * @optional
		 */
		/**
		 * Forced minimum for the ticks. If not specified, the minimum of the data range is used to calculate the tick minimum
		 * @name INumericTickGenerationOptions#min
		 * @type Number
		 * @optional
		 */
		/**
		 * The maximum value of the ticks. If not specified, the maximum of the data range is used to calculate the tick maximum
		 * @name INumericTickGenerationOptions#max
		 * @type Number
		 * @optional
		 */

		/**
		 * Generate a set of linear ticks
		 * @method Chart.Ticks.generators.linear
		 * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks
		 * @param dataRange {IRange} the range of the data
		 * @returns {Array<Number>} array of tick values
		 */
		linear: function(generationOptions, dataRange) {
			var ticks = [];
			// To get a "nice" value for the tick spacing, we will use the appropriately named
			// "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
			// for details.

			var spacing;
			if (generationOptions.stepSize && generationOptions.stepSize > 0) {
				spacing = generationOptions.stepSize;
			} else {
				var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false);
				spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true);
			}
			var niceMin = Math.floor(dataRange.min / spacing) * spacing;
			var niceMax = Math.ceil(dataRange.max / spacing) * spacing;

			// If min, max and stepSize is set and they make an evenly spaced scale use it.
			if (generationOptions.min && generationOptions.max && generationOptions.stepSize) {
				// If very close to our whole number, use it.
				if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) {
					niceMin = generationOptions.min;
					niceMax = generationOptions.max;
				}
			}

			var numSpaces = (niceMax - niceMin) / spacing;
			// If very close to our rounded value, use it.
			if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
				numSpaces = Math.round(numSpaces);
			} else {
				numSpaces = Math.ceil(numSpaces);
			}

			// Put the values into the ticks array
			ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin);
			for (var j = 1; j < numSpaces; ++j) {
				ticks.push(niceMin + (j * spacing));
			}
			ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax);

			return ticks;
		},

		/**
		 * Generate a set of logarithmic ticks
		 * @method Chart.Ticks.generators.logarithmic
		 * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks
		 * @param dataRange {IRange} the range of the data
		 * @returns {Array<Number>} array of tick values
		 */
		logarithmic: function(generationOptions, dataRange) {
			var ticks = [];
			var valueOrDefault = helpers.valueOrDefault;

			// Figure out what the max number of ticks we can support it is based on the size of
			// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
			// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
			// the graph
			var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min))));

			var endExp = Math.floor(helpers.log10(dataRange.max));
			var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
			var exp, significand;

			if (tickVal === 0) {
				exp = Math.floor(helpers.log10(dataRange.minNotZero));
				significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp));

				ticks.push(tickVal);
				tickVal = significand * Math.pow(10, exp);
			} else {
				exp = Math.floor(helpers.log10(tickVal));
				significand = Math.floor(tickVal / Math.pow(10, exp));
			}

			do {
				ticks.push(tickVal);

				++significand;
				if (significand === 10) {
					significand = 1;
					++exp;
				}

				tickVal = significand * Math.pow(10, exp);
			} while (exp < endExp || (exp === endExp && significand < endSignificand));

			var lastTick = valueOrDefault(generationOptions.max, tickVal);
			ticks.push(lastTick);

			return ticks;
		}
	},

	/**
	 * Namespace to hold formatters for different types of ticks
	 * @namespace Chart.Ticks.formatters
	 */
	formatters: {
		/**
		 * Formatter for value labels
		 * @method Chart.Ticks.formatters.values
		 * @param value the value to display
		 * @return {String|Array} the label to display
		 */
		values: function(value) {
			return helpers.isArray(value) ? value : '' + value;
		},

		/**
		 * Formatter for linear numeric ticks
		 * @method Chart.Ticks.formatters.linear
		 * @param tickValue {Number} the value to be formatted
		 * @param index {Number} the position of the tickValue parameter in the ticks array
		 * @param ticks {Array<Number>} the list of ticks being converted
		 * @return {String} string representation of the tickValue parameter
		 */
		linear: function(tickValue, index, ticks) {
			// If we have lots of ticks, don't use the ones
			var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];

			// If we have a number like 2.5 as the delta, figure out how many decimal places we need
			if (Math.abs(delta) > 1) {
				if (tickValue !== Math.floor(tickValue)) {
					// not an integer
					delta = tickValue - Math.floor(tickValue);
				}
			}

			var logDelta = helpers.log10(Math.abs(delta));
			var tickString = '';

			if (tickValue !== 0) {
				var numDecimal = -1 * Math.floor(logDelta);
				numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places
				tickString = tickValue.toFixed(numDecimal);
			} else {
				tickString = '0'; // never show decimal places for 0
			}

			return tickString;
		},

		logarithmic: function(tickValue, index, ticks) {
			var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue))));

			if (tickValue === 0) {
				return '0';
			} else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) {
				return tickValue.toExponential();
			}
			return '';
		}
	}
};

},{"45":45}],35:[function(require,module,exports){
'use strict';

var defaults = require(25);
var Element = require(26);
var helpers = require(45);

defaults._set('global', {
	tooltips: {
		enabled: true,
		custom: null,
		mode: 'nearest',
		position: 'average',
		intersect: true,
		backgroundColor: 'rgba(0,0,0,0.8)',
		titleFontStyle: 'bold',
		titleSpacing: 2,
		titleMarginBottom: 6,
		titleFontColor: '#fff',
		titleAlign: 'left',
		bodySpacing: 2,
		bodyFontColor: '#fff',
		bodyAlign: 'left',
		footerFontStyle: 'bold',
		footerSpacing: 2,
		footerMarginTop: 6,
		footerFontColor: '#fff',
		footerAlign: 'left',
		yPadding: 6,
		xPadding: 6,
		caretPadding: 2,
		caretSize: 5,
		cornerRadius: 6,
		multiKeyBackground: '#fff',
		displayColors: true,
		borderColor: 'rgba(0,0,0,0)',
		borderWidth: 0,
		callbacks: {
			// Args are: (tooltipItems, data)
			beforeTitle: helpers.noop,
			title: function(tooltipItems, data) {
				// Pick first xLabel for now
				var title = '';
				var labels = data.labels;
				var labelCount = labels ? labels.length : 0;

				if (tooltipItems.length > 0) {
					var item = tooltipItems[0];

					if (item.xLabel) {
						title = item.xLabel;
					} else if (labelCount > 0 && item.index < labelCount) {
						title = labels[item.index];
					}
				}

				return title;
			},
			afterTitle: helpers.noop,

			// Args are: (tooltipItems, data)
			beforeBody: helpers.noop,

			// Args are: (tooltipItem, data)
			beforeLabel: helpers.noop,
			label: function(tooltipItem, data) {
				var label = data.datasets[tooltipItem.datasetIndex].label || '';

				if (label) {
					label += ': ';
				}
				label += tooltipItem.yLabel;
				return label;
			},
			labelColor: function(tooltipItem, chart) {
				var meta = chart.getDatasetMeta(tooltipItem.datasetIndex);
				var activeElement = meta.data[tooltipItem.index];
				var view = activeElement._view;
				return {
					borderColor: view.borderColor,
					backgroundColor: view.backgroundColor
				};
			},
			labelTextColor: function() {
				return this._options.bodyFontColor;
			},
			afterLabel: helpers.noop,

			// Args are: (tooltipItems, data)
			afterBody: helpers.noop,

			// Args are: (tooltipItems, data)
			beforeFooter: helpers.noop,
			footer: helpers.noop,
			afterFooter: helpers.noop
		}
	}
});

module.exports = function(Chart) {

	/**
 	 * Helper method to merge the opacity into a color
 	 */
	function mergeOpacity(colorString, opacity) {
		var color = helpers.color(colorString);
		return color.alpha(opacity * color.alpha()).rgbaString();
	}

	// Helper to push or concat based on if the 2nd parameter is an array or not
	function pushOrConcat(base, toPush) {
		if (toPush) {
			if (helpers.isArray(toPush)) {
				// base = base.concat(toPush);
				Array.prototype.push.apply(base, toPush);
			} else {
				base.push(toPush);
			}
		}

		return base;
	}

	// Private helper to create a tooltip item model
	// @param element : the chart element (point, arc, bar) to create the tooltip item for
	// @return : new tooltip item
	function createTooltipItem(element) {
		var xScale = element._xScale;
		var yScale = element._yScale || element._scale; // handle radar || polarArea charts
		var index = element._index;
		var datasetIndex = element._datasetIndex;

		return {
			xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '',
			yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '',
			index: index,
			datasetIndex: datasetIndex,
			x: element._model.x,
			y: element._model.y
		};
	}

	/**
	 * Helper to get the reset model for the tooltip
	 * @param tooltipOpts {Object} the tooltip options
	 */
	function getBaseModel(tooltipOpts) {
		var globalDefaults = defaults.global;
		var valueOrDefault = helpers.valueOrDefault;

		return {
			// Positioning
			xPadding: tooltipOpts.xPadding,
			yPadding: tooltipOpts.yPadding,
			xAlign: tooltipOpts.xAlign,
			yAlign: tooltipOpts.yAlign,

			// Body
			bodyFontColor: tooltipOpts.bodyFontColor,
			_bodyFontFamily: valueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),
			_bodyFontStyle: valueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),
			_bodyAlign: tooltipOpts.bodyAlign,
			bodyFontSize: valueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),
			bodySpacing: tooltipOpts.bodySpacing,

			// Title
			titleFontColor: tooltipOpts.titleFontColor,
			_titleFontFamily: valueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),
			_titleFontStyle: valueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),
			titleFontSize: valueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),
			_titleAlign: tooltipOpts.titleAlign,
			titleSpacing: tooltipOpts.titleSpacing,
			titleMarginBottom: tooltipOpts.titleMarginBottom,

			// Footer
			footerFontColor: tooltipOpts.footerFontColor,
			_footerFontFamily: valueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),
			_footerFontStyle: valueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),
			footerFontSize: valueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),
			_footerAlign: tooltipOpts.footerAlign,
			footerSpacing: tooltipOpts.footerSpacing,
			footerMarginTop: tooltipOpts.footerMarginTop,

			// Appearance
			caretSize: tooltipOpts.caretSize,
			cornerRadius: tooltipOpts.cornerRadius,
			backgroundColor: tooltipOpts.backgroundColor,
			opacity: 0,
			legendColorBackground: tooltipOpts.multiKeyBackground,
			displayColors: tooltipOpts.displayColors,
			borderColor: tooltipOpts.borderColor,
			borderWidth: tooltipOpts.borderWidth
		};
	}

	/**
	 * Get the size of the tooltip
	 */
	function getTooltipSize(tooltip, model) {
		var ctx = tooltip._chart.ctx;

		var height = model.yPadding * 2; // Tooltip Padding
		var width = 0;

		// Count of all lines in the body
		var body = model.body;
		var combinedBodyLength = body.reduce(function(count, bodyItem) {
			return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length;
		}, 0);
		combinedBodyLength += model.beforeBody.length + model.afterBody.length;

		var titleLineCount = model.title.length;
		var footerLineCount = model.footer.length;
		var titleFontSize = model.titleFontSize;
		var bodyFontSize = model.bodyFontSize;
		var footerFontSize = model.footerFontSize;

		height += titleLineCount * titleFontSize; // Title Lines
		height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing
		height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin
		height += combinedBodyLength * bodyFontSize; // Body Lines
		height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing
		height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin
		height += footerLineCount * (footerFontSize); // Footer Lines
		height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing

		// Title width
		var widthPadding = 0;
		var maxLineWidth = function(line) {
			width = Math.max(width, ctx.measureText(line).width + widthPadding);
		};

		ctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily);
		helpers.each(model.title, maxLineWidth);

		// Body width
		ctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily);
		helpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth);

		// Body lines may include some extra width due to the color box
		widthPadding = model.displayColors ? (bodyFontSize + 2) : 0;
		helpers.each(body, function(bodyItem) {
			helpers.each(bodyItem.before, maxLineWidth);
			helpers.each(bodyItem.lines, maxLineWidth);
			helpers.each(bodyItem.after, maxLineWidth);
		});

		// Reset back to 0
		widthPadding = 0;

		// Footer width
		ctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily);
		helpers.each(model.footer, maxLineWidth);

		// Add padding
		width += 2 * model.xPadding;

		return {
			width: width,
			height: height
		};
	}

	/**
	 * Helper to get the alignment of a tooltip given the size
	 */
	function determineAlignment(tooltip, size) {
		var model = tooltip._model;
		var chart = tooltip._chart;
		var chartArea = tooltip._chart.chartArea;
		var xAlign = 'center';
		var yAlign = 'center';

		if (model.y < size.height) {
			yAlign = 'top';
		} else if (model.y > (chart.height - size.height)) {
			yAlign = 'bottom';
		}

		var lf, rf; // functions to determine left, right alignment
		var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart
		var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges
		var midX = (chartArea.left + chartArea.right) / 2;
		var midY = (chartArea.top + chartArea.bottom) / 2;

		if (yAlign === 'center') {
			lf = function(x) {
				return x <= midX;
			};
			rf = function(x) {
				return x > midX;
			};
		} else {
			lf = function(x) {
				return x <= (size.width / 2);
			};
			rf = function(x) {
				return x >= (chart.width - (size.width / 2));
			};
		}

		olf = function(x) {
			return x + size.width > chart.width;
		};
		orf = function(x) {
			return x - size.width < 0;
		};
		yf = function(y) {
			return y <= midY ? 'top' : 'bottom';
		};

		if (lf(model.x)) {
			xAlign = 'left';

			// Is tooltip too wide and goes over the right side of the chart.?
			if (olf(model.x)) {
				xAlign = 'center';
				yAlign = yf(model.y);
			}
		} else if (rf(model.x)) {
			xAlign = 'right';

			// Is tooltip too wide and goes outside left edge of canvas?
			if (orf(model.x)) {
				xAlign = 'center';
				yAlign = yf(model.y);
			}
		}

		var opts = tooltip._options;
		return {
			xAlign: opts.xAlign ? opts.xAlign : xAlign,
			yAlign: opts.yAlign ? opts.yAlign : yAlign
		};
	}

	/**
	 * @Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment
	 */
	function getBackgroundPoint(vm, size, alignment) {
		// Background Position
		var x = vm.x;
		var y = vm.y;

		var caretSize = vm.caretSize;
		var caretPadding = vm.caretPadding;
		var cornerRadius = vm.cornerRadius;
		var xAlign = alignment.xAlign;
		var yAlign = alignment.yAlign;
		var paddingAndSize = caretSize + caretPadding;
		var radiusAndPadding = cornerRadius + caretPadding;

		if (xAlign === 'right') {
			x -= size.width;
		} else if (xAlign === 'center') {
			x -= (size.width / 2);
		}

		if (yAlign === 'top') {
			y += paddingAndSize;
		} else if (yAlign === 'bottom') {
			y -= size.height + paddingAndSize;
		} else {
			y -= (size.height / 2);
		}

		if (yAlign === 'center') {
			if (xAlign === 'left') {
				x += paddingAndSize;
			} else if (xAlign === 'right') {
				x -= paddingAndSize;
			}
		} else if (xAlign === 'left') {
			x -= radiusAndPadding;
		} else if (xAlign === 'right') {
			x += radiusAndPadding;
		}

		return {
			x: x,
			y: y
		};
	}

	Chart.Tooltip = Element.extend({
		initialize: function() {
			this._model = getBaseModel(this._options);
		},

		// Get the title
		// Args are: (tooltipItem, data)
		getTitle: function() {
			var me = this;
			var opts = me._options;
			var callbacks = opts.callbacks;

			var beforeTitle = callbacks.beforeTitle.apply(me, arguments);
			var title = callbacks.title.apply(me, arguments);
			var afterTitle = callbacks.afterTitle.apply(me, arguments);

			var lines = [];
			lines = pushOrConcat(lines, beforeTitle);
			lines = pushOrConcat(lines, title);
			lines = pushOrConcat(lines, afterTitle);

			return lines;
		},

		// Args are: (tooltipItem, data)
		getBeforeBody: function() {
			var lines = this._options.callbacks.beforeBody.apply(this, arguments);
			return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
		},

		// Args are: (tooltipItem, data)
		getBody: function(tooltipItems, data) {
			var me = this;
			var callbacks = me._options.callbacks;
			var bodyItems = [];

			helpers.each(tooltipItems, function(tooltipItem) {
				var bodyItem = {
					before: [],
					lines: [],
					after: []
				};
				pushOrConcat(bodyItem.before, callbacks.beforeLabel.call(me, tooltipItem, data));
				pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data));
				pushOrConcat(bodyItem.after, callbacks.afterLabel.call(me, tooltipItem, data));

				bodyItems.push(bodyItem);
			});

			return bodyItems;
		},

		// Args are: (tooltipItem, data)
		getAfterBody: function() {
			var lines = this._options.callbacks.afterBody.apply(this, arguments);
			return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
		},

		// Get the footer and beforeFooter and afterFooter lines
		// Args are: (tooltipItem, data)
		getFooter: function() {
			var me = this;
			var callbacks = me._options.callbacks;

			var beforeFooter = callbacks.beforeFooter.apply(me, arguments);
			var footer = callbacks.footer.apply(me, arguments);
			var afterFooter = callbacks.afterFooter.apply(me, arguments);

			var lines = [];
			lines = pushOrConcat(lines, beforeFooter);
			lines = pushOrConcat(lines, footer);
			lines = pushOrConcat(lines, afterFooter);

			return lines;
		},

		update: function(changed) {
			var me = this;
			var opts = me._options;

			// Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition
			// that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time
			// which breaks any animations.
			var existingModel = me._model;
			var model = me._model = getBaseModel(opts);
			var active = me._active;

			var data = me._data;

			// In the case where active.length === 0 we need to keep these at existing values for good animations
			var alignment = {
				xAlign: existingModel.xAlign,
				yAlign: existingModel.yAlign
			};
			var backgroundPoint = {
				x: existingModel.x,
				y: existingModel.y
			};
			var tooltipSize = {
				width: existingModel.width,
				height: existingModel.height
			};
			var tooltipPosition = {
				x: existingModel.caretX,
				y: existingModel.caretY
			};

			var i, len;

			if (active.length) {
				model.opacity = 1;

				var labelColors = [];
				var labelTextColors = [];
				tooltipPosition = Chart.Tooltip.positioners[opts.position](active, me._eventPosition);

				var tooltipItems = [];
				for (i = 0, len = active.length; i < len; ++i) {
					tooltipItems.push(createTooltipItem(active[i]));
				}

				// If the user provided a filter function, use it to modify the tooltip items
				if (opts.filter) {
					tooltipItems = tooltipItems.filter(function(a) {
						return opts.filter(a, data);
					});
				}

				// If the user provided a sorting function, use it to modify the tooltip items
				if (opts.itemSort) {
					tooltipItems = tooltipItems.sort(function(a, b) {
						return opts.itemSort(a, b, data);
					});
				}

				// Determine colors for boxes
				helpers.each(tooltipItems, function(tooltipItem) {
					labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart));
					labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart));
				});


				// Build the Text Lines
				model.title = me.getTitle(tooltipItems, data);
				model.beforeBody = me.getBeforeBody(tooltipItems, data);
				model.body = me.getBody(tooltipItems, data);
				model.afterBody = me.getAfterBody(tooltipItems, data);
				model.footer = me.getFooter(tooltipItems, data);

				// Initial positioning and colors
				model.x = Math.round(tooltipPosition.x);
				model.y = Math.round(tooltipPosition.y);
				model.caretPadding = opts.caretPadding;
				model.labelColors = labelColors;
				model.labelTextColors = labelTextColors;

				// data points
				model.dataPoints = tooltipItems;

				// We need to determine alignment of the tooltip
				tooltipSize = getTooltipSize(this, model);
				alignment = determineAlignment(this, tooltipSize);
				// Final Size and Position
				backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment);
			} else {
				model.opacity = 0;
			}

			model.xAlign = alignment.xAlign;
			model.yAlign = alignment.yAlign;
			model.x = backgroundPoint.x;
			model.y = backgroundPoint.y;
			model.width = tooltipSize.width;
			model.height = tooltipSize.height;

			// Point where the caret on the tooltip points to
			model.caretX = tooltipPosition.x;
			model.caretY = tooltipPosition.y;

			me._model = model;

			if (changed && opts.custom) {
				opts.custom.call(me, model);
			}

			return me;
		},
		drawCaret: function(tooltipPoint, size) {
			var ctx = this._chart.ctx;
			var vm = this._view;
			var caretPosition = this.getCaretPosition(tooltipPoint, size, vm);

			ctx.lineTo(caretPosition.x1, caretPosition.y1);
			ctx.lineTo(caretPosition.x2, caretPosition.y2);
			ctx.lineTo(caretPosition.x3, caretPosition.y3);
		},
		getCaretPosition: function(tooltipPoint, size, vm) {
			var x1, x2, x3, y1, y2, y3;
			var caretSize = vm.caretSize;
			var cornerRadius = vm.cornerRadius;
			var xAlign = vm.xAlign;
			var yAlign = vm.yAlign;
			var ptX = tooltipPoint.x;
			var ptY = tooltipPoint.y;
			var width = size.width;
			var height = size.height;

			if (yAlign === 'center') {
				y2 = ptY + (height / 2);

				if (xAlign === 'left') {
					x1 = ptX;
					x2 = x1 - caretSize;
					x3 = x1;

					y1 = y2 + caretSize;
					y3 = y2 - caretSize;
				} else {
					x1 = ptX + width;
					x2 = x1 + caretSize;
					x3 = x1;

					y1 = y2 - caretSize;
					y3 = y2 + caretSize;
				}
			} else {
				if (xAlign === 'left') {
					x2 = ptX + cornerRadius + (caretSize);
					x1 = x2 - caretSize;
					x3 = x2 + caretSize;
				} else if (xAlign === 'right') {
					x2 = ptX + width - cornerRadius - caretSize;
					x1 = x2 - caretSize;
					x3 = x2 + caretSize;
				} else {
					x2 = ptX + (width / 2);
					x1 = x2 - caretSize;
					x3 = x2 + caretSize;
				}
				if (yAlign === 'top') {
					y1 = ptY;
					y2 = y1 - caretSize;
					y3 = y1;
				} else {
					y1 = ptY + height;
					y2 = y1 + caretSize;
					y3 = y1;
					// invert drawing order
					var tmp = x3;
					x3 = x1;
					x1 = tmp;
				}
			}
			return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3};
		},
		drawTitle: function(pt, vm, ctx, opacity) {
			var title = vm.title;

			if (title.length) {
				ctx.textAlign = vm._titleAlign;
				ctx.textBaseline = 'top';

				var titleFontSize = vm.titleFontSize;
				var titleSpacing = vm.titleSpacing;

				ctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity);
				ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);

				var i, len;
				for (i = 0, len = title.length; i < len; ++i) {
					ctx.fillText(title[i], pt.x, pt.y);
					pt.y += titleFontSize + titleSpacing; // Line Height and spacing

					if (i + 1 === title.length) {
						pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing
					}
				}
			}
		},
		drawBody: function(pt, vm, ctx, opacity) {
			var bodyFontSize = vm.bodyFontSize;
			var bodySpacing = vm.bodySpacing;
			var body = vm.body;

			ctx.textAlign = vm._bodyAlign;
			ctx.textBaseline = 'top';
			ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);

			// Before Body
			var xLinePadding = 0;
			var fillLineOfText = function(line) {
				ctx.fillText(line, pt.x + xLinePadding, pt.y);
				pt.y += bodyFontSize + bodySpacing;
			};

			// Before body lines
			helpers.each(vm.beforeBody, fillLineOfText);

			var drawColorBoxes = vm.displayColors;
			xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0;

			// Draw body lines now
			helpers.each(body, function(bodyItem, i) {
				helpers.each(bodyItem.before, fillLineOfText);

				helpers.each(bodyItem.lines, function(line) {
					// Draw Legend-like boxes if needed
					if (drawColorBoxes) {
						// Fill a white rect so that colours merge nicely if the opacity is < 1
						ctx.fillStyle = mergeOpacity(vm.legendColorBackground, opacity);
						ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize);

						// Border
						ctx.lineWidth = 1;
						ctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity);
						ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize);

						// Inner square
						ctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity);
						ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
						var textColor = mergeOpacity(vm.labelTextColors[i], opacity);
						ctx.fillStyle = textColor;
					}

					fillLineOfText(line);
				});

				helpers.each(bodyItem.after, fillLineOfText);
			});

			// Reset back to 0 for after body
			xLinePadding = 0;

			// After body lines
			helpers.each(vm.afterBody, fillLineOfText);
			pt.y -= bodySpacing; // Remove last body spacing
		},
		drawFooter: function(pt, vm, ctx, opacity) {
			var footer = vm.footer;

			if (footer.length) {
				pt.y += vm.footerMarginTop;

				ctx.textAlign = vm._footerAlign;
				ctx.textBaseline = 'top';

				ctx.fillStyle = mergeOpacity(vm.footerFontColor, opacity);
				ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);

				helpers.each(footer, function(line) {
					ctx.fillText(line, pt.x, pt.y);
					pt.y += vm.footerFontSize + vm.footerSpacing;
				});
			}
		},
		drawBackground: function(pt, vm, ctx, tooltipSize, opacity) {
			ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity);
			ctx.strokeStyle = mergeOpacity(vm.borderColor, opacity);
			ctx.lineWidth = vm.borderWidth;
			var xAlign = vm.xAlign;
			var yAlign = vm.yAlign;
			var x = pt.x;
			var y = pt.y;
			var width = tooltipSize.width;
			var height = tooltipSize.height;
			var radius = vm.cornerRadius;

			ctx.beginPath();
			ctx.moveTo(x + radius, y);
			if (yAlign === 'top') {
				this.drawCaret(pt, tooltipSize);
			}
			ctx.lineTo(x + width - radius, y);
			ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
			if (yAlign === 'center' && xAlign === 'right') {
				this.drawCaret(pt, tooltipSize);
			}
			ctx.lineTo(x + width, y + height - radius);
			ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
			if (yAlign === 'bottom') {
				this.drawCaret(pt, tooltipSize);
			}
			ctx.lineTo(x + radius, y + height);
			ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
			if (yAlign === 'center' && xAlign === 'left') {
				this.drawCaret(pt, tooltipSize);
			}
			ctx.lineTo(x, y + radius);
			ctx.quadraticCurveTo(x, y, x + radius, y);
			ctx.closePath();

			ctx.fill();

			if (vm.borderWidth > 0) {
				ctx.stroke();
			}
		},
		draw: function() {
			var ctx = this._chart.ctx;
			var vm = this._view;

			if (vm.opacity === 0) {
				return;
			}

			var tooltipSize = {
				width: vm.width,
				height: vm.height
			};
			var pt = {
				x: vm.x,
				y: vm.y
			};

			// IE11/Edge does not like very small opacities, so snap to 0
			var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;

			// Truthy/falsey value for empty tooltip
			var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length;

			if (this._options.enabled && hasTooltipContent) {
				// Draw Background
				this.drawBackground(pt, vm, ctx, tooltipSize, opacity);

				// Draw Title, Body, and Footer
				pt.x += vm.xPadding;
				pt.y += vm.yPadding;

				// Titles
				this.drawTitle(pt, vm, ctx, opacity);

				// Body
				this.drawBody(pt, vm, ctx, opacity);

				// Footer
				this.drawFooter(pt, vm, ctx, opacity);
			}
		},

		/**
		 * Handle an event
		 * @private
		 * @param {IEvent} event - The event to handle
		 * @returns {Boolean} true if the tooltip changed
		 */
		handleEvent: function(e) {
			var me = this;
			var options = me._options;
			var changed = false;

			me._lastActive = me._lastActive || [];

			// Find Active Elements for tooltips
			if (e.type === 'mouseout') {
				me._active = [];
			} else {
				me._active = me._chart.getElementsAtEventForMode(e, options.mode, options);
			}

			// Remember Last Actives
			changed = !helpers.arrayEquals(me._active, me._lastActive);

			// If tooltip didn't change, do not handle the target event
			if (!changed) {
				return false;
			}

			me._lastActive = me._active;

			if (options.enabled || options.custom) {
				me._eventPosition = {
					x: e.x,
					y: e.y
				};

				var model = me._model;
				me.update(true);
				me.pivot();

				// See if our tooltip position changed
				changed |= (model.x !== me._model.x) || (model.y !== me._model.y);
			}

			return changed;
		}
	});

	/**
	 * @namespace Chart.Tooltip.positioners
	 */
	Chart.Tooltip.positioners = {
		/**
		 * Average mode places the tooltip at the average position of the elements shown
		 * @function Chart.Tooltip.positioners.average
		 * @param elements {ChartElement[]} the elements being displayed in the tooltip
		 * @returns {Point} tooltip position
		 */
		average: function(elements) {
			if (!elements.length) {
				return false;
			}

			var i, len;
			var x = 0;
			var y = 0;
			var count = 0;

			for (i = 0, len = elements.length; i < len; ++i) {
				var el = elements[i];
				if (el && el.hasValue()) {
					var pos = el.tooltipPosition();
					x += pos.x;
					y += pos.y;
					++count;
				}
			}

			return {
				x: Math.round(x / count),
				y: Math.round(y / count)
			};
		},

		/**
		 * Gets the tooltip position nearest of the item nearest to the event position
		 * @function Chart.Tooltip.positioners.nearest
		 * @param elements {Chart.Element[]} the tooltip elements
		 * @param eventPosition {Point} the position of the event in canvas coordinates
		 * @returns {Point} the tooltip position
		 */
		nearest: function(elements, eventPosition) {
			var x = eventPosition.x;
			var y = eventPosition.y;
			var minDistance = Number.POSITIVE_INFINITY;
			var i, len, nearestElement;

			for (i = 0, len = elements.length; i < len; ++i) {
				var el = elements[i];
				if (el && el.hasValue()) {
					var center = el.getCenterPoint();
					var d = helpers.distanceBetweenPoints(eventPosition, center);

					if (d < minDistance) {
						minDistance = d;
						nearestElement = el;
					}
				}
			}

			if (nearestElement) {
				var tp = nearestElement.tooltipPosition();
				x = tp.x;
				y = tp.y;
			}

			return {
				x: x,
				y: y
			};
		}
	};
};

},{"25":25,"26":26,"45":45}],36:[function(require,module,exports){
'use strict';

var defaults = require(25);
var Element = require(26);
var helpers = require(45);

defaults._set('global', {
	elements: {
		arc: {
			backgroundColor: defaults.global.defaultColor,
			borderColor: '#fff',
			borderWidth: 2
		}
	}
});

module.exports = Element.extend({
	inLabelRange: function(mouseX) {
		var vm = this._view;

		if (vm) {
			return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
		}
		return false;
	},

	inRange: function(chartX, chartY) {
		var vm = this._view;

		if (vm) {
			var pointRelativePosition = helpers.getAngleFromPoint(vm, {x: chartX, y: chartY});
			var	angle = pointRelativePosition.angle;
			var distance = pointRelativePosition.distance;

			// Sanitise angle range
			var startAngle = vm.startAngle;
			var endAngle = vm.endAngle;
			while (endAngle < startAngle) {
				endAngle += 2.0 * Math.PI;
			}
			while (angle > endAngle) {
				angle -= 2.0 * Math.PI;
			}
			while (angle < startAngle) {
				angle += 2.0 * Math.PI;
			}

			// Check if within the range of the open/close angle
			var betweenAngles = (angle >= startAngle && angle <= endAngle);
			var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);

			return (betweenAngles && withinRadius);
		}
		return false;
	},

	getCenterPoint: function() {
		var vm = this._view;
		var halfAngle = (vm.startAngle + vm.endAngle) / 2;
		var halfRadius = (vm.innerRadius + vm.outerRadius) / 2;
		return {
			x: vm.x + Math.cos(halfAngle) * halfRadius,
			y: vm.y + Math.sin(halfAngle) * halfRadius
		};
	},

	getArea: function() {
		var vm = this._view;
		return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2));
	},

	tooltipPosition: function() {
		var vm = this._view;
		var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2);
		var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;

		return {
			x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
			y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
		};
	},

	draw: function() {
		var ctx = this._chart.ctx;
		var vm = this._view;
		var sA = vm.startAngle;
		var eA = vm.endAngle;

		ctx.beginPath();

		ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);
		ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);

		ctx.closePath();
		ctx.strokeStyle = vm.borderColor;
		ctx.lineWidth = vm.borderWidth;

		ctx.fillStyle = vm.backgroundColor;

		ctx.fill();
		ctx.lineJoin = 'bevel';

		if (vm.borderWidth) {
			ctx.stroke();
		}
	}
});

},{"25":25,"26":26,"45":45}],37:[function(require,module,exports){
'use strict';

var defaults = require(25);
var Element = require(26);
var helpers = require(45);

var globalDefaults = defaults.global;

defaults._set('global', {
	elements: {
		line: {
			tension: 0.4,
			backgroundColor: globalDefaults.defaultColor,
			borderWidth: 3,
			borderColor: globalDefaults.defaultColor,
			borderCapStyle: 'butt',
			borderDash: [],
			borderDashOffset: 0.0,
			borderJoinStyle: 'miter',
			capBezierPoints: true,
			fill: true, // do we fill in the area between the line and its base axis
		}
	}
});

module.exports = Element.extend({
	draw: function() {
		var me = this;
		var vm = me._view;
		var ctx = me._chart.ctx;
		var spanGaps = vm.spanGaps;
		var points = me._children.slice(); // clone array
		var globalOptionLineElements = globalDefaults.elements.line;
		var lastDrawnIndex = -1;
		var index, current, previous, currentVM;

		// If we are looping, adding the first point again
		if (me._loop && points.length) {
			points.push(points[0]);
		}

		ctx.save();

		// Stroke Line Options
		ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle;

		// IE 9 and 10 do not support line dash
		if (ctx.setLineDash) {
			ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);
		}

		ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset;
		ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;
		ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth;
		ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor;

		// Stroke Line
		ctx.beginPath();
		lastDrawnIndex = -1;

		for (index = 0; index < points.length; ++index) {
			current = points[index];
			previous = helpers.previousItem(points, index);
			currentVM = current._view;

			// First point moves to it's starting position no matter what
			if (index === 0) {
				if (!currentVM.skip) {
					ctx.moveTo(currentVM.x, currentVM.y);
					lastDrawnIndex = index;
				}
			} else {
				previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex];

				if (!currentVM.skip) {
					if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) {
						// There was a gap and this is the first point after the gap
						ctx.moveTo(currentVM.x, currentVM.y);
					} else {
						// Line to next point
						helpers.canvas.lineTo(ctx, previous._view, current._view);
					}
					lastDrawnIndex = index;
				}
			}
		}

		ctx.stroke();
		ctx.restore();
	}
});

},{"25":25,"26":26,"45":45}],38:[function(require,module,exports){
'use strict';

var defaults = require(25);
var Element = require(26);
var helpers = require(45);

var defaultColor = defaults.global.defaultColor;

defaults._set('global', {
	elements: {
		point: {
			radius: 3,
			pointStyle: 'circle',
			backgroundColor: defaultColor,
			borderColor: defaultColor,
			borderWidth: 1,
			// Hover
			hitRadius: 1,
			hoverRadius: 4,
			hoverBorderWidth: 1
		}
	}
});

function xRange(mouseX) {
	var vm = this._view;
	return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
}

function yRange(mouseY) {
	var vm = this._view;
	return vm ? (Math.pow(mouseY - vm.y, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
}

module.exports = Element.extend({
	inRange: function(mouseX, mouseY) {
		var vm = this._view;
		return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;
	},

	inLabelRange: xRange,
	inXRange: xRange,
	inYRange: yRange,

	getCenterPoint: function() {
		var vm = this._view;
		return {
			x: vm.x,
			y: vm.y
		};
	},

	getArea: function() {
		return Math.PI * Math.pow(this._view.radius, 2);
	},

	tooltipPosition: function() {
		var vm = this._view;
		return {
			x: vm.x,
			y: vm.y,
			padding: vm.radius + vm.borderWidth
		};
	},

	draw: function(chartArea) {
		var vm = this._view;
		var model = this._model;
		var ctx = this._chart.ctx;
		var pointStyle = vm.pointStyle;
		var radius = vm.radius;
		var x = vm.x;
		var y = vm.y;
		var color = helpers.color;
		var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.)
		var ratio = 0;

		if (vm.skip) {
			return;
		}

		ctx.strokeStyle = vm.borderColor || defaultColor;
		ctx.lineWidth = helpers.valueOrDefault(vm.borderWidth, defaults.global.elements.point.borderWidth);
		ctx.fillStyle = vm.backgroundColor || defaultColor;

		// Cliping for Points.
		// going out from inner charArea?
		if ((chartArea !== undefined) && ((model.x < chartArea.left) || (chartArea.right * errMargin < model.x) || (model.y < chartArea.top) || (chartArea.bottom * errMargin < model.y))) {
			// Point fade out
			if (model.x < chartArea.left) {
				ratio = (x - model.x) / (chartArea.left - model.x);
			} else if (chartArea.right * errMargin < model.x) {
				ratio = (model.x - x) / (model.x - chartArea.right);
			} else if (model.y < chartArea.top) {
				ratio = (y - model.y) / (chartArea.top - model.y);
			} else if (chartArea.bottom * errMargin < model.y) {
				ratio = (model.y - y) / (model.y - chartArea.bottom);
			}
			ratio = Math.round(ratio * 100) / 100;
			ctx.strokeStyle = color(ctx.strokeStyle).alpha(ratio).rgbString();
			ctx.fillStyle = color(ctx.fillStyle).alpha(ratio).rgbString();
		}

		helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y);
	}
});

},{"25":25,"26":26,"45":45}],39:[function(require,module,exports){
'use strict';

var defaults = require(25);
var Element = require(26);

defaults._set('global', {
	elements: {
		rectangle: {
			backgroundColor: defaults.global.defaultColor,
			borderColor: defaults.global.defaultColor,
			borderSkipped: 'bottom',
			borderWidth: 0
		}
	}
});

function isVertical(bar) {
	return bar._view.width !== undefined;
}

/**
 * Helper function to get the bounds of the bar regardless of the orientation
 * @param bar {Chart.Element.Rectangle} the bar
 * @return {Bounds} bounds of the bar
 * @private
 */
function getBarBounds(bar) {
	var vm = bar._view;
	var x1, x2, y1, y2;

	if (isVertical(bar)) {
		// vertical
		var halfWidth = vm.width / 2;
		x1 = vm.x - halfWidth;
		x2 = vm.x + halfWidth;
		y1 = Math.min(vm.y, vm.base);
		y2 = Math.max(vm.y, vm.base);
	} else {
		// horizontal bar
		var halfHeight = vm.height / 2;
		x1 = Math.min(vm.x, vm.base);
		x2 = Math.max(vm.x, vm.base);
		y1 = vm.y - halfHeight;
		y2 = vm.y + halfHeight;
	}

	return {
		left: x1,
		top: y1,
		right: x2,
		bottom: y2
	};
}

module.exports = Element.extend({
	draw: function() {
		var ctx = this._chart.ctx;
		var vm = this._view;
		var left, right, top, bottom, signX, signY, borderSkipped;
		var borderWidth = vm.borderWidth;

		if (!vm.horizontal) {
			// bar
			left = vm.x - vm.width / 2;
			right = vm.x + vm.width / 2;
			top = vm.y;
			bottom = vm.base;
			signX = 1;
			signY = bottom > top ? 1 : -1;
			borderSkipped = vm.borderSkipped || 'bottom';
		} else {
			// horizontal bar
			left = vm.base;
			right = vm.x;
			top = vm.y - vm.height / 2;
			bottom = vm.y + vm.height / 2;
			signX = right > left ? 1 : -1;
			signY = 1;
			borderSkipped = vm.borderSkipped || 'left';
		}

		// Canvas doesn't allow us to stroke inside the width so we can
		// adjust the sizes to fit if we're setting a stroke on the line
		if (borderWidth) {
			// borderWidth shold be less than bar width and bar height.
			var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));
			borderWidth = borderWidth > barSize ? barSize : borderWidth;
			var halfStroke = borderWidth / 2;
			// Adjust borderWidth when bar top position is near vm.base(zero).
			var borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0);
			var borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0);
			var borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0);
			var borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0);
			// not become a vertical line?
			if (borderLeft !== borderRight) {
				top = borderTop;
				bottom = borderBottom;
			}
			// not become a horizontal line?
			if (borderTop !== borderBottom) {
				left = borderLeft;
				right = borderRight;
			}
		}

		ctx.beginPath();
		ctx.fillStyle = vm.backgroundColor;
		ctx.strokeStyle = vm.borderColor;
		ctx.lineWidth = borderWidth;

		// Corner points, from bottom-left to bottom-right clockwise
		// | 1 2 |
		// | 0 3 |
		var corners = [
			[left, bottom],
			[left, top],
			[right, top],
			[right, bottom]
		];

		// Find first (starting) corner with fallback to 'bottom'
		var borders = ['bottom', 'left', 'top', 'right'];
		var startCorner = borders.indexOf(borderSkipped, 0);
		if (startCorner === -1) {
			startCorner = 0;
		}

		function cornerAt(index) {
			return corners[(startCorner + index) % 4];
		}

		// Draw rectangle from 'startCorner'
		var corner = cornerAt(0);
		ctx.moveTo(corner[0], corner[1]);

		for (var i = 1; i < 4; i++) {
			corner = cornerAt(i);
			ctx.lineTo(corner[0], corner[1]);
		}

		ctx.fill();
		if (borderWidth) {
			ctx.stroke();
		}
	},

	height: function() {
		var vm = this._view;
		return vm.base - vm.y;
	},

	inRange: function(mouseX, mouseY) {
		var inRange = false;

		if (this._view) {
			var bounds = getBarBounds(this);
			inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom;
		}

		return inRange;
	},

	inLabelRange: function(mouseX, mouseY) {
		var me = this;
		if (!me._view) {
			return false;
		}

		var inRange = false;
		var bounds = getBarBounds(me);

		if (isVertical(me)) {
			inRange = mouseX >= bounds.left && mouseX <= bounds.right;
		} else {
			inRange = mouseY >= bounds.top && mouseY <= bounds.bottom;
		}

		return inRange;
	},

	inXRange: function(mouseX) {
		var bounds = getBarBounds(this);
		return mouseX >= bounds.left && mouseX <= bounds.right;
	},

	inYRange: function(mouseY) {
		var bounds = getBarBounds(this);
		return mouseY >= bounds.top && mouseY <= bounds.bottom;
	},

	getCenterPoint: function() {
		var vm = this._view;
		var x, y;
		if (isVertical(this)) {
			x = vm.x;
			y = (vm.y + vm.base) / 2;
		} else {
			x = (vm.x + vm.base) / 2;
			y = vm.y;
		}

		return {x: x, y: y};
	},

	getArea: function() {
		var vm = this._view;
		return vm.width * Math.abs(vm.y - vm.base);
	},

	tooltipPosition: function() {
		var vm = this._view;
		return {
			x: vm.x,
			y: vm.y
		};
	}
});

},{"25":25,"26":26}],40:[function(require,module,exports){
'use strict';

module.exports = {};
module.exports.Arc = require(36);
module.exports.Line = require(37);
module.exports.Point = require(38);
module.exports.Rectangle = require(39);

},{"36":36,"37":37,"38":38,"39":39}],41:[function(require,module,exports){
'use strict';

var helpers = require(42);

/**
 * @namespace Chart.helpers.canvas
 */
var exports = module.exports = {
	/**
	 * Clears the entire canvas associated to the given `chart`.
	 * @param {Chart} chart - The chart for which to clear the canvas.
	 */
	clear: function(chart) {
		chart.ctx.clearRect(0, 0, chart.width, chart.height);
	},

	/**
	 * Creates a "path" for a rectangle with rounded corners at position (x, y) with a
	 * given size (width, height) and the same `radius` for all corners.
	 * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context.
	 * @param {Number} x - The x axis of the coordinate for the rectangle starting point.
	 * @param {Number} y - The y axis of the coordinate for the rectangle starting point.
	 * @param {Number} width - The rectangle's width.
	 * @param {Number} height - The rectangle's height.
	 * @param {Number} radius - The rounded amount (in pixels) for the four corners.
	 * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object?
	 */
	roundedRect: function(ctx, x, y, width, height, radius) {
		if (radius) {
			var rx = Math.min(radius, width / 2);
			var ry = Math.min(radius, height / 2);

			ctx.moveTo(x + rx, y);
			ctx.lineTo(x + width - rx, y);
			ctx.quadraticCurveTo(x + width, y, x + width, y + ry);
			ctx.lineTo(x + width, y + height - ry);
			ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height);
			ctx.lineTo(x + rx, y + height);
			ctx.quadraticCurveTo(x, y + height, x, y + height - ry);
			ctx.lineTo(x, y + ry);
			ctx.quadraticCurveTo(x, y, x + rx, y);
		} else {
			ctx.rect(x, y, width, height);
		}
	},

	drawPoint: function(ctx, style, radius, x, y) {
		var type, edgeLength, xOffset, yOffset, height, size;

		if (typeof style === 'object') {
			type = style.toString();
			if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
				ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height);
				return;
			}
		}

		if (isNaN(radius) || radius <= 0) {
			return;
		}

		switch (style) {
		// Default includes circle
		default:
			ctx.beginPath();
			ctx.arc(x, y, radius, 0, Math.PI * 2);
			ctx.closePath();
			ctx.fill();
			break;
		case 'triangle':
			ctx.beginPath();
			edgeLength = 3 * radius / Math.sqrt(3);
			height = edgeLength * Math.sqrt(3) / 2;
			ctx.moveTo(x - edgeLength / 2, y + height / 3);
			ctx.lineTo(x + edgeLength / 2, y + height / 3);
			ctx.lineTo(x, y - 2 * height / 3);
			ctx.closePath();
			ctx.fill();
			break;
		case 'rect':
			size = 1 / Math.SQRT2 * radius;
			ctx.beginPath();
			ctx.fillRect(x - size, y - size, 2 * size, 2 * size);
			ctx.strokeRect(x - size, y - size, 2 * size, 2 * size);
			break;
		case 'rectRounded':
			var offset = radius / Math.SQRT2;
			var leftX = x - offset;
			var topY = y - offset;
			var sideSize = Math.SQRT2 * radius;
			ctx.beginPath();
			this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius / 2);
			ctx.closePath();
			ctx.fill();
			break;
		case 'rectRot':
			size = 1 / Math.SQRT2 * radius;
			ctx.beginPath();
			ctx.moveTo(x - size, y);
			ctx.lineTo(x, y + size);
			ctx.lineTo(x + size, y);
			ctx.lineTo(x, y - size);
			ctx.closePath();
			ctx.fill();
			break;
		case 'cross':
			ctx.beginPath();
			ctx.moveTo(x, y + radius);
			ctx.lineTo(x, y - radius);
			ctx.moveTo(x - radius, y);
			ctx.lineTo(x + radius, y);
			ctx.closePath();
			break;
		case 'crossRot':
			ctx.beginPath();
			xOffset = Math.cos(Math.PI / 4) * radius;
			yOffset = Math.sin(Math.PI / 4) * radius;
			ctx.moveTo(x - xOffset, y - yOffset);
			ctx.lineTo(x + xOffset, y + yOffset);
			ctx.moveTo(x - xOffset, y + yOffset);
			ctx.lineTo(x + xOffset, y - yOffset);
			ctx.closePath();
			break;
		case 'star':
			ctx.beginPath();
			ctx.moveTo(x, y + radius);
			ctx.lineTo(x, y - radius);
			ctx.moveTo(x - radius, y);
			ctx.lineTo(x + radius, y);
			xOffset = Math.cos(Math.PI / 4) * radius;
			yOffset = Math.sin(Math.PI / 4) * radius;
			ctx.moveTo(x - xOffset, y - yOffset);
			ctx.lineTo(x + xOffset, y + yOffset);
			ctx.moveTo(x - xOffset, y + yOffset);
			ctx.lineTo(x + xOffset, y - yOffset);
			ctx.closePath();
			break;
		case 'line':
			ctx.beginPath();
			ctx.moveTo(x - radius, y);
			ctx.lineTo(x + radius, y);
			ctx.closePath();
			break;
		case 'dash':
			ctx.beginPath();
			ctx.moveTo(x, y);
			ctx.lineTo(x + radius, y);
			ctx.closePath();
			break;
		}

		ctx.stroke();
	},

	clipArea: function(ctx, area) {
		ctx.save();
		ctx.beginPath();
		ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
		ctx.clip();
	},

	unclipArea: function(ctx) {
		ctx.restore();
	},

	lineTo: function(ctx, previous, target, flip) {
		if (target.steppedLine) {
			if ((target.steppedLine === 'after' && !flip) || (target.steppedLine !== 'after' && flip)) {
				ctx.lineTo(previous.x, target.y);
			} else {
				ctx.lineTo(target.x, previous.y);
			}
			ctx.lineTo(target.x, target.y);
			return;
		}

		if (!target.tension) {
			ctx.lineTo(target.x, target.y);
			return;
		}

		ctx.bezierCurveTo(
			flip ? previous.controlPointPreviousX : previous.controlPointNextX,
			flip ? previous.controlPointPreviousY : previous.controlPointNextY,
			flip ? target.controlPointNextX : target.controlPointPreviousX,
			flip ? target.controlPointNextY : target.controlPointPreviousY,
			target.x,
			target.y);
	}
};

// DEPRECATIONS

/**
 * Provided for backward compatibility, use Chart.helpers.canvas.clear instead.
 * @namespace Chart.helpers.clear
 * @deprecated since version 2.7.0
 * @todo remove at version 3
 * @private
 */
helpers.clear = exports.clear;

/**
 * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead.
 * @namespace Chart.helpers.drawRoundedRectangle
 * @deprecated since version 2.7.0
 * @todo remove at version 3
 * @private
 */
helpers.drawRoundedRectangle = function(ctx) {
	ctx.beginPath();
	exports.roundedRect.apply(exports, arguments);
	ctx.closePath();
};

},{"42":42}],42:[function(require,module,exports){
'use strict';

/**
 * @namespace Chart.helpers
 */
var helpers = {
	/**
	 * An empty function that can be used, for example, for optional callback.
	 */
	noop: function() {},

	/**
	 * Returns a unique id, sequentially generated from a global variable.
	 * @returns {Number}
	 * @function
	 */
	uid: (function() {
		var id = 0;
		return function() {
			return id++;
		};
	}()),

	/**
	 * Returns true if `value` is neither null nor undefined, else returns false.
	 * @param {*} value - The value to test.
	 * @returns {Boolean}
	 * @since 2.7.0
	 */
	isNullOrUndef: function(value) {
		return value === null || typeof value === 'undefined';
	},

	/**
	 * Returns true if `value` is an array, else returns false.
	 * @param {*} value - The value to test.
	 * @returns {Boolean}
	 * @function
	 */
	isArray: Array.isArray ? Array.isArray : function(value) {
		return Object.prototype.toString.call(value) === '[object Array]';
	},

	/**
	 * Returns true if `value` is an object (excluding null), else returns false.
	 * @param {*} value - The value to test.
	 * @returns {Boolean}
	 * @since 2.7.0
	 */
	isObject: function(value) {
		return value !== null && Object.prototype.toString.call(value) === '[object Object]';
	},

	/**
	 * Returns `value` if defined, else returns `defaultValue`.
	 * @param {*} value - The value to return if defined.
	 * @param {*} defaultValue - The value to return if `value` is undefined.
	 * @returns {*}
	 */
	valueOrDefault: function(value, defaultValue) {
		return typeof value === 'undefined' ? defaultValue : value;
	},

	/**
	 * Returns value at the given `index` in array if defined, else returns `defaultValue`.
	 * @param {Array} value - The array to lookup for value at `index`.
	 * @param {Number} index - The index in `value` to lookup for value.
	 * @param {*} defaultValue - The value to return if `value[index]` is undefined.
	 * @returns {*}
	 */
	valueAtIndexOrDefault: function(value, index, defaultValue) {
		return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue);
	},

	/**
	 * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the
	 * value returned by `fn`. If `fn` is not a function, this method returns undefined.
	 * @param {Function} fn - The function to call.
	 * @param {Array|undefined|null} args - The arguments with which `fn` should be called.
	 * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.
	 * @returns {*}
	 */
	callback: function(fn, args, thisArg) {
		if (fn && typeof fn.call === 'function') {
			return fn.apply(thisArg, args);
		}
	},

	/**
	 * Note(SB) for performance sake, this method should only be used when loopable type
	 * is unknown or in none intensive code (not called often and small loopable). Else
	 * it's preferable to use a regular for() loop and save extra function calls.
	 * @param {Object|Array} loopable - The object or array to be iterated.
	 * @param {Function} fn - The function to call for each item.
	 * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.
	 * @param {Boolean} [reverse] - If true, iterates backward on the loopable.
	 */
	each: function(loopable, fn, thisArg, reverse) {
		var i, len, keys;
		if (helpers.isArray(loopable)) {
			len = loopable.length;
			if (reverse) {
				for (i = len - 1; i >= 0; i--) {
					fn.call(thisArg, loopable[i], i);
				}
			} else {
				for (i = 0; i < len; i++) {
					fn.call(thisArg, loopable[i], i);
				}
			}
		} else if (helpers.isObject(loopable)) {
			keys = Object.keys(loopable);
			len = keys.length;
			for (i = 0; i < len; i++) {
				fn.call(thisArg, loopable[keys[i]], keys[i]);
			}
		}
	},

	/**
	 * Returns true if the `a0` and `a1` arrays have the same content, else returns false.
	 * @see http://stackoverflow.com/a/14853974
	 * @param {Array} a0 - The array to compare
	 * @param {Array} a1 - The array to compare
	 * @returns {Boolean}
	 */
	arrayEquals: function(a0, a1) {
		var i, ilen, v0, v1;

		if (!a0 || !a1 || a0.length !== a1.length) {
			return false;
		}

		for (i = 0, ilen = a0.length; i < ilen; ++i) {
			v0 = a0[i];
			v1 = a1[i];

			if (v0 instanceof Array && v1 instanceof Array) {
				if (!helpers.arrayEquals(v0, v1)) {
					return false;
				}
			} else if (v0 !== v1) {
				// NOTE: two different object instances will never be equal: {x:20} != {x:20}
				return false;
			}
		}

		return true;
	},

	/**
	 * Returns a deep copy of `source` without keeping references on objects and arrays.
	 * @param {*} source - The value to clone.
	 * @returns {*}
	 */
	clone: function(source) {
		if (helpers.isArray(source)) {
			return source.map(helpers.clone);
		}

		if (helpers.isObject(source)) {
			var target = {};
			var keys = Object.keys(source);
			var klen = keys.length;
			var k = 0;

			for (; k < klen; ++k) {
				target[keys[k]] = helpers.clone(source[keys[k]]);
			}

			return target;
		}

		return source;
	},

	/**
	 * The default merger when Chart.helpers.merge is called without merger option.
	 * Note(SB): this method is also used by configMerge and scaleMerge as fallback.
	 * @private
	 */
	_merger: function(key, target, source, options) {
		var tval = target[key];
		var sval = source[key];

		if (helpers.isObject(tval) && helpers.isObject(sval)) {
			helpers.merge(tval, sval, options);
		} else {
			target[key] = helpers.clone(sval);
		}
	},

	/**
	 * Merges source[key] in target[key] only if target[key] is undefined.
	 * @private
	 */
	_mergerIf: function(key, target, source) {
		var tval = target[key];
		var sval = source[key];

		if (helpers.isObject(tval) && helpers.isObject(sval)) {
			helpers.mergeIf(tval, sval);
		} else if (!target.hasOwnProperty(key)) {
			target[key] = helpers.clone(sval);
		}
	},

	/**
	 * Recursively deep copies `source` properties into `target` with the given `options`.
	 * IMPORTANT: `target` is not cloned and will be updated with `source` properties.
	 * @param {Object} target - The target object in which all sources are merged into.
	 * @param {Object|Array(Object)} source - Object(s) to merge into `target`.
	 * @param {Object} [options] - Merging options:
	 * @param {Function} [options.merger] - The merge method (key, target, source, options)
	 * @returns {Object} The `target` object.
	 */
	merge: function(target, source, options) {
		var sources = helpers.isArray(source) ? source : [source];
		var ilen = sources.length;
		var merge, i, keys, klen, k;

		if (!helpers.isObject(target)) {
			return target;
		}

		options = options || {};
		merge = options.merger || helpers._merger;

		for (i = 0; i < ilen; ++i) {
			source = sources[i];
			if (!helpers.isObject(source)) {
				continue;
			}

			keys = Object.keys(source);
			for (k = 0, klen = keys.length; k < klen; ++k) {
				merge(keys[k], target, source, options);
			}
		}

		return target;
	},

	/**
	 * Recursively deep copies `source` properties into `target` *only* if not defined in target.
	 * IMPORTANT: `target` is not cloned and will be updated with `source` properties.
	 * @param {Object} target - The target object in which all sources are merged into.
	 * @param {Object|Array(Object)} source - Object(s) to merge into `target`.
	 * @returns {Object} The `target` object.
	 */
	mergeIf: function(target, source) {
		return helpers.merge(target, source, {merger: helpers._mergerIf});
	}
};

module.exports = helpers;

// DEPRECATIONS

/**
 * Provided for backward compatibility, use Chart.helpers.callback instead.
 * @function Chart.helpers.callCallback
 * @deprecated since version 2.6.0
 * @todo remove at version 3
 * @private
 */
helpers.callCallback = helpers.callback;

/**
 * Provided for backward compatibility, use Array.prototype.indexOf instead.
 * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+
 * @function Chart.helpers.indexOf
 * @deprecated since version 2.7.0
 * @todo remove at version 3
 * @private
 */
helpers.indexOf = function(array, item, fromIndex) {
	return Array.prototype.indexOf.call(array, item, fromIndex);
};

/**
 * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead.
 * @function Chart.helpers.getValueOrDefault
 * @deprecated since version 2.7.0
 * @todo remove at version 3
 * @private
 */
helpers.getValueOrDefault = helpers.valueOrDefault;

/**
 * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead.
 * @function Chart.helpers.getValueAtIndexOrDefault
 * @deprecated since version 2.7.0
 * @todo remove at version 3
 * @private
 */
helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault;

},{}],43:[function(require,module,exports){
'use strict';

var helpers = require(42);

/**
 * Easing functions adapted from Robert Penner's easing equations.
 * @namespace Chart.helpers.easingEffects
 * @see http://www.robertpenner.com/easing/
 */
var effects = {
	linear: function(t) {
		return t;
	},

	easeInQuad: function(t) {
		return t * t;
	},

	easeOutQuad: function(t) {
		return -t * (t - 2);
	},

	easeInOutQuad: function(t) {
		if ((t /= 0.5) < 1) {
			return 0.5 * t * t;
		}
		return -0.5 * ((--t) * (t - 2) - 1);
	},

	easeInCubic: function(t) {
		return t * t * t;
	},

	easeOutCubic: function(t) {
		return (t = t - 1) * t * t + 1;
	},

	easeInOutCubic: function(t) {
		if ((t /= 0.5) < 1) {
			return 0.5 * t * t * t;
		}
		return 0.5 * ((t -= 2) * t * t + 2);
	},

	easeInQuart: function(t) {
		return t * t * t * t;
	},

	easeOutQuart: function(t) {
		return -((t = t - 1) * t * t * t - 1);
	},

	easeInOutQuart: function(t) {
		if ((t /= 0.5) < 1) {
			return 0.5 * t * t * t * t;
		}
		return -0.5 * ((t -= 2) * t * t * t - 2);
	},

	easeInQuint: function(t) {
		return t * t * t * t * t;
	},

	easeOutQuint: function(t) {
		return (t = t - 1) * t * t * t * t + 1;
	},

	easeInOutQuint: function(t) {
		if ((t /= 0.5) < 1) {
			return 0.5 * t * t * t * t * t;
		}
		return 0.5 * ((t -= 2) * t * t * t * t + 2);
	},

	easeInSine: function(t) {
		return -Math.cos(t * (Math.PI / 2)) + 1;
	},

	easeOutSine: function(t) {
		return Math.sin(t * (Math.PI / 2));
	},

	easeInOutSine: function(t) {
		return -0.5 * (Math.cos(Math.PI * t) - 1);
	},

	easeInExpo: function(t) {
		return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));
	},

	easeOutExpo: function(t) {
		return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1;
	},

	easeInOutExpo: function(t) {
		if (t === 0) {
			return 0;
		}
		if (t === 1) {
			return 1;
		}
		if ((t /= 0.5) < 1) {
			return 0.5 * Math.pow(2, 10 * (t - 1));
		}
		return 0.5 * (-Math.pow(2, -10 * --t) + 2);
	},

	easeInCirc: function(t) {
		if (t >= 1) {
			return t;
		}
		return -(Math.sqrt(1 - t * t) - 1);
	},

	easeOutCirc: function(t) {
		return Math.sqrt(1 - (t = t - 1) * t);
	},

	easeInOutCirc: function(t) {
		if ((t /= 0.5) < 1) {
			return -0.5 * (Math.sqrt(1 - t * t) - 1);
		}
		return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);
	},

	easeInElastic: function(t) {
		var s = 1.70158;
		var p = 0;
		var a = 1;
		if (t === 0) {
			return 0;
		}
		if (t === 1) {
			return 1;
		}
		if (!p) {
			p = 0.3;
		}
		if (a < 1) {
			a = 1;
			s = p / 4;
		} else {
			s = p / (2 * Math.PI) * Math.asin(1 / a);
		}
		return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
	},

	easeOutElastic: function(t) {
		var s = 1.70158;
		var p = 0;
		var a = 1;
		if (t === 0) {
			return 0;
		}
		if (t === 1) {
			return 1;
		}
		if (!p) {
			p = 0.3;
		}
		if (a < 1) {
			a = 1;
			s = p / 4;
		} else {
			s = p / (2 * Math.PI) * Math.asin(1 / a);
		}
		return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1;
	},

	easeInOutElastic: function(t) {
		var s = 1.70158;
		var p = 0;
		var a = 1;
		if (t === 0) {
			return 0;
		}
		if ((t /= 0.5) === 2) {
			return 1;
		}
		if (!p) {
			p = 0.45;
		}
		if (a < 1) {
			a = 1;
			s = p / 4;
		} else {
			s = p / (2 * Math.PI) * Math.asin(1 / a);
		}
		if (t < 1) {
			return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
		}
		return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1;
	},
	easeInBack: function(t) {
		var s = 1.70158;
		return t * t * ((s + 1) * t - s);
	},

	easeOutBack: function(t) {
		var s = 1.70158;
		return (t = t - 1) * t * ((s + 1) * t + s) + 1;
	},

	easeInOutBack: function(t) {
		var s = 1.70158;
		if ((t /= 0.5) < 1) {
			return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s));
		}
		return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
	},

	easeInBounce: function(t) {
		return 1 - effects.easeOutBounce(1 - t);
	},

	easeOutBounce: function(t) {
		if (t < (1 / 2.75)) {
			return 7.5625 * t * t;
		}
		if (t < (2 / 2.75)) {
			return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75;
		}
		if (t < (2.5 / 2.75)) {
			return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375;
		}
		return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375;
	},

	easeInOutBounce: function(t) {
		if (t < 0.5) {
			return effects.easeInBounce(t * 2) * 0.5;
		}
		return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5;
	}
};

module.exports = {
	effects: effects
};

// DEPRECATIONS

/**
 * Provided for backward compatibility, use Chart.helpers.easing.effects instead.
 * @function Chart.helpers.easingEffects
 * @deprecated since version 2.7.0
 * @todo remove at version 3
 * @private
 */
helpers.easingEffects = effects;

},{"42":42}],44:[function(require,module,exports){
'use strict';

var helpers = require(42);

/**
 * @alias Chart.helpers.options
 * @namespace
 */
module.exports = {
	/**
	 * Converts the given line height `value` in pixels for a specific font `size`.
	 * @param {Number|String} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
	 * @param {Number} size - The font size (in pixels) used to resolve relative `value`.
	 * @returns {Number} The effective line height in pixels (size * 1.2 if value is invalid).
	 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
	 * @since 2.7.0
	 */
	toLineHeight: function(value, size) {
		var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);
		if (!matches || matches[1] === 'normal') {
			return size * 1.2;
		}

		value = +matches[2];

		switch (matches[3]) {
		case 'px':
			return value;
		case '%':
			value /= 100;
			break;
		default:
			break;
		}

		return size * value;
	},

	/**
	 * Converts the given value into a padding object with pre-computed width/height.
	 * @param {Number|Object} value - If a number, set the value to all TRBL component,
	 *  else, if and object, use defined properties and sets undefined ones to 0.
	 * @returns {Object} The padding values (top, right, bottom, left, width, height)
	 * @since 2.7.0
	 */
	toPadding: function(value) {
		var t, r, b, l;

		if (helpers.isObject(value)) {
			t = +value.top || 0;
			r = +value.right || 0;
			b = +value.bottom || 0;
			l = +value.left || 0;
		} else {
			t = r = b = l = +value || 0;
		}

		return {
			top: t,
			right: r,
			bottom: b,
			left: l,
			height: t + b,
			width: l + r
		};
	},

	/**
	 * Evaluates the given `inputs` sequentially and returns the first defined value.
	 * @param {Array[]} inputs - An array of values, falling back to the last value.
	 * @param {Object} [context] - If defined and the current value is a function, the value
	 * is called with `context` as first argument and the result becomes the new input.
	 * @param {Number} [index] - If defined and the current value is an array, the value
	 * at `index` become the new input.
	 * @since 2.7.0
	 */
	resolve: function(inputs, context, index) {
		var i, ilen, value;

		for (i = 0, ilen = inputs.length; i < ilen; ++i) {
			value = inputs[i];
			if (value === undefined) {
				continue;
			}
			if (context !== undefined && typeof value === 'function') {
				value = value(context);
			}
			if (index !== undefined && helpers.isArray(value)) {
				value = value[index];
			}
			if (value !== undefined) {
				return value;
			}
		}
	}
};

},{"42":42}],45:[function(require,module,exports){
'use strict';

module.exports = require(42);
module.exports.easing = require(43);
module.exports.canvas = require(41);
module.exports.options = require(44);

},{"41":41,"42":42,"43":43,"44":44}],46:[function(require,module,exports){
/**
 * Platform fallback implementation (minimal).
 * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939
 */

module.exports = {
	acquireContext: function(item) {
		if (item && item.canvas) {
			// Support for any object associated to a canvas (including a context2d)
			item = item.canvas;
		}

		return item && item.getContext('2d') || null;
	}
};

},{}],47:[function(require,module,exports){
/**
 * Chart.Platform implementation for targeting a web browser
 */

'use strict';

var helpers = require(45);

var EXPANDO_KEY = '$chartjs';
var CSS_PREFIX = 'chartjs-';
var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor';
var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation';
var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart'];

/**
 * DOM event types -> Chart.js event types.
 * Note: only events with different types are mapped.
 * @see https://developer.mozilla.org/en-US/docs/Web/Events
 */
var EVENT_TYPES = {
	touchstart: 'mousedown',
	touchmove: 'mousemove',
	touchend: 'mouseup',
	pointerenter: 'mouseenter',
	pointerdown: 'mousedown',
	pointermove: 'mousemove',
	pointerup: 'mouseup',
	pointerleave: 'mouseout',
	pointerout: 'mouseout'
};

/**
 * The "used" size is the final value of a dimension property after all calculations have
 * been performed. This method uses the computed style of `element` but returns undefined
 * if the computed style is not expressed in pixels. That can happen in some cases where
 * `element` has a size relative to its parent and this last one is not yet displayed,
 * for example because of `display: none` on a parent node.
 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
 * @returns {Number} Size in pixels or undefined if unknown.
 */
function readUsedSize(element, property) {
	var value = helpers.getStyle(element, property);
	var matches = value && value.match(/^(\d+)(\.\d+)?px$/);
	return matches ? Number(matches[1]) : undefined;
}

/**
 * Initializes the canvas style and render size without modifying the canvas display size,
 * since responsiveness is handled by the controller.resize() method. The config is used
 * to determine the aspect ratio to apply in case no explicit height has been specified.
 */
function initCanvas(canvas, config) {
	var style = canvas.style;

	// NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it
	// returns null or '' if no explicit value has been set to the canvas attribute.
	var renderHeight = canvas.getAttribute('height');
	var renderWidth = canvas.getAttribute('width');

	// Chart.js modifies some canvas values that we want to restore on destroy
	canvas[EXPANDO_KEY] = {
		initial: {
			height: renderHeight,
			width: renderWidth,
			style: {
				display: style.display,
				height: style.height,
				width: style.width
			}
		}
	};

	// Force canvas to display as block to avoid extra space caused by inline
	// elements, which would interfere with the responsive resize process.
	// https://github.com/chartjs/Chart.js/issues/2538
	style.display = style.display || 'block';

	if (renderWidth === null || renderWidth === '') {
		var displayWidth = readUsedSize(canvas, 'width');
		if (displayWidth !== undefined) {
			canvas.width = displayWidth;
		}
	}

	if (renderHeight === null || renderHeight === '') {
		if (canvas.style.height === '') {
			// If no explicit render height and style height, let's apply the aspect ratio,
			// which one can be specified by the user but also by charts as default option
			// (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.
			canvas.height = canvas.width / (config.options.aspectRatio || 2);
		} else {
			var displayHeight = readUsedSize(canvas, 'height');
			if (displayWidth !== undefined) {
				canvas.height = displayHeight;
			}
		}
	}

	return canvas;
}

/**
 * Detects support for options object argument in addEventListener.
 * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
 * @private
 */
var supportsEventListenerOptions = (function() {
	var supports = false;
	try {
		var options = Object.defineProperty({}, 'passive', {
			get: function() {
				supports = true;
			}
		});
		window.addEventListener('e', null, options);
	} catch (e) {
		// continue regardless of error
	}
	return supports;
}());

// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events.
// https://github.com/chartjs/Chart.js/issues/4287
var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false;

function addEventListener(node, type, listener) {
	node.addEventListener(type, listener, eventListenerOptions);
}

function removeEventListener(node, type, listener) {
	node.removeEventListener(type, listener, eventListenerOptions);
}

function createEvent(type, chart, x, y, nativeEvent) {
	return {
		type: type,
		chart: chart,
		native: nativeEvent || null,
		x: x !== undefined ? x : null,
		y: y !== undefined ? y : null,
	};
}

function fromNativeEvent(event, chart) {
	var type = EVENT_TYPES[event.type] || event.type;
	var pos = helpers.getRelativePosition(event, chart);
	return createEvent(type, chart, pos.x, pos.y, event);
}

function throttled(fn, thisArg) {
	var ticking = false;
	var args = [];

	return function() {
		args = Array.prototype.slice.call(arguments);
		thisArg = thisArg || this;

		if (!ticking) {
			ticking = true;
			helpers.requestAnimFrame.call(window, function() {
				ticking = false;
				fn.apply(thisArg, args);
			});
		}
	};
}

// Implementation based on https://github.com/marcj/css-element-queries
function createResizer(handler) {
	var resizer = document.createElement('div');
	var cls = CSS_PREFIX + 'size-monitor';
	var maxSize = 1000000;
	var style =
		'position:absolute;' +
		'left:0;' +
		'top:0;' +
		'right:0;' +
		'bottom:0;' +
		'overflow:hidden;' +
		'pointer-events:none;' +
		'visibility:hidden;' +
		'z-index:-1;';

	resizer.style.cssText = style;
	resizer.className = cls;
	resizer.innerHTML =
		'<div class="' + cls + '-expand" style="' + style + '">' +
			'<div style="' +
				'position:absolute;' +
				'width:' + maxSize + 'px;' +
				'height:' + maxSize + 'px;' +
				'left:0;' +
				'top:0">' +
			'</div>' +
		'</div>' +
		'<div class="' + cls + '-shrink" style="' + style + '">' +
			'<div style="' +
				'position:absolute;' +
				'width:200%;' +
				'height:200%;' +
				'left:0; ' +
				'top:0">' +
			'</div>' +
		'</div>';

	var expand = resizer.childNodes[0];
	var shrink = resizer.childNodes[1];

	resizer._reset = function() {
		expand.scrollLeft = maxSize;
		expand.scrollTop = maxSize;
		shrink.scrollLeft = maxSize;
		shrink.scrollTop = maxSize;
	};
	var onScroll = function() {
		resizer._reset();
		handler();
	};

	addEventListener(expand, 'scroll', onScroll.bind(expand, 'expand'));
	addEventListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink'));

	return resizer;
}

// https://davidwalsh.name/detect-node-insertion
function watchForRender(node, handler) {
	var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
	var proxy = expando.renderProxy = function(e) {
		if (e.animationName === CSS_RENDER_ANIMATION) {
			handler();
		}
	};

	helpers.each(ANIMATION_START_EVENTS, function(type) {
		addEventListener(node, type, proxy);
	});

	node.classList.add(CSS_RENDER_MONITOR);
}

function unwatchForRender(node) {
	var expando = node[EXPANDO_KEY] || {};
	var proxy = expando.renderProxy;

	if (proxy) {
		helpers.each(ANIMATION_START_EVENTS, function(type) {
			removeEventListener(node, type, proxy);
		});

		delete expando.renderProxy;
	}

	node.classList.remove(CSS_RENDER_MONITOR);
}

function addResizeListener(node, listener, chart) {
	var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});

	// Let's keep track of this added resizer and thus avoid DOM query when removing it.
	var resizer = expando.resizer = createResizer(throttled(function() {
		if (expando.resizer) {
			return listener(createEvent('resize', chart));
		}
	}));

	// The resizer needs to be attached to the node parent, so we first need to be
	// sure that `node` is attached to the DOM before injecting the resizer element.
	watchForRender(node, function() {
		if (expando.resizer) {
			var container = node.parentNode;
			if (container && container !== resizer.parentNode) {
				container.insertBefore(resizer, container.firstChild);
			}

			// The container size might have changed, let's reset the resizer state.
			resizer._reset();
		}
	});
}

function removeResizeListener(node) {
	var expando = node[EXPANDO_KEY] || {};
	var resizer = expando.resizer;

	delete expando.resizer;
	unwatchForRender(node);

	if (resizer && resizer.parentNode) {
		resizer.parentNode.removeChild(resizer);
	}
}

function injectCSS(platform, css) {
	// http://stackoverflow.com/q/3922139
	var style = platform._style || document.createElement('style');
	if (!platform._style) {
		platform._style = style;
		css = '/* Chart.js */\n' + css;
		style.setAttribute('type', 'text/css');
		document.getElementsByTagName('head')[0].appendChild(style);
	}

	style.appendChild(document.createTextNode(css));
}

module.exports = {
	/**
	 * This property holds whether this platform is enabled for the current environment.
	 * Currently used by platform.js to select the proper implementation.
	 * @private
	 */
	_enabled: typeof window !== 'undefined' && typeof document !== 'undefined',

	initialize: function() {
		var keyframes = 'from{opacity:0.99}to{opacity:1}';

		injectCSS(this,
			// DOM rendering detection
			// https://davidwalsh.name/detect-node-insertion
			'@-webkit-keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' +
			'@keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' +
			'.' + CSS_RENDER_MONITOR + '{' +
				'-webkit-animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' +
				'animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' +
			'}'
		);
	},

	acquireContext: function(item, config) {
		if (typeof item === 'string') {
			item = document.getElementById(item);
		} else if (item.length) {
			// Support for array based queries (such as jQuery)
			item = item[0];
		}

		if (item && item.canvas) {
			// Support for any object associated to a canvas (including a context2d)
			item = item.canvas;
		}

		// To prevent canvas fingerprinting, some add-ons undefine the getContext
		// method, for example: https://github.com/kkapsner/CanvasBlocker
		// https://github.com/chartjs/Chart.js/issues/2807
		var context = item && item.getContext && item.getContext('2d');

		// `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is
		// inside an iframe or when running in a protected environment. We could guess the
		// types from their toString() value but let's keep things flexible and assume it's
		// a sufficient condition if the item has a context2D which has item as `canvas`.
		// https://github.com/chartjs/Chart.js/issues/3887
		// https://github.com/chartjs/Chart.js/issues/4102
		// https://github.com/chartjs/Chart.js/issues/4152
		if (context && context.canvas === item) {
			initCanvas(item, config);
			return context;
		}

		return null;
	},

	releaseContext: function(context) {
		var canvas = context.canvas;
		if (!canvas[EXPANDO_KEY]) {
			return;
		}

		var initial = canvas[EXPANDO_KEY].initial;
		['height', 'width'].forEach(function(prop) {
			var value = initial[prop];
			if (helpers.isNullOrUndef(value)) {
				canvas.removeAttribute(prop);
			} else {
				canvas.setAttribute(prop, value);
			}
		});

		helpers.each(initial.style || {}, function(value, key) {
			canvas.style[key] = value;
		});

		// The canvas render size might have been changed (and thus the state stack discarded),
		// we can't use save() and restore() to restore the initial state. So make sure that at
		// least the canvas context is reset to the default state by setting the canvas width.
		// https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html
		canvas.width = canvas.width;

		delete canvas[EXPANDO_KEY];
	},

	addEventListener: function(chart, type, listener) {
		var canvas = chart.canvas;
		if (type === 'resize') {
			// Note: the resize event is not supported on all browsers.
			addResizeListener(canvas, listener, chart);
			return;
		}

		var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {});
		var proxies = expando.proxies || (expando.proxies = {});
		var proxy = proxies[chart.id + '_' + type] = function(event) {
			listener(fromNativeEvent(event, chart));
		};

		addEventListener(canvas, type, proxy);
	},

	removeEventListener: function(chart, type, listener) {
		var canvas = chart.canvas;
		if (type === 'resize') {
			// Note: the resize event is not supported on all browsers.
			removeResizeListener(canvas, listener);
			return;
		}

		var expando = listener[EXPANDO_KEY] || {};
		var proxies = expando.proxies || {};
		var proxy = proxies[chart.id + '_' + type];
		if (!proxy) {
			return;
		}

		removeEventListener(canvas, type, proxy);
	}
};

// DEPRECATIONS

/**
 * Provided for backward compatibility, use EventTarget.addEventListener instead.
 * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
 * @function Chart.helpers.addEvent
 * @deprecated since version 2.7.0
 * @todo remove at version 3
 * @private
 */
helpers.addEvent = addEventListener;

/**
 * Provided for backward compatibility, use EventTarget.removeEventListener instead.
 * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
 * @function Chart.helpers.removeEvent
 * @deprecated since version 2.7.0
 * @todo remove at version 3
 * @private
 */
helpers.removeEvent = removeEventListener;

},{"45":45}],48:[function(require,module,exports){
'use strict';

var helpers = require(45);
var basic = require(46);
var dom = require(47);

// @TODO Make possible to select another platform at build time.
var implementation = dom._enabled ? dom : basic;

/**
 * @namespace Chart.platform
 * @see https://chartjs.gitbooks.io/proposals/content/Platform.html
 * @since 2.4.0
 */
module.exports = helpers.extend({
	/**
	 * @since 2.7.0
	 */
	initialize: function() {},

	/**
	 * Called at chart construction time, returns a context2d instance implementing
	 * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.
	 * @param {*} item - The native item from which to acquire context (platform specific)
	 * @param {Object} options - The chart options
	 * @returns {CanvasRenderingContext2D} context2d instance
	 */
	acquireContext: function() {},

	/**
	 * Called at chart destruction time, releases any resources associated to the context
	 * previously returned by the acquireContext() method.
	 * @param {CanvasRenderingContext2D} context - The context2d instance
	 * @returns {Boolean} true if the method succeeded, else false
	 */
	releaseContext: function() {},

	/**
	 * Registers the specified listener on the given chart.
	 * @param {Chart} chart - Chart from which to listen for event
	 * @param {String} type - The ({@link IEvent}) type to listen for
	 * @param {Function} listener - Receives a notification (an object that implements
	 * the {@link IEvent} interface) when an event of the specified type occurs.
	 */
	addEventListener: function() {},

	/**
	 * Removes the specified listener previously registered with addEventListener.
	 * @param {Chart} chart -Chart from which to remove the listener
	 * @param {String} type - The ({@link IEvent}) type to remove
	 * @param {Function} listener - The listener function to remove from the event target.
	 */
	removeEventListener: function() {}

}, implementation);

/**
 * @interface IPlatform
 * Allows abstracting platform dependencies away from the chart
 * @borrows Chart.platform.acquireContext as acquireContext
 * @borrows Chart.platform.releaseContext as releaseContext
 * @borrows Chart.platform.addEventListener as addEventListener
 * @borrows Chart.platform.removeEventListener as removeEventListener
 */

/**
 * @interface IEvent
 * @prop {String} type - The event type name, possible values are:
 * 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout',
 * 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize'
 * @prop {*} native - The original native event (null for emulated events, e.g. 'resize')
 * @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events)
 * @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events)
 */

},{"45":45,"46":46,"47":47}],49:[function(require,module,exports){
/**
 * Plugin based on discussion from the following Chart.js issues:
 * @see https://github.com/chartjs/Chart.js/issues/2380#issuecomment-279961569
 * @see https://github.com/chartjs/Chart.js/issues/2440#issuecomment-256461897
 */

'use strict';

var defaults = require(25);
var elements = require(40);
var helpers = require(45);

defaults._set('global', {
	plugins: {
		filler: {
			propagate: true
		}
	}
});

module.exports = function() {

	var mappers = {
		dataset: function(source) {
			var index = source.fill;
			var chart = source.chart;
			var meta = chart.getDatasetMeta(index);
			var visible = meta && chart.isDatasetVisible(index);
			var points = (visible && meta.dataset._children) || [];
			var length = points.length || 0;

			return !length ? null : function(point, i) {
				return (i < length && points[i]._view) || null;
			};
		},

		boundary: function(source) {
			var boundary = source.boundary;
			var x = boundary ? boundary.x : null;
			var y = boundary ? boundary.y : null;

			return function(point) {
				return {
					x: x === null ? point.x : x,
					y: y === null ? point.y : y,
				};
			};
		}
	};

	// @todo if (fill[0] === '#')
	function decodeFill(el, index, count) {
		var model = el._model || {};
		var fill = model.fill;
		var target;

		if (fill === undefined) {
			fill = !!model.backgroundColor;
		}

		if (fill === false || fill === null) {
			return false;
		}

		if (fill === true) {
			return 'origin';
		}

		target = parseFloat(fill, 10);
		if (isFinite(target) && Math.floor(target) === target) {
			if (fill[0] === '-' || fill[0] === '+') {
				target = index + target;
			}

			if (target === index || target < 0 || target >= count) {
				return false;
			}

			return target;
		}

		switch (fill) {
		// compatibility
		case 'bottom':
			return 'start';
		case 'top':
			return 'end';
		case 'zero':
			return 'origin';
		// supported boundaries
		case 'origin':
		case 'start':
		case 'end':
			return fill;
		// invalid fill values
		default:
			return false;
		}
	}

	function computeBoundary(source) {
		var model = source.el._model || {};
		var scale = source.el._scale || {};
		var fill = source.fill;
		var target = null;
		var horizontal;

		if (isFinite(fill)) {
			return null;
		}

		// Backward compatibility: until v3, we still need to support boundary values set on
		// the model (scaleTop, scaleBottom and scaleZero) because some external plugins and
		// controllers might still use it (e.g. the Smith chart).

		if (fill === 'start') {
			target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom;
		} else if (fill === 'end') {
			target = model.scaleTop === undefined ? scale.top : model.scaleTop;
		} else if (model.scaleZero !== undefined) {
			target = model.scaleZero;
		} else if (scale.getBasePosition) {
			target = scale.getBasePosition();
		} else if (scale.getBasePixel) {
			target = scale.getBasePixel();
		}

		if (target !== undefined && target !== null) {
			if (target.x !== undefined && target.y !== undefined) {
				return target;
			}

			if (typeof target === 'number' && isFinite(target)) {
				horizontal = scale.isHorizontal();
				return {
					x: horizontal ? target : null,
					y: horizontal ? null : target
				};
			}
		}

		return null;
	}

	function resolveTarget(sources, index, propagate) {
		var source = sources[index];
		var fill = source.fill;
		var visited = [index];
		var target;

		if (!propagate) {
			return fill;
		}

		while (fill !== false && visited.indexOf(fill) === -1) {
			if (!isFinite(fill)) {
				return fill;
			}

			target = sources[fill];
			if (!target) {
				return false;
			}

			if (target.visible) {
				return fill;
			}

			visited.push(fill);
			fill = target.fill;
		}

		return false;
	}

	function createMapper(source) {
		var fill = source.fill;
		var type = 'dataset';

		if (fill === false) {
			return null;
		}

		if (!isFinite(fill)) {
			type = 'boundary';
		}

		return mappers[type](source);
	}

	function isDrawable(point) {
		return point && !point.skip;
	}

	function drawArea(ctx, curve0, curve1, len0, len1) {
		var i;

		if (!len0 || !len1) {
			return;
		}

		// building first area curve (normal)
		ctx.moveTo(curve0[0].x, curve0[0].y);
		for (i = 1; i < len0; ++i) {
			helpers.canvas.lineTo(ctx, curve0[i - 1], curve0[i]);
		}

		// joining the two area curves
		ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y);

		// building opposite area curve (reverse)
		for (i = len1 - 1; i > 0; --i) {
			helpers.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true);
		}
	}

	function doFill(ctx, points, mapper, view, color, loop) {
		var count = points.length;
		var span = view.spanGaps;
		var curve0 = [];
		var curve1 = [];
		var len0 = 0;
		var len1 = 0;
		var i, ilen, index, p0, p1, d0, d1;

		ctx.beginPath();

		for (i = 0, ilen = (count + !!loop); i < ilen; ++i) {
			index = i % count;
			p0 = points[index]._view;
			p1 = mapper(p0, index, view);
			d0 = isDrawable(p0);
			d1 = isDrawable(p1);

			if (d0 && d1) {
				len0 = curve0.push(p0);
				len1 = curve1.push(p1);
			} else if (len0 && len1) {
				if (!span) {
					drawArea(ctx, curve0, curve1, len0, len1);
					len0 = len1 = 0;
					curve0 = [];
					curve1 = [];
				} else {
					if (d0) {
						curve0.push(p0);
					}
					if (d1) {
						curve1.push(p1);
					}
				}
			}
		}

		drawArea(ctx, curve0, curve1, len0, len1);

		ctx.closePath();
		ctx.fillStyle = color;
		ctx.fill();
	}

	return {
		id: 'filler',

		afterDatasetsUpdate: function(chart, options) {
			var count = (chart.data.datasets || []).length;
			var propagate = options.propagate;
			var sources = [];
			var meta, i, el, source;

			for (i = 0; i < count; ++i) {
				meta = chart.getDatasetMeta(i);
				el = meta.dataset;
				source = null;

				if (el && el._model && el instanceof elements.Line) {
					source = {
						visible: chart.isDatasetVisible(i),
						fill: decodeFill(el, i, count),
						chart: chart,
						el: el
					};
				}

				meta.$filler = source;
				sources.push(source);
			}

			for (i = 0; i < count; ++i) {
				source = sources[i];
				if (!source) {
					continue;
				}

				source.fill = resolveTarget(sources, i, propagate);
				source.boundary = computeBoundary(source);
				source.mapper = createMapper(source);
			}
		},

		beforeDatasetDraw: function(chart, args) {
			var meta = args.meta.$filler;
			if (!meta) {
				return;
			}

			var ctx = chart.ctx;
			var el = meta.el;
			var view = el._view;
			var points = el._children || [];
			var mapper = meta.mapper;
			var color = view.backgroundColor || defaults.global.defaultColor;

			if (mapper && color && points.length) {
				helpers.canvas.clipArea(ctx, chart.chartArea);
				doFill(ctx, points, mapper, view, color, el._loop);
				helpers.canvas.unclipArea(ctx);
			}
		}
	};
};

},{"25":25,"40":40,"45":45}],50:[function(require,module,exports){
'use strict';

var defaults = require(25);
var Element = require(26);
var helpers = require(45);

defaults._set('global', {
	legend: {
		display: true,
		position: 'top',
		fullWidth: true,
		reverse: false,
		weight: 1000,

		// a callback that will handle
		onClick: function(e, legendItem) {
			var index = legendItem.datasetIndex;
			var ci = this.chart;
			var meta = ci.getDatasetMeta(index);

			// See controller.isDatasetVisible comment
			meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null;

			// We hid a dataset ... rerender the chart
			ci.update();
		},

		onHover: null,

		labels: {
			boxWidth: 40,
			padding: 10,
			// Generates labels shown in the legend
			// Valid properties to return:
			// text : text to display
			// fillStyle : fill of coloured box
			// strokeStyle: stroke of coloured box
			// hidden : if this legend item refers to a hidden item
			// lineCap : cap style for line
			// lineDash
			// lineDashOffset :
			// lineJoin :
			// lineWidth :
			generateLabels: function(chart) {
				var data = chart.data;
				return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) {
					return {
						text: dataset.label,
						fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]),
						hidden: !chart.isDatasetVisible(i),
						lineCap: dataset.borderCapStyle,
						lineDash: dataset.borderDash,
						lineDashOffset: dataset.borderDashOffset,
						lineJoin: dataset.borderJoinStyle,
						lineWidth: dataset.borderWidth,
						strokeStyle: dataset.borderColor,
						pointStyle: dataset.pointStyle,

						// Below is extra data used for toggling the datasets
						datasetIndex: i
					};
				}, this) : [];
			}
		}
	},

	legendCallback: function(chart) {
		var text = [];
		text.push('<ul class="' + chart.id + '-legend">');
		for (var i = 0; i < chart.data.datasets.length; i++) {
			text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>');
			if (chart.data.datasets[i].label) {
				text.push(chart.data.datasets[i].label);
			}
			text.push('</li>');
		}
		text.push('</ul>');
		return text.join('');
	}
});

module.exports = function(Chart) {

	var layout = Chart.layoutService;
	var noop = helpers.noop;

	/**
	 * Helper function to get the box width based on the usePointStyle option
	 * @param labelopts {Object} the label options on the legend
	 * @param fontSize {Number} the label font size
	 * @return {Number} width of the color box area
	 */
	function getBoxWidth(labelOpts, fontSize) {
		return labelOpts.usePointStyle ?
			fontSize * Math.SQRT2 :
			labelOpts.boxWidth;
	}

	Chart.Legend = Element.extend({

		initialize: function(config) {
			helpers.extend(this, config);

			// Contains hit boxes for each dataset (in dataset order)
			this.legendHitBoxes = [];

			// Are we in doughnut mode which has a different data type
			this.doughnutMode = false;
		},

		// These methods are ordered by lifecycle. Utilities then follow.
		// Any function defined here is inherited by all legend types.
		// Any function can be extended by the legend type

		beforeUpdate: noop,
		update: function(maxWidth, maxHeight, margins) {
			var me = this;

			// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
			me.beforeUpdate();

			// Absorb the master measurements
			me.maxWidth = maxWidth;
			me.maxHeight = maxHeight;
			me.margins = margins;

			// Dimensions
			me.beforeSetDimensions();
			me.setDimensions();
			me.afterSetDimensions();
			// Labels
			me.beforeBuildLabels();
			me.buildLabels();
			me.afterBuildLabels();

			// Fit
			me.beforeFit();
			me.fit();
			me.afterFit();
			//
			me.afterUpdate();

			return me.minSize;
		},
		afterUpdate: noop,

		//

		beforeSetDimensions: noop,
		setDimensions: function() {
			var me = this;
			// Set the unconstrained dimension before label rotation
			if (me.isHorizontal()) {
				// Reset position before calculating rotation
				me.width = me.maxWidth;
				me.left = 0;
				me.right = me.width;
			} else {
				me.height = me.maxHeight;

				// Reset position before calculating rotation
				me.top = 0;
				me.bottom = me.height;
			}

			// Reset padding
			me.paddingLeft = 0;
			me.paddingTop = 0;
			me.paddingRight = 0;
			me.paddingBottom = 0;

			// Reset minSize
			me.minSize = {
				width: 0,
				height: 0
			};
		},
		afterSetDimensions: noop,

		//

		beforeBuildLabels: noop,
		buildLabels: function() {
			var me = this;
			var labelOpts = me.options.labels || {};
			var legendItems = helpers.callback(labelOpts.generateLabels, [me.chart], me) || [];

			if (labelOpts.filter) {
				legendItems = legendItems.filter(function(item) {
					return labelOpts.filter(item, me.chart.data);
				});
			}

			if (me.options.reverse) {
				legendItems.reverse();
			}

			me.legendItems = legendItems;
		},
		afterBuildLabels: noop,

		//

		beforeFit: noop,
		fit: function() {
			var me = this;
			var opts = me.options;
			var labelOpts = opts.labels;
			var display = opts.display;

			var ctx = me.ctx;

			var globalDefault = defaults.global;
			var valueOrDefault = helpers.valueOrDefault;
			var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);
			var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);
			var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);
			var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);

			// Reset hit boxes
			var hitboxes = me.legendHitBoxes = [];

			var minSize = me.minSize;
			var isHorizontal = me.isHorizontal();

			if (isHorizontal) {
				minSize.width = me.maxWidth; // fill all the width
				minSize.height = display ? 10 : 0;
			} else {
				minSize.width = display ? 10 : 0;
				minSize.height = me.maxHeight; // fill all the height
			}

			// Increase sizes here
			if (display) {
				ctx.font = labelFont;

				if (isHorizontal) {
					// Labels

					// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
					var lineWidths = me.lineWidths = [0];
					var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0;

					ctx.textAlign = 'left';
					ctx.textBaseline = 'top';

					helpers.each(me.legendItems, function(legendItem, i) {
						var boxWidth = getBoxWidth(labelOpts, fontSize);
						var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;

						if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) {
							totalHeight += fontSize + (labelOpts.padding);
							lineWidths[lineWidths.length] = me.left;
						}

						// Store the hitbox width and height here. Final position will be updated in `draw`
						hitboxes[i] = {
							left: 0,
							top: 0,
							width: width,
							height: fontSize
						};

						lineWidths[lineWidths.length - 1] += width + labelOpts.padding;
					});

					minSize.height += totalHeight;

				} else {
					var vPadding = labelOpts.padding;
					var columnWidths = me.columnWidths = [];
					var totalWidth = labelOpts.padding;
					var currentColWidth = 0;
					var currentColHeight = 0;
					var itemHeight = fontSize + vPadding;

					helpers.each(me.legendItems, function(legendItem, i) {
						var boxWidth = getBoxWidth(labelOpts, fontSize);
						var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;

						// If too tall, go to new column
						if (currentColHeight + itemHeight > minSize.height) {
							totalWidth += currentColWidth + labelOpts.padding;
							columnWidths.push(currentColWidth); // previous column width

							currentColWidth = 0;
							currentColHeight = 0;
						}

						// Get max width
						currentColWidth = Math.max(currentColWidth, itemWidth);
						currentColHeight += itemHeight;

						// Store the hitbox width and height here. Final position will be updated in `draw`
						hitboxes[i] = {
							left: 0,
							top: 0,
							width: itemWidth,
							height: fontSize
						};
					});

					totalWidth += currentColWidth;
					columnWidths.push(currentColWidth);
					minSize.width += totalWidth;
				}
			}

			me.width = minSize.width;
			me.height = minSize.height;
		},
		afterFit: noop,

		// Shared Methods
		isHorizontal: function() {
			return this.options.position === 'top' || this.options.position === 'bottom';
		},

		// Actually draw the legend on the canvas
		draw: function() {
			var me = this;
			var opts = me.options;
			var labelOpts = opts.labels;
			var globalDefault = defaults.global;
			var lineDefault = globalDefault.elements.line;
			var legendWidth = me.width;
			var lineWidths = me.lineWidths;

			if (opts.display) {
				var ctx = me.ctx;
				var valueOrDefault = helpers.valueOrDefault;
				var fontColor = valueOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor);
				var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);
				var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);
				var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);
				var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
				var cursor;

				// Canvas setup
				ctx.textAlign = 'left';
				ctx.textBaseline = 'middle';
				ctx.lineWidth = 0.5;
				ctx.strokeStyle = fontColor; // for strikethrough effect
				ctx.fillStyle = fontColor; // render in correct colour
				ctx.font = labelFont;

				var boxWidth = getBoxWidth(labelOpts, fontSize);
				var hitboxes = me.legendHitBoxes;

				// current position
				var drawLegendBox = function(x, y, legendItem) {
					if (isNaN(boxWidth) || boxWidth <= 0) {
						return;
					}

					// Set the ctx for the box
					ctx.save();

					ctx.fillStyle = valueOrDefault(legendItem.fillStyle, globalDefault.defaultColor);
					ctx.lineCap = valueOrDefault(legendItem.lineCap, lineDefault.borderCapStyle);
					ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset);
					ctx.lineJoin = valueOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle);
					ctx.lineWidth = valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth);
					ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, globalDefault.defaultColor);
					var isLineWidthZero = (valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0);

					if (ctx.setLineDash) {
						// IE 9 and 10 do not support line dash
						ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash));
					}

					if (opts.labels && opts.labels.usePointStyle) {
						// Recalculate x and y for drawPoint() because its expecting
						// x and y to be center of figure (instead of top left)
						var radius = fontSize * Math.SQRT2 / 2;
						var offSet = radius / Math.SQRT2;
						var centerX = x + offSet;
						var centerY = y + offSet;

						// Draw pointStyle as legend symbol
						helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
					} else {
						// Draw box as legend symbol
						if (!isLineWidthZero) {
							ctx.strokeRect(x, y, boxWidth, fontSize);
						}
						ctx.fillRect(x, y, boxWidth, fontSize);
					}

					ctx.restore();
				};
				var fillText = function(x, y, legendItem, textWidth) {
					var halfFontSize = fontSize / 2;
					var xLeft = boxWidth + halfFontSize + x;
					var yMiddle = y + halfFontSize;

					ctx.fillText(legendItem.text, xLeft, yMiddle);

					if (legendItem.hidden) {
						// Strikethrough the text if hidden
						ctx.beginPath();
						ctx.lineWidth = 2;
						ctx.moveTo(xLeft, yMiddle);
						ctx.lineTo(xLeft + textWidth, yMiddle);
						ctx.stroke();
					}
				};

				// Horizontal
				var isHorizontal = me.isHorizontal();
				if (isHorizontal) {
					cursor = {
						x: me.left + ((legendWidth - lineWidths[0]) / 2),
						y: me.top + labelOpts.padding,
						line: 0
					};
				} else {
					cursor = {
						x: me.left + labelOpts.padding,
						y: me.top + labelOpts.padding,
						line: 0
					};
				}

				var itemHeight = fontSize + labelOpts.padding;
				helpers.each(me.legendItems, function(legendItem, i) {
					var textWidth = ctx.measureText(legendItem.text).width;
					var width = boxWidth + (fontSize / 2) + textWidth;
					var x = cursor.x;
					var y = cursor.y;

					if (isHorizontal) {
						if (x + width >= legendWidth) {
							y = cursor.y += itemHeight;
							cursor.line++;
							x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2);
						}
					} else if (y + itemHeight > me.bottom) {
						x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding;
						y = cursor.y = me.top + labelOpts.padding;
						cursor.line++;
					}

					drawLegendBox(x, y, legendItem);

					hitboxes[i].left = x;
					hitboxes[i].top = y;

					// Fill the actual label
					fillText(x, y, legendItem, textWidth);

					if (isHorizontal) {
						cursor.x += width + (labelOpts.padding);
					} else {
						cursor.y += itemHeight;
					}

				});
			}
		},

		/**
		 * Handle an event
		 * @private
		 * @param {IEvent} event - The event to handle
		 * @return {Boolean} true if a change occured
		 */
		handleEvent: function(e) {
			var me = this;
			var opts = me.options;
			var type = e.type === 'mouseup' ? 'click' : e.type;
			var changed = false;

			if (type === 'mousemove') {
				if (!opts.onHover) {
					return;
				}
			} else if (type === 'click') {
				if (!opts.onClick) {
					return;
				}
			} else {
				return;
			}

			// Chart event already has relative position in it
			var x = e.x;
			var y = e.y;

			if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
				// See if we are touching one of the dataset boxes
				var lh = me.legendHitBoxes;
				for (var i = 0; i < lh.length; ++i) {
					var hitBox = lh[i];

					if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
						// Touching an element
						if (type === 'click') {
							// use e.native for backwards compatibility
							opts.onClick.call(me, e.native, me.legendItems[i]);
							changed = true;
							break;
						} else if (type === 'mousemove') {
							// use e.native for backwards compatibility
							opts.onHover.call(me, e.native, me.legendItems[i]);
							changed = true;
							break;
						}
					}
				}
			}

			return changed;
		}
	});

	function createNewLegendAndAttach(chart, legendOpts) {
		var legend = new Chart.Legend({
			ctx: chart.ctx,
			options: legendOpts,
			chart: chart
		});

		layout.configure(chart, legend, legendOpts);
		layout.addBox(chart, legend);
		chart.legend = legend;
	}

	return {
		id: 'legend',

		beforeInit: function(chart) {
			var legendOpts = chart.options.legend;

			if (legendOpts) {
				createNewLegendAndAttach(chart, legendOpts);
			}
		},

		beforeUpdate: function(chart) {
			var legendOpts = chart.options.legend;
			var legend = chart.legend;

			if (legendOpts) {
				helpers.mergeIf(legendOpts, defaults.global.legend);

				if (legend) {
					layout.configure(chart, legend, legendOpts);
					legend.options = legendOpts;
				} else {
					createNewLegendAndAttach(chart, legendOpts);
				}
			} else if (legend) {
				layout.removeBox(chart, legend);
				delete chart.legend;
			}
		},

		afterEvent: function(chart, e) {
			var legend = chart.legend;
			if (legend) {
				legend.handleEvent(e);
			}
		}
	};
};

},{"25":25,"26":26,"45":45}],51:[function(require,module,exports){
'use strict';

var defaults = require(25);
var Element = require(26);
var helpers = require(45);

defaults._set('global', {
	title: {
		display: false,
		fontStyle: 'bold',
		fullWidth: true,
		lineHeight: 1.2,
		padding: 10,
		position: 'top',
		text: '',
		weight: 2000         // by default greater than legend (1000) to be above
	}
});

module.exports = function(Chart) {

	var layout = Chart.layoutService;
	var noop = helpers.noop;

	Chart.Title = Element.extend({
		initialize: function(config) {
			var me = this;
			helpers.extend(me, config);

			// Contains hit boxes for each dataset (in dataset order)
			me.legendHitBoxes = [];
		},

		// These methods are ordered by lifecycle. Utilities then follow.

		beforeUpdate: noop,
		update: function(maxWidth, maxHeight, margins) {
			var me = this;

			// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
			me.beforeUpdate();

			// Absorb the master measurements
			me.maxWidth = maxWidth;
			me.maxHeight = maxHeight;
			me.margins = margins;

			// Dimensions
			me.beforeSetDimensions();
			me.setDimensions();
			me.afterSetDimensions();
			// Labels
			me.beforeBuildLabels();
			me.buildLabels();
			me.afterBuildLabels();

			// Fit
			me.beforeFit();
			me.fit();
			me.afterFit();
			//
			me.afterUpdate();

			return me.minSize;

		},
		afterUpdate: noop,

		//

		beforeSetDimensions: noop,
		setDimensions: function() {
			var me = this;
			// Set the unconstrained dimension before label rotation
			if (me.isHorizontal()) {
				// Reset position before calculating rotation
				me.width = me.maxWidth;
				me.left = 0;
				me.right = me.width;
			} else {
				me.height = me.maxHeight;

				// Reset position before calculating rotation
				me.top = 0;
				me.bottom = me.height;
			}

			// Reset padding
			me.paddingLeft = 0;
			me.paddingTop = 0;
			me.paddingRight = 0;
			me.paddingBottom = 0;

			// Reset minSize
			me.minSize = {
				width: 0,
				height: 0
			};
		},
		afterSetDimensions: noop,

		//

		beforeBuildLabels: noop,
		buildLabels: noop,
		afterBuildLabels: noop,

		//

		beforeFit: noop,
		fit: function() {
			var me = this;
			var valueOrDefault = helpers.valueOrDefault;
			var opts = me.options;
			var display = opts.display;
			var fontSize = valueOrDefault(opts.fontSize, defaults.global.defaultFontSize);
			var minSize = me.minSize;
			var lineCount = helpers.isArray(opts.text) ? opts.text.length : 1;
			var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);
			var textSize = display ? (lineCount * lineHeight) + (opts.padding * 2) : 0;

			if (me.isHorizontal()) {
				minSize.width = me.maxWidth; // fill all the width
				minSize.height = textSize;
			} else {
				minSize.width = textSize;
				minSize.height = me.maxHeight; // fill all the height
			}

			me.width = minSize.width;
			me.height = minSize.height;

		},
		afterFit: noop,

		// Shared Methods
		isHorizontal: function() {
			var pos = this.options.position;
			return pos === 'top' || pos === 'bottom';
		},

		// Actually draw the title block on the canvas
		draw: function() {
			var me = this;
			var ctx = me.ctx;
			var valueOrDefault = helpers.valueOrDefault;
			var opts = me.options;
			var globalDefaults = defaults.global;

			if (opts.display) {
				var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize);
				var fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle);
				var fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily);
				var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily);
				var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);
				var offset = lineHeight / 2 + opts.padding;
				var rotation = 0;
				var top = me.top;
				var left = me.left;
				var bottom = me.bottom;
				var right = me.right;
				var maxWidth, titleX, titleY;

				ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour
				ctx.font = titleFont;

				// Horizontal
				if (me.isHorizontal()) {
					titleX = left + ((right - left) / 2); // midpoint of the width
					titleY = top + offset;
					maxWidth = right - left;
				} else {
					titleX = opts.position === 'left' ? left + offset : right - offset;
					titleY = top + ((bottom - top) / 2);
					maxWidth = bottom - top;
					rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
				}

				ctx.save();
				ctx.translate(titleX, titleY);
				ctx.rotate(rotation);
				ctx.textAlign = 'center';
				ctx.textBaseline = 'middle';

				var text = opts.text;
				if (helpers.isArray(text)) {
					var y = 0;
					for (var i = 0; i < text.length; ++i) {
						ctx.fillText(text[i], 0, y, maxWidth);
						y += lineHeight;
					}
				} else {
					ctx.fillText(text, 0, 0, maxWidth);
				}

				ctx.restore();
			}
		}
	});

	function createNewTitleBlockAndAttach(chart, titleOpts) {
		var title = new Chart.Title({
			ctx: chart.ctx,
			options: titleOpts,
			chart: chart
		});

		layout.configure(chart, title, titleOpts);
		layout.addBox(chart, title);
		chart.titleBlock = title;
	}

	return {
		id: 'title',

		beforeInit: function(chart) {
			var titleOpts = chart.options.title;

			if (titleOpts) {
				createNewTitleBlockAndAttach(chart, titleOpts);
			}
		},

		beforeUpdate: function(chart) {
			var titleOpts = chart.options.title;
			var titleBlock = chart.titleBlock;

			if (titleOpts) {
				helpers.mergeIf(titleOpts, defaults.global.title);

				if (titleBlock) {
					layout.configure(chart, titleBlock, titleOpts);
					titleBlock.options = titleOpts;
				} else {
					createNewTitleBlockAndAttach(chart, titleOpts);
				}
			} else if (titleBlock) {
				Chart.layoutService.removeBox(chart, titleBlock);
				delete chart.titleBlock;
			}
		}
	};
};

},{"25":25,"26":26,"45":45}],52:[function(require,module,exports){
'use strict';

module.exports = function(Chart) {

	// Default config for a category scale
	var defaultConfig = {
		position: 'bottom'
	};

	var DatasetScale = Chart.Scale.extend({
		/**
		* Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those
		* else fall back to data.labels
		* @private
		*/
		getLabels: function() {
			var data = this.chart.data;
			return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels;
		},

		determineDataLimits: function() {
			var me = this;
			var labels = me.getLabels();
			me.minIndex = 0;
			me.maxIndex = labels.length - 1;
			var findIndex;

			if (me.options.ticks.min !== undefined) {
				// user specified min value
				findIndex = labels.indexOf(me.options.ticks.min);
				me.minIndex = findIndex !== -1 ? findIndex : me.minIndex;
			}

			if (me.options.ticks.max !== undefined) {
				// user specified max value
				findIndex = labels.indexOf(me.options.ticks.max);
				me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex;
			}

			me.min = labels[me.minIndex];
			me.max = labels[me.maxIndex];
		},

		buildTicks: function() {
			var me = this;
			var labels = me.getLabels();
			// If we are viewing some subset of labels, slice the original array
			me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1);
		},

		getLabelForIndex: function(index, datasetIndex) {
			var me = this;
			var data = me.chart.data;
			var isHorizontal = me.isHorizontal();

			if (data.yLabels && !isHorizontal) {
				return me.getRightValue(data.datasets[datasetIndex].data[index]);
			}
			return me.ticks[index - me.minIndex];
		},

		// Used to get data value locations.  Value can either be an index or a numerical value
		getPixelForValue: function(value, index) {
			var me = this;
			var offset = me.options.offset;
			// 1 is added because we need the length but we have the indexes
			var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1);

			// If value is a data object, then index is the index in the data array,
			// not the index of the scale. We need to change that.
			var valueCategory;
			if (value !== undefined && value !== null) {
				valueCategory = me.isHorizontal() ? value.x : value.y;
			}
			if (valueCategory !== undefined || (value !== undefined && isNaN(index))) {
				var labels = me.getLabels();
				value = valueCategory || value;
				var idx = labels.indexOf(value);
				index = idx !== -1 ? idx : index;
			}

			if (me.isHorizontal()) {
				var valueWidth = me.width / offsetAmt;
				var widthOffset = (valueWidth * (index - me.minIndex));

				if (offset) {
					widthOffset += (valueWidth / 2);
				}

				return me.left + Math.round(widthOffset);
			}
			var valueHeight = me.height / offsetAmt;
			var heightOffset = (valueHeight * (index - me.minIndex));

			if (offset) {
				heightOffset += (valueHeight / 2);
			}

			return me.top + Math.round(heightOffset);
		},
		getPixelForTick: function(index) {
			return this.getPixelForValue(this.ticks[index], index + this.minIndex, null);
		},
		getValueForPixel: function(pixel) {
			var me = this;
			var offset = me.options.offset;
			var value;
			var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
			var horz = me.isHorizontal();
			var valueDimension = (horz ? me.width : me.height) / offsetAmt;

			pixel -= horz ? me.left : me.top;

			if (offset) {
				pixel -= (valueDimension / 2);
			}

			if (pixel <= 0) {
				value = 0;
			} else {
				value = Math.round(pixel / valueDimension);
			}

			return value + me.minIndex;
		},
		getBasePixel: function() {
			return this.bottom;
		}
	});

	Chart.scaleService.registerScaleType('category', DatasetScale, defaultConfig);

};

},{}],53:[function(require,module,exports){
'use strict';

var defaults = require(25);
var helpers = require(45);
var Ticks = require(34);

module.exports = function(Chart) {

	var defaultConfig = {
		position: 'left',
		ticks: {
			callback: Ticks.formatters.linear
		}
	};

	var LinearScale = Chart.LinearScaleBase.extend({

		determineDataLimits: function() {
			var me = this;
			var opts = me.options;
			var chart = me.chart;
			var data = chart.data;
			var datasets = data.datasets;
			var isHorizontal = me.isHorizontal();
			var DEFAULT_MIN = 0;
			var DEFAULT_MAX = 1;

			function IDMatches(meta) {
				return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
			}

			// First Calculate the range
			me.min = null;
			me.max = null;

			var hasStacks = opts.stacked;
			if (hasStacks === undefined) {
				helpers.each(datasets, function(dataset, datasetIndex) {
					if (hasStacks) {
						return;
					}

					var meta = chart.getDatasetMeta(datasetIndex);
					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
						meta.stack !== undefined) {
						hasStacks = true;
					}
				});
			}

			if (opts.stacked || hasStacks) {
				var valuesPerStack = {};

				helpers.each(datasets, function(dataset, datasetIndex) {
					var meta = chart.getDatasetMeta(datasetIndex);
					var key = [
						meta.type,
						// we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
						((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
						meta.stack
					].join('.');

					if (valuesPerStack[key] === undefined) {
						valuesPerStack[key] = {
							positiveValues: [],
							negativeValues: []
						};
					}

					// Store these per type
					var positiveValues = valuesPerStack[key].positiveValues;
					var negativeValues = valuesPerStack[key].negativeValues;

					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
						helpers.each(dataset.data, function(rawValue, index) {
							var value = +me.getRightValue(rawValue);
							if (isNaN(value) || meta.data[index].hidden) {
								return;
							}

							positiveValues[index] = positiveValues[index] || 0;
							negativeValues[index] = negativeValues[index] || 0;

							if (opts.relativePoints) {
								positiveValues[index] = 100;
							} else if (value < 0) {
								negativeValues[index] += value;
							} else {
								positiveValues[index] += value;
							}
						});
					}
				});

				helpers.each(valuesPerStack, function(valuesForType) {
					var values = valuesForType.positiveValues.concat(valuesForType.negativeValues);
					var minVal = helpers.min(values);
					var maxVal = helpers.max(values);
					me.min = me.min === null ? minVal : Math.min(me.min, minVal);
					me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
				});

			} else {
				helpers.each(datasets, function(dataset, datasetIndex) {
					var meta = chart.getDatasetMeta(datasetIndex);
					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
						helpers.each(dataset.data, function(rawValue, index) {
							var value = +me.getRightValue(rawValue);
							if (isNaN(value) || meta.data[index].hidden) {
								return;
							}

							if (me.min === null) {
								me.min = value;
							} else if (value < me.min) {
								me.min = value;
							}

							if (me.max === null) {
								me.max = value;
							} else if (value > me.max) {
								me.max = value;
							}
						});
					}
				});
			}

			me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN;
			me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX;

			// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
			this.handleTickRangeOptions();
		},
		getTickLimit: function() {
			var maxTicks;
			var me = this;
			var tickOpts = me.options.ticks;

			if (me.isHorizontal()) {
				maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50));
			} else {
				// The factor of 2 used to scale the font size has been experimentally determined.
				var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize);
				maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize)));
			}

			return maxTicks;
		},
		// Called after the ticks are built. We need
		handleDirectionalChanges: function() {
			if (!this.isHorizontal()) {
				// We are in a vertical orientation. The top value is the highest. So reverse the array
				this.ticks.reverse();
			}
		},
		getLabelForIndex: function(index, datasetIndex) {
			return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
		},
		// Utils
		getPixelForValue: function(value) {
			// This must be called after fit has been run so that
			// this.left, this.top, this.right, and this.bottom have been defined
			var me = this;
			var start = me.start;

			var rightValue = +me.getRightValue(value);
			var pixel;
			var range = me.end - start;

			if (me.isHorizontal()) {
				pixel = me.left + (me.width / range * (rightValue - start));
				return Math.round(pixel);
			}

			pixel = me.bottom - (me.height / range * (rightValue - start));
			return Math.round(pixel);
		},
		getValueForPixel: function(pixel) {
			var me = this;
			var isHorizontal = me.isHorizontal();
			var innerDimension = isHorizontal ? me.width : me.height;
			var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension;
			return me.start + ((me.end - me.start) * offset);
		},
		getPixelForTick: function(index) {
			return this.getPixelForValue(this.ticksAsNumbers[index]);
		}
	});
	Chart.scaleService.registerScaleType('linear', LinearScale, defaultConfig);

};

},{"25":25,"34":34,"45":45}],54:[function(require,module,exports){
'use strict';

var helpers = require(45);
var Ticks = require(34);

module.exports = function(Chart) {

	var noop = helpers.noop;

	Chart.LinearScaleBase = Chart.Scale.extend({
		getRightValue: function(value) {
			if (typeof value === 'string') {
				return +value;
			}
			return Chart.Scale.prototype.getRightValue.call(this, value);
		},

		handleTickRangeOptions: function() {
			var me = this;
			var opts = me.options;
			var tickOpts = opts.ticks;

			// If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
			// do nothing since that would make the chart weird. If the user really wants a weird chart
			// axis, they can manually override it
			if (tickOpts.beginAtZero) {
				var minSign = helpers.sign(me.min);
				var maxSign = helpers.sign(me.max);

				if (minSign < 0 && maxSign < 0) {
					// move the top up to 0
					me.max = 0;
				} else if (minSign > 0 && maxSign > 0) {
					// move the bottom down to 0
					me.min = 0;
				}
			}

			var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined;
			var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined;

			if (tickOpts.min !== undefined) {
				me.min = tickOpts.min;
			} else if (tickOpts.suggestedMin !== undefined) {
				if (me.min === null) {
					me.min = tickOpts.suggestedMin;
				} else {
					me.min = Math.min(me.min, tickOpts.suggestedMin);
				}
			}

			if (tickOpts.max !== undefined) {
				me.max = tickOpts.max;
			} else if (tickOpts.suggestedMax !== undefined) {
				if (me.max === null) {
					me.max = tickOpts.suggestedMax;
				} else {
					me.max = Math.max(me.max, tickOpts.suggestedMax);
				}
			}

			if (setMin !== setMax) {
				// We set the min or the max but not both.
				// So ensure that our range is good
				// Inverted or 0 length range can happen when
				// ticks.min is set, and no datasets are visible
				if (me.min >= me.max) {
					if (setMin) {
						me.max = me.min + 1;
					} else {
						me.min = me.max - 1;
					}
				}
			}

			if (me.min === me.max) {
				me.max++;

				if (!tickOpts.beginAtZero) {
					me.min--;
				}
			}
		},
		getTickLimit: noop,
		handleDirectionalChanges: noop,

		buildTicks: function() {
			var me = this;
			var opts = me.options;
			var tickOpts = opts.ticks;

			// Figure out what the max number of ticks we can support it is based on the size of
			// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
			// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
			// the graph. Make sure we always have at least 2 ticks
			var maxTicks = me.getTickLimit();
			maxTicks = Math.max(2, maxTicks);

			var numericGeneratorOptions = {
				maxTicks: maxTicks,
				min: tickOpts.min,
				max: tickOpts.max,
				stepSize: helpers.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize)
			};
			var ticks = me.ticks = Ticks.generators.linear(numericGeneratorOptions, me);

			me.handleDirectionalChanges();

			// At this point, we need to update our max and min given the tick values since we have expanded the
			// range of the scale
			me.max = helpers.max(ticks);
			me.min = helpers.min(ticks);

			if (tickOpts.reverse) {
				ticks.reverse();

				me.start = me.max;
				me.end = me.min;
			} else {
				me.start = me.min;
				me.end = me.max;
			}
		},
		convertTicksToLabels: function() {
			var me = this;
			me.ticksAsNumbers = me.ticks.slice();
			me.zeroLineIndex = me.ticks.indexOf(0);

			Chart.Scale.prototype.convertTicksToLabels.call(me);
		}
	});
};

},{"34":34,"45":45}],55:[function(require,module,exports){
'use strict';

var helpers = require(45);
var Ticks = require(34);

module.exports = function(Chart) {

	var defaultConfig = {
		position: 'left',

		// label settings
		ticks: {
			callback: Ticks.formatters.logarithmic
		}
	};

	var LogarithmicScale = Chart.Scale.extend({
		determineDataLimits: function() {
			var me = this;
			var opts = me.options;
			var tickOpts = opts.ticks;
			var chart = me.chart;
			var data = chart.data;
			var datasets = data.datasets;
			var valueOrDefault = helpers.valueOrDefault;
			var isHorizontal = me.isHorizontal();
			function IDMatches(meta) {
				return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
			}

			// Calculate Range
			me.min = null;
			me.max = null;
			me.minNotZero = null;

			var hasStacks = opts.stacked;
			if (hasStacks === undefined) {
				helpers.each(datasets, function(dataset, datasetIndex) {
					if (hasStacks) {
						return;
					}

					var meta = chart.getDatasetMeta(datasetIndex);
					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
						meta.stack !== undefined) {
						hasStacks = true;
					}
				});
			}

			if (opts.stacked || hasStacks) {
				var valuesPerStack = {};

				helpers.each(datasets, function(dataset, datasetIndex) {
					var meta = chart.getDatasetMeta(datasetIndex);
					var key = [
						meta.type,
						// we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
						((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
						meta.stack
					].join('.');

					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
						if (valuesPerStack[key] === undefined) {
							valuesPerStack[key] = [];
						}

						helpers.each(dataset.data, function(rawValue, index) {
							var values = valuesPerStack[key];
							var value = +me.getRightValue(rawValue);
							if (isNaN(value) || meta.data[index].hidden) {
								return;
							}

							values[index] = values[index] || 0;

							if (opts.relativePoints) {
								values[index] = 100;
							} else {
								// Don't need to split positive and negative since the log scale can't handle a 0 crossing
								values[index] += value;
							}
						});
					}
				});

				helpers.each(valuesPerStack, function(valuesForType) {
					var minVal = helpers.min(valuesForType);
					var maxVal = helpers.max(valuesForType);
					me.min = me.min === null ? minVal : Math.min(me.min, minVal);
					me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
				});

			} else {
				helpers.each(datasets, function(dataset, datasetIndex) {
					var meta = chart.getDatasetMeta(datasetIndex);
					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
						helpers.each(dataset.data, function(rawValue, index) {
							var value = +me.getRightValue(rawValue);
							if (isNaN(value) || meta.data[index].hidden) {
								return;
							}

							if (me.min === null) {
								me.min = value;
							} else if (value < me.min) {
								me.min = value;
							}

							if (me.max === null) {
								me.max = value;
							} else if (value > me.max) {
								me.max = value;
							}

							if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {
								me.minNotZero = value;
							}
						});
					}
				});
			}

			me.min = valueOrDefault(tickOpts.min, me.min);
			me.max = valueOrDefault(tickOpts.max, me.max);

			if (me.min === me.max) {
				if (me.min !== 0 && me.min !== null) {
					me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1);
					me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1);
				} else {
					me.min = 1;
					me.max = 10;
				}
			}
		},
		buildTicks: function() {
			var me = this;
			var opts = me.options;
			var tickOpts = opts.ticks;

			var generationOptions = {
				min: tickOpts.min,
				max: tickOpts.max
			};
			var ticks = me.ticks = Ticks.generators.logarithmic(generationOptions, me);

			if (!me.isHorizontal()) {
				// We are in a vertical orientation. The top value is the highest. So reverse the array
				ticks.reverse();
			}

			// At this point, we need to update our max and min given the tick values since we have expanded the
			// range of the scale
			me.max = helpers.max(ticks);
			me.min = helpers.min(ticks);

			if (tickOpts.reverse) {
				ticks.reverse();

				me.start = me.max;
				me.end = me.min;
			} else {
				me.start = me.min;
				me.end = me.max;
			}
		},
		convertTicksToLabels: function() {
			this.tickValues = this.ticks.slice();

			Chart.Scale.prototype.convertTicksToLabels.call(this);
		},
		// Get the correct tooltip label
		getLabelForIndex: function(index, datasetIndex) {
			return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
		},
		getPixelForTick: function(index) {
			return this.getPixelForValue(this.tickValues[index]);
		},
		getPixelForValue: function(value) {
			var me = this;
			var start = me.start;
			var newVal = +me.getRightValue(value);
			var opts = me.options;
			var tickOpts = opts.ticks;
			var innerDimension, pixel, range;

			if (me.isHorizontal()) {
				range = helpers.log10(me.end) - helpers.log10(start); // todo: if start === 0
				if (newVal === 0) {
					pixel = me.left;
				} else {
					innerDimension = me.width;
					pixel = me.left + (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
				}
			} else {
				// Bottom - top since pixels increase downward on a screen
				innerDimension = me.height;
				if (start === 0 && !tickOpts.reverse) {
					range = helpers.log10(me.end) - helpers.log10(me.minNotZero);
					if (newVal === start) {
						pixel = me.bottom;
					} else if (newVal === me.minNotZero) {
						pixel = me.bottom - innerDimension * 0.02;
					} else {
						pixel = me.bottom - innerDimension * 0.02 - (innerDimension * 0.98 / range * (helpers.log10(newVal) - helpers.log10(me.minNotZero)));
					}
				} else if (me.end === 0 && tickOpts.reverse) {
					range = helpers.log10(me.start) - helpers.log10(me.minNotZero);
					if (newVal === me.end) {
						pixel = me.top;
					} else if (newVal === me.minNotZero) {
						pixel = me.top + innerDimension * 0.02;
					} else {
						pixel = me.top + innerDimension * 0.02 + (innerDimension * 0.98 / range * (helpers.log10(newVal) - helpers.log10(me.minNotZero)));
					}
				} else if (newVal === 0) {
					pixel = tickOpts.reverse ? me.top : me.bottom;
				} else {
					range = helpers.log10(me.end) - helpers.log10(start);
					innerDimension = me.height;
					pixel = me.bottom - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
				}
			}
			return pixel;
		},
		getValueForPixel: function(pixel) {
			var me = this;
			var range = helpers.log10(me.end) - helpers.log10(me.start);
			var value, innerDimension;

			if (me.isHorizontal()) {
				innerDimension = me.width;
				value = me.start * Math.pow(10, (pixel - me.left) * range / innerDimension);
			} else { // todo: if start === 0
				innerDimension = me.height;
				value = Math.pow(10, (me.bottom - pixel) * range / innerDimension) / me.start;
			}
			return value;
		}
	});
	Chart.scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig);

};

},{"34":34,"45":45}],56:[function(require,module,exports){
'use strict';

var defaults = require(25);
var helpers = require(45);
var Ticks = require(34);

module.exports = function(Chart) {

	var globalDefaults = defaults.global;

	var defaultConfig = {
		display: true,

		// Boolean - Whether to animate scaling the chart from the centre
		animate: true,
		position: 'chartArea',

		angleLines: {
			display: true,
			color: 'rgba(0, 0, 0, 0.1)',
			lineWidth: 1
		},

		gridLines: {
			circular: false
		},

		// label settings
		ticks: {
			// Boolean - Show a backdrop to the scale label
			showLabelBackdrop: true,

			// String - The colour of the label backdrop
			backdropColor: 'rgba(255,255,255,0.75)',

			// Number - The backdrop padding above & below the label in pixels
			backdropPaddingY: 2,

			// Number - The backdrop padding to the side of the label in pixels
			backdropPaddingX: 2,

			callback: Ticks.formatters.linear
		},

		pointLabels: {
			// Boolean - if true, show point labels
			display: true,

			// Number - Point label font size in pixels
			fontSize: 10,

			// Function - Used to convert point labels
			callback: function(label) {
				return label;
			}
		}
	};

	function getValueCount(scale) {
		var opts = scale.options;
		return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0;
	}

	function getPointLabelFontOptions(scale) {
		var pointLabelOptions = scale.options.pointLabels;
		var fontSize = helpers.valueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize);
		var fontStyle = helpers.valueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle);
		var fontFamily = helpers.valueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily);
		var font = helpers.fontString(fontSize, fontStyle, fontFamily);

		return {
			size: fontSize,
			style: fontStyle,
			family: fontFamily,
			font: font
		};
	}

	function measureLabelSize(ctx, fontSize, label) {
		if (helpers.isArray(label)) {
			return {
				w: helpers.longestText(ctx, ctx.font, label),
				h: (label.length * fontSize) + ((label.length - 1) * 1.5 * fontSize)
			};
		}

		return {
			w: ctx.measureText(label).width,
			h: fontSize
		};
	}

	function determineLimits(angle, pos, size, min, max) {
		if (angle === min || angle === max) {
			return {
				start: pos - (size / 2),
				end: pos + (size / 2)
			};
		} else if (angle < min || angle > max) {
			return {
				start: pos - size - 5,
				end: pos
			};
		}

		return {
			start: pos,
			end: pos + size + 5
		};
	}

	/**
	 * Helper function to fit a radial linear scale with point labels
	 */
	function fitWithPointLabels(scale) {
		/*
		 * Right, this is really confusing and there is a lot of maths going on here
		 * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
		 *
		 * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
		 *
		 * Solution:
		 *
		 * We assume the radius of the polygon is half the size of the canvas at first
		 * at each index we check if the text overlaps.
		 *
		 * Where it does, we store that angle and that index.
		 *
		 * After finding the largest index and angle we calculate how much we need to remove
		 * from the shape radius to move the point inwards by that x.
		 *
		 * We average the left and right distances to get the maximum shape radius that can fit in the box
		 * along with labels.
		 *
		 * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
		 * on each side, removing that from the size, halving it and adding the left x protrusion width.
		 *
		 * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
		 * and position it in the most space efficient manner
		 *
		 * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
		 */

		var plFont = getPointLabelFontOptions(scale);

		// Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
		// Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
		var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);
		var furthestLimits = {
			r: scale.width,
			l: 0,
			t: scale.height,
			b: 0
		};
		var furthestAngles = {};
		var i, textSize, pointPosition;

		scale.ctx.font = plFont.font;
		scale._pointLabelSizes = [];

		var valueCount = getValueCount(scale);
		for (i = 0; i < valueCount; i++) {
			pointPosition = scale.getPointPosition(i, largestPossibleRadius);
			textSize = measureLabelSize(scale.ctx, plFont.size, scale.pointLabels[i] || '');
			scale._pointLabelSizes[i] = textSize;

			// Add quarter circle to make degree 0 mean top of circle
			var angleRadians = scale.getIndexAngle(i);
			var angle = helpers.toDegrees(angleRadians) % 360;
			var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
			var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);

			if (hLimits.start < furthestLimits.l) {
				furthestLimits.l = hLimits.start;
				furthestAngles.l = angleRadians;
			}

			if (hLimits.end > furthestLimits.r) {
				furthestLimits.r = hLimits.end;
				furthestAngles.r = angleRadians;
			}

			if (vLimits.start < furthestLimits.t) {
				furthestLimits.t = vLimits.start;
				furthestAngles.t = angleRadians;
			}

			if (vLimits.end > furthestLimits.b) {
				furthestLimits.b = vLimits.end;
				furthestAngles.b = angleRadians;
			}
		}

		scale.setReductions(largestPossibleRadius, furthestLimits, furthestAngles);
	}

	/**
	 * Helper function to fit a radial linear scale with no point labels
	 */
	function fit(scale) {
		var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);
		scale.drawingArea = Math.round(largestPossibleRadius);
		scale.setCenterPoint(0, 0, 0, 0);
	}

	function getTextAlignForAngle(angle) {
		if (angle === 0 || angle === 180) {
			return 'center';
		} else if (angle < 180) {
			return 'left';
		}

		return 'right';
	}

	function fillText(ctx, text, position, fontSize) {
		if (helpers.isArray(text)) {
			var y = position.y;
			var spacing = 1.5 * fontSize;

			for (var i = 0; i < text.length; ++i) {
				ctx.fillText(text[i], position.x, y);
				y += spacing;
			}
		} else {
			ctx.fillText(text, position.x, position.y);
		}
	}

	function adjustPointPositionForLabelHeight(angle, textSize, position) {
		if (angle === 90 || angle === 270) {
			position.y -= (textSize.h / 2);
		} else if (angle > 270 || angle < 90) {
			position.y -= textSize.h;
		}
	}

	function drawPointLabels(scale) {
		var ctx = scale.ctx;
		var valueOrDefault = helpers.valueOrDefault;
		var opts = scale.options;
		var angleLineOpts = opts.angleLines;
		var pointLabelOpts = opts.pointLabels;

		ctx.lineWidth = angleLineOpts.lineWidth;
		ctx.strokeStyle = angleLineOpts.color;

		var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);

		// Point Label Font
		var plFont = getPointLabelFontOptions(scale);

		ctx.textBaseline = 'top';

		for (var i = getValueCount(scale) - 1; i >= 0; i--) {
			if (angleLineOpts.display) {
				var outerPosition = scale.getPointPosition(i, outerDistance);
				ctx.beginPath();
				ctx.moveTo(scale.xCenter, scale.yCenter);
				ctx.lineTo(outerPosition.x, outerPosition.y);
				ctx.stroke();
				ctx.closePath();
			}

			if (pointLabelOpts.display) {
				// Extra 3px out for some label spacing
				var pointLabelPosition = scale.getPointPosition(i, outerDistance + 5);

				// Keep this in loop since we may support array properties here
				var pointLabelFontColor = valueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor);
				ctx.font = plFont.font;
				ctx.fillStyle = pointLabelFontColor;

				var angleRadians = scale.getIndexAngle(i);
				var angle = helpers.toDegrees(angleRadians);
				ctx.textAlign = getTextAlignForAngle(angle);
				adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);
				fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.size);
			}
		}
	}

	function drawRadiusLine(scale, gridLineOpts, radius, index) {
		var ctx = scale.ctx;
		ctx.strokeStyle = helpers.valueAtIndexOrDefault(gridLineOpts.color, index - 1);
		ctx.lineWidth = helpers.valueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1);

		if (scale.options.gridLines.circular) {
			// Draw circular arcs between the points
			ctx.beginPath();
			ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2);
			ctx.closePath();
			ctx.stroke();
		} else {
			// Draw straight lines connecting each index
			var valueCount = getValueCount(scale);

			if (valueCount === 0) {
				return;
			}

			ctx.beginPath();
			var pointPosition = scale.getPointPosition(0, radius);
			ctx.moveTo(pointPosition.x, pointPosition.y);

			for (var i = 1; i < valueCount; i++) {
				pointPosition = scale.getPointPosition(i, radius);
				ctx.lineTo(pointPosition.x, pointPosition.y);
			}

			ctx.closePath();
			ctx.stroke();
		}
	}

	function numberOrZero(param) {
		return helpers.isNumber(param) ? param : 0;
	}

	var LinearRadialScale = Chart.LinearScaleBase.extend({
		setDimensions: function() {
			var me = this;
			var opts = me.options;
			var tickOpts = opts.ticks;
			// Set the unconstrained dimension before label rotation
			me.width = me.maxWidth;
			me.height = me.maxHeight;
			me.xCenter = Math.round(me.width / 2);
			me.yCenter = Math.round(me.height / 2);

			var minSize = helpers.min([me.height, me.width]);
			var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
			me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2);
		},
		determineDataLimits: function() {
			var me = this;
			var chart = me.chart;
			var min = Number.POSITIVE_INFINITY;
			var max = Number.NEGATIVE_INFINITY;

			helpers.each(chart.data.datasets, function(dataset, datasetIndex) {
				if (chart.isDatasetVisible(datasetIndex)) {
					var meta = chart.getDatasetMeta(datasetIndex);

					helpers.each(dataset.data, function(rawValue, index) {
						var value = +me.getRightValue(rawValue);
						if (isNaN(value) || meta.data[index].hidden) {
							return;
						}

						min = Math.min(value, min);
						max = Math.max(value, max);
					});
				}
			});

			me.min = (min === Number.POSITIVE_INFINITY ? 0 : min);
			me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max);

			// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
			me.handleTickRangeOptions();
		},
		getTickLimit: function() {
			var tickOpts = this.options.ticks;
			var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
			return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize)));
		},
		convertTicksToLabels: function() {
			var me = this;

			Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me);

			// Point labels
			me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me);
		},
		getLabelForIndex: function(index, datasetIndex) {
			return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
		},
		fit: function() {
			if (this.options.pointLabels.display) {
				fitWithPointLabels(this);
			} else {
				fit(this);
			}
		},
		/**
		 * Set radius reductions and determine new radius and center point
		 * @private
		 */
		setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) {
			var me = this;
			var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);
			var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r);
			var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);
			var radiusReductionBottom = -Math.max(furthestLimits.b - me.height, 0) / Math.cos(furthestAngles.b);

			radiusReductionLeft = numberOrZero(radiusReductionLeft);
			radiusReductionRight = numberOrZero(radiusReductionRight);
			radiusReductionTop = numberOrZero(radiusReductionTop);
			radiusReductionBottom = numberOrZero(radiusReductionBottom);

			me.drawingArea = Math.min(
				Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),
				Math.round(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2));
			me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
		},
		setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) {
			var me = this;
			var maxRight = me.width - rightMovement - me.drawingArea;
			var maxLeft = leftMovement + me.drawingArea;
			var maxTop = topMovement + me.drawingArea;
			var maxBottom = me.height - bottomMovement - me.drawingArea;

			me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left);
			me.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top);
		},

		getIndexAngle: function(index) {
			var angleMultiplier = (Math.PI * 2) / getValueCount(this);
			var startAngle = this.chart.options && this.chart.options.startAngle ?
				this.chart.options.startAngle :
				0;

			var startAngleRadians = startAngle * Math.PI * 2 / 360;

			// Start from the top instead of right, so remove a quarter of the circle
			return index * angleMultiplier + startAngleRadians;
		},
		getDistanceFromCenterForValue: function(value) {
			var me = this;

			if (value === null) {
				return 0; // null always in center
			}

			// Take into account half font size + the yPadding of the top value
			var scalingFactor = me.drawingArea / (me.max - me.min);
			if (me.options.ticks.reverse) {
				return (me.max - value) * scalingFactor;
			}
			return (value - me.min) * scalingFactor;
		},
		getPointPosition: function(index, distanceFromCenter) {
			var me = this;
			var thisAngle = me.getIndexAngle(index) - (Math.PI / 2);
			return {
				x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter,
				y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter
			};
		},
		getPointPositionForValue: function(index, value) {
			return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
		},

		getBasePosition: function() {
			var me = this;
			var min = me.min;
			var max = me.max;

			return me.getPointPositionForValue(0,
				me.beginAtZero ? 0 :
				min < 0 && max < 0 ? max :
				min > 0 && max > 0 ? min :
				0);
		},

		draw: function() {
			var me = this;
			var opts = me.options;
			var gridLineOpts = opts.gridLines;
			var tickOpts = opts.ticks;
			var valueOrDefault = helpers.valueOrDefault;

			if (opts.display) {
				var ctx = me.ctx;
				var startAngle = this.getIndexAngle(0);

				// Tick Font
				var tickFontSize = valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
				var tickFontStyle = valueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle);
				var tickFontFamily = valueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily);
				var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);

				helpers.each(me.ticks, function(label, index) {
					// Don't draw a centre value (if it is minimum)
					if (index > 0 || tickOpts.reverse) {
						var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);

						// Draw circular lines around the scale
						if (gridLineOpts.display && index !== 0) {
							drawRadiusLine(me, gridLineOpts, yCenterOffset, index);
						}

						if (tickOpts.display) {
							var tickFontColor = valueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor);
							ctx.font = tickLabelFont;

							ctx.save();
							ctx.translate(me.xCenter, me.yCenter);
							ctx.rotate(startAngle);

							if (tickOpts.showLabelBackdrop) {
								var labelWidth = ctx.measureText(label).width;
								ctx.fillStyle = tickOpts.backdropColor;
								ctx.fillRect(
									-labelWidth / 2 - tickOpts.backdropPaddingX,
									-yCenterOffset - tickFontSize / 2 - tickOpts.backdropPaddingY,
									labelWidth + tickOpts.backdropPaddingX * 2,
									tickFontSize + tickOpts.backdropPaddingY * 2
								);
							}

							ctx.textAlign = 'center';
							ctx.textBaseline = 'middle';
							ctx.fillStyle = tickFontColor;
							ctx.fillText(label, 0, -yCenterOffset);
							ctx.restore();
						}
					}
				});

				if (opts.angleLines.display || opts.pointLabels.display) {
					drawPointLabels(me);
				}
			}
		}
	});
	Chart.scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig);

};

},{"25":25,"34":34,"45":45}],57:[function(require,module,exports){
/* global window: false */
'use strict';

var moment = require(6);
moment = typeof moment === 'function' ? moment : window.moment;

var defaults = require(25);
var helpers = require(45);

// Integer constants are from the ES6 spec.
var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991;
var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;

var INTERVALS = {
	millisecond: {
		major: true,
		size: 1,
		steps: [1, 2, 5, 10, 20, 50, 100, 250, 500]
	},
	second: {
		major: true,
		size: 1000,
		steps: [1, 2, 5, 10, 30]
	},
	minute: {
		major: true,
		size: 60000,
		steps: [1, 2, 5, 10, 30]
	},
	hour: {
		major: true,
		size: 3600000,
		steps: [1, 2, 3, 6, 12]
	},
	day: {
		major: true,
		size: 86400000,
		steps: [1, 2, 5]
	},
	week: {
		major: false,
		size: 604800000,
		steps: [1, 2, 3, 4]
	},
	month: {
		major: true,
		size: 2.628e9,
		steps: [1, 2, 3]
	},
	quarter: {
		major: false,
		size: 7.884e9,
		steps: [1, 2, 3, 4]
	},
	year: {
		major: true,
		size: 3.154e10
	}
};

var UNITS = Object.keys(INTERVALS);

function sorter(a, b) {
	return a - b;
}

function arrayUnique(items) {
	var hash = {};
	var out = [];
	var i, ilen, item;

	for (i = 0, ilen = items.length; i < ilen; ++i) {
		item = items[i];
		if (!hash[item]) {
			hash[item] = true;
			out.push(item);
		}
	}

	return out;
}

/**
 * Returns an array of {time, pos} objects used to interpolate a specific `time` or position
 * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is
 * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other
 * extremity (left + width or top + height). Note that it would be more optimized to directly
 * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need
 * to create the lookup table. The table ALWAYS contains at least two items: min and max.
 *
 * @param {Number[]} timestamps - timestamps sorted from lowest to highest.
 * @param {String} distribution - If 'linear', timestamps will be spread linearly along the min
 * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}.
 * If 'series', timestamps will be positioned at the same distance from each other. In this
 * case, only timestamps that break the time linearity are registered, meaning that in the
 * best case, all timestamps are linear, the table contains only min and max.
 */
function buildLookupTable(timestamps, min, max, distribution) {
	if (distribution === 'linear' || !timestamps.length) {
		return [
			{time: min, pos: 0},
			{time: max, pos: 1}
		];
	}

	var table = [];
	var items = [min];
	var i, ilen, prev, curr, next;

	for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
		curr = timestamps[i];
		if (curr > min && curr < max) {
			items.push(curr);
		}
	}

	items.push(max);

	for (i = 0, ilen = items.length; i < ilen; ++i) {
		next = items[i + 1];
		prev = items[i - 1];
		curr = items[i];

		// only add points that breaks the scale linearity
		if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) {
			table.push({time: curr, pos: i / (ilen - 1)});
		}
	}

	return table;
}

// @see adapted from http://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/
function lookup(table, key, value) {
	var lo = 0;
	var hi = table.length - 1;
	var mid, i0, i1;

	while (lo >= 0 && lo <= hi) {
		mid = (lo + hi) >> 1;
		i0 = table[mid - 1] || null;
		i1 = table[mid];

		if (!i0) {
			// given value is outside table (before first item)
			return {lo: null, hi: i1};
		} else if (i1[key] < value) {
			lo = mid + 1;
		} else if (i0[key] > value) {
			hi = mid - 1;
		} else {
			return {lo: i0, hi: i1};
		}
	}

	// given value is outside table (after last item)
	return {lo: i1, hi: null};
}

/**
 * Linearly interpolates the given source `value` using the table items `skey` values and
 * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos')
 * returns the position for a timestamp equal to 42. If value is out of bounds, values at
 * index [0, 1] or [n - 1, n] are used for the interpolation.
 */
function interpolate(table, skey, sval, tkey) {
	var range = lookup(table, skey, sval);

	// Note: the lookup table ALWAYS contains at least 2 items (min and max)
	var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo;
	var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi;

	var span = next[skey] - prev[skey];
	var ratio = span ? (sval - prev[skey]) / span : 0;
	var offset = (next[tkey] - prev[tkey]) * ratio;

	return prev[tkey] + offset;
}

/**
 * Convert the given value to a moment object using the given time options.
 * @see http://momentjs.com/docs/#/parsing/
 */
function momentify(value, options) {
	var parser = options.parser;
	var format = options.parser || options.format;

	if (typeof parser === 'function') {
		return parser(value);
	}

	if (typeof value === 'string' && typeof format === 'string') {
		return moment(value, format);
	}

	if (!(value instanceof moment)) {
		value = moment(value);
	}

	if (value.isValid()) {
		return value;
	}

	// Labels are in an incompatible moment format and no `parser` has been provided.
	// The user might still use the deprecated `format` option to convert his inputs.
	if (typeof format === 'function') {
		return format(value);
	}

	return value;
}

function parse(input, scale) {
	if (helpers.isNullOrUndef(input)) {
		return null;
	}

	var options = scale.options.time;
	var value = momentify(scale.getRightValue(input), options);
	if (!value.isValid()) {
		return null;
	}

	if (options.round) {
		value.startOf(options.round);
	}

	return value.valueOf();
}

/**
 * Returns the number of unit to skip to be able to display up to `capacity` number of ticks
 * in `unit` for the given `min` / `max` range and respecting the interval steps constraints.
 */
function determineStepSize(min, max, unit, capacity) {
	var range = max - min;
	var interval = INTERVALS[unit];
	var milliseconds = interval.size;
	var steps = interval.steps;
	var i, ilen, factor;

	if (!steps) {
		return Math.ceil(range / ((capacity || 1) * milliseconds));
	}

	for (i = 0, ilen = steps.length; i < ilen; ++i) {
		factor = steps[i];
		if (Math.ceil(range / (milliseconds * factor)) <= capacity) {
			break;
		}
	}

	return factor;
}

function determineUnit(minUnit, min, max, capacity) {
	var ilen = UNITS.length;
	var i, interval, factor;

	for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) {
		interval = INTERVALS[UNITS[i]];
		factor = interval.steps ? interval.steps[interval.steps.length - 1] : MAX_INTEGER;

		if (Math.ceil((max - min) / (factor * interval.size)) <= capacity) {
			return UNITS[i];
		}
	}

	return UNITS[ilen - 1];
}

function determineMajorUnit(unit) {
	for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) {
		if (INTERVALS[UNITS[i]].major) {
			return UNITS[i];
		}
	}
}

/**
 * Generates a maximum of `capacity` timestamps between min and max, rounded to the
 * `minor` unit, aligned on the `major` unit and using the given scale time `options`.
 * Important: this method can return ticks outside the min and max range, it's the
 * responsibility of the calling code to clamp values if needed.
 */
function generate(min, max, minor, major, capacity, options) {
	var timeOpts = options.time;
	var stepSize = helpers.valueOrDefault(timeOpts.stepSize, timeOpts.unitStepSize);
	var weekday = minor === 'week' ? timeOpts.isoWeekday : false;
	var majorTicksEnabled = options.ticks.major.enabled;
	var interval = INTERVALS[minor];
	var first = moment(min);
	var last = moment(max);
	var ticks = [];
	var time;

	if (!stepSize) {
		stepSize = determineStepSize(min, max, minor, capacity);
	}

	// For 'week' unit, handle the first day of week option
	if (weekday) {
		first = first.isoWeekday(weekday);
		last = last.isoWeekday(weekday);
	}

	// Align first/last ticks on unit
	first = first.startOf(weekday ? 'day' : minor);
	last = last.startOf(weekday ? 'day' : minor);

	// Make sure that the last tick include max
	if (last < max) {
		last.add(1, minor);
	}

	time = moment(first);

	if (majorTicksEnabled && major && !weekday && !timeOpts.round) {
		// Align the first tick on the previous `minor` unit aligned on the `major` unit:
		// we first aligned time on the previous `major` unit then add the number of full
		// stepSize there is between first and the previous major time.
		time.startOf(major);
		time.add(~~((first - time) / (interval.size * stepSize)) * stepSize, minor);
	}

	for (; time < last; time.add(stepSize, minor)) {
		ticks.push(+time);
	}

	ticks.push(+time);

	return ticks;
}

/**
 * Returns the right and left offsets from edges in the form of {left, right}.
 * Offsets are added when the `offset` option is true.
 */
function computeOffsets(table, ticks, min, max, options) {
	var left = 0;
	var right = 0;
	var upper, lower;

	if (options.offset && ticks.length) {
		if (!options.time.min) {
			upper = ticks.length > 1 ? ticks[1] : max;
			lower = ticks[0];
			left = (
				interpolate(table, 'time', upper, 'pos') -
				interpolate(table, 'time', lower, 'pos')
			) / 2;
		}
		if (!options.time.max) {
			upper = ticks[ticks.length - 1];
			lower = ticks.length > 1 ? ticks[ticks.length - 2] : min;
			right = (
				interpolate(table, 'time', upper, 'pos') -
				interpolate(table, 'time', lower, 'pos')
			) / 2;
		}
	}

	return {left: left, right: right};
}

function ticksFromTimestamps(values, majorUnit) {
	var ticks = [];
	var i, ilen, value, major;

	for (i = 0, ilen = values.length; i < ilen; ++i) {
		value = values[i];
		major = majorUnit ? value === +moment(value).startOf(majorUnit) : false;

		ticks.push({
			value: value,
			major: major
		});
	}

	return ticks;
}

module.exports = function(Chart) {

	var defaultConfig = {
		position: 'bottom',

		/**
		 * Data distribution along the scale:
		 * - 'linear': data are spread according to their time (distances can vary),
		 * - 'series': data are spread at the same distance from each other.
		 * @see https://github.com/chartjs/Chart.js/pull/4507
		 * @since 2.7.0
		 */
		distribution: 'linear',

		/**
		 * Scale boundary strategy (bypassed by min/max time options)
		 * - `data`: make sure data are fully visible, ticks outside are removed
		 * - `ticks`: make sure ticks are fully visible, data outside are truncated
		 * @see https://github.com/chartjs/Chart.js/pull/4556
		 * @since 2.7.0
		 */
		bounds: 'data',

		time: {
			parser: false, // false == a pattern string from http://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment
			format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/
			unit: false, // false == automatic or override with week, month, year, etc.
			round: false, // none, or override with week, month, year, etc.
			displayFormat: false, // DEPRECATED
			isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/
			minUnit: 'millisecond',

			// defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/
			displayFormats: {
				millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM,
				second: 'h:mm:ss a', // 11:20:01 AM
				minute: 'h:mm a', // 11:20 AM
				hour: 'hA', // 5PM
				day: 'MMM D', // Sep 4
				week: 'll', // Week 46, or maybe "[W]WW - YYYY" ?
				month: 'MMM YYYY', // Sept 2015
				quarter: '[Q]Q - YYYY', // Q3
				year: 'YYYY' // 2015
			},
		},
		ticks: {
			autoSkip: false,

			/**
			 * Ticks generation input values:
			 * - 'auto': generates "optimal" ticks based on scale size and time options.
			 * - 'data': generates ticks from data (including labels from data {t|x|y} objects).
			 * - 'labels': generates ticks from user given `data.labels` values ONLY.
			 * @see https://github.com/chartjs/Chart.js/pull/4507
			 * @since 2.7.0
			 */
			source: 'auto',

			major: {
				enabled: false
			}
		}
	};

	var TimeScale = Chart.Scale.extend({
		initialize: function() {
			if (!moment) {
				throw new Error('Chart.js - Moment.js could not be found! You must include it before Chart.js to use the time scale. Download at https://momentjs.com');
			}

			this.mergeTicksOptions();

			Chart.Scale.prototype.initialize.call(this);
		},

		update: function() {
			var me = this;
			var options = me.options;

			// DEPRECATIONS: output a message only one time per update
			if (options.time && options.time.format) {
				console.warn('options.time.format is deprecated and replaced by options.time.parser.');
			}

			return Chart.Scale.prototype.update.apply(me, arguments);
		},

		/**
		 * Allows data to be referenced via 't' attribute
		 */
		getRightValue: function(rawValue) {
			if (rawValue && rawValue.t !== undefined) {
				rawValue = rawValue.t;
			}
			return Chart.Scale.prototype.getRightValue.call(this, rawValue);
		},

		determineDataLimits: function() {
			var me = this;
			var chart = me.chart;
			var timeOpts = me.options.time;
			var min = parse(timeOpts.min, me) || MAX_INTEGER;
			var max = parse(timeOpts.max, me) || MIN_INTEGER;
			var timestamps = [];
			var datasets = [];
			var labels = [];
			var i, j, ilen, jlen, data, timestamp;

			// Convert labels to timestamps
			for (i = 0, ilen = chart.data.labels.length; i < ilen; ++i) {
				labels.push(parse(chart.data.labels[i], me));
			}

			// Convert data to timestamps
			for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
				if (chart.isDatasetVisible(i)) {
					data = chart.data.datasets[i].data;

					// Let's consider that all data have the same format.
					if (helpers.isObject(data[0])) {
						datasets[i] = [];

						for (j = 0, jlen = data.length; j < jlen; ++j) {
							timestamp = parse(data[j], me);
							timestamps.push(timestamp);
							datasets[i][j] = timestamp;
						}
					} else {
						timestamps.push.apply(timestamps, labels);
						datasets[i] = labels.slice(0);
					}
				} else {
					datasets[i] = [];
				}
			}

			if (labels.length) {
				// Sort labels **after** data have been converted
				labels = arrayUnique(labels).sort(sorter);
				min = Math.min(min, labels[0]);
				max = Math.max(max, labels[labels.length - 1]);
			}

			if (timestamps.length) {
				timestamps = arrayUnique(timestamps).sort(sorter);
				min = Math.min(min, timestamps[0]);
				max = Math.max(max, timestamps[timestamps.length - 1]);
			}

			// In case there is no valid min/max, let's use today limits
			min = min === MAX_INTEGER ? +moment().startOf('day') : min;
			max = max === MIN_INTEGER ? +moment().endOf('day') + 1 : max;

			// Make sure that max is strictly higher than min (required by the lookup table)
			me.min = Math.min(min, max);
			me.max = Math.max(min + 1, max);

			// PRIVATE
			me._horizontal = me.isHorizontal();
			me._table = [];
			me._timestamps = {
				data: timestamps,
				datasets: datasets,
				labels: labels
			};
		},

		buildTicks: function() {
			var me = this;
			var min = me.min;
			var max = me.max;
			var options = me.options;
			var timeOpts = options.time;
			var formats = timeOpts.displayFormats;
			var capacity = me.getLabelCapacity(min);
			var unit = timeOpts.unit || determineUnit(timeOpts.minUnit, min, max, capacity);
			var majorUnit = determineMajorUnit(unit);
			var timestamps = [];
			var ticks = [];
			var i, ilen, timestamp;

			switch (options.ticks.source) {
			case 'data':
				timestamps = me._timestamps.data;
				break;
			case 'labels':
				timestamps = me._timestamps.labels;
				break;
			case 'auto':
			default:
				timestamps = generate(min, max, unit, majorUnit, capacity, options);
			}

			if (options.bounds === 'ticks' && timestamps.length) {
				min = timestamps[0];
				max = timestamps[timestamps.length - 1];
			}

			// Enforce limits with user min/max options
			min = parse(timeOpts.min, me) || min;
			max = parse(timeOpts.max, me) || max;

			// Remove ticks outside the min/max range
			for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
				timestamp = timestamps[i];
				if (timestamp >= min && timestamp <= max) {
					ticks.push(timestamp);
				}
			}

			me.min = min;
			me.max = max;

			// PRIVATE
			me._unit = unit;
			me._majorUnit = majorUnit;
			me._minorFormat = formats[unit];
			me._majorFormat = formats[majorUnit];
			me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution);
			me._offsets = computeOffsets(me._table, ticks, min, max, options);

			return ticksFromTimestamps(ticks, majorUnit);
		},

		getLabelForIndex: function(index, datasetIndex) {
			var me = this;
			var data = me.chart.data;
			var timeOpts = me.options.time;
			var label = data.labels && index < data.labels.length ? data.labels[index] : '';
			var value = data.datasets[datasetIndex].data[index];

			if (helpers.isObject(value)) {
				label = me.getRightValue(value);
			}
			if (timeOpts.tooltipFormat) {
				label = momentify(label, timeOpts).format(timeOpts.tooltipFormat);
			}

			return label;
		},

		/**
		 * Function to format an individual tick mark
		 * @private
		 */
		tickFormatFunction: function(tick, index, ticks) {
			var me = this;
			var options = me.options;
			var time = tick.valueOf();
			var majorUnit = me._majorUnit;
			var majorFormat = me._majorFormat;
			var majorTime = tick.clone().startOf(me._majorUnit).valueOf();
			var majorTickOpts = options.ticks.major;
			var major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime;
			var label = tick.format(major ? majorFormat : me._minorFormat);
			var tickOpts = major ? majorTickOpts : options.ticks.minor;
			var formatter = helpers.valueOrDefault(tickOpts.callback, tickOpts.userCallback);

			return formatter ? formatter(label, index, ticks) : label;
		},

		convertTicksToLabels: function(ticks) {
			var labels = [];
			var i, ilen;

			for (i = 0, ilen = ticks.length; i < ilen; ++i) {
				labels.push(this.tickFormatFunction(moment(ticks[i].value), i, ticks));
			}

			return labels;
		},

		/**
		 * @private
		 */
		getPixelForOffset: function(time) {
			var me = this;
			var size = me._horizontal ? me.width : me.height;
			var start = me._horizontal ? me.left : me.top;
			var pos = interpolate(me._table, 'time', time, 'pos');

			return start + size * (me._offsets.left + pos) / (me._offsets.left + 1 + me._offsets.right);
		},

		getPixelForValue: function(value, index, datasetIndex) {
			var me = this;
			var time = null;

			if (index !== undefined && datasetIndex !== undefined) {
				time = me._timestamps.datasets[datasetIndex][index];
			}

			if (time === null) {
				time = parse(value, me);
			}

			if (time !== null) {
				return me.getPixelForOffset(time);
			}
		},

		getPixelForTick: function(index) {
			var ticks = this.getTicks();
			return index >= 0 && index < ticks.length ?
				this.getPixelForOffset(ticks[index].value) :
				null;
		},

		getValueForPixel: function(pixel) {
			var me = this;
			var size = me._horizontal ? me.width : me.height;
			var start = me._horizontal ? me.left : me.top;
			var pos = (size ? (pixel - start) / size : 0) * (me._offsets.left + 1 + me._offsets.left) - me._offsets.right;
			var time = interpolate(me._table, 'pos', pos, 'time');

			return moment(time);
		},

		/**
		 * Crude approximation of what the label width might be
		 * @private
		 */
		getLabelWidth: function(label) {
			var me = this;
			var ticksOpts = me.options.ticks;
			var tickLabelWidth = me.ctx.measureText(label).width;
			var angle = helpers.toRadians(ticksOpts.maxRotation);
			var cosRotation = Math.cos(angle);
			var sinRotation = Math.sin(angle);
			var tickFontSize = helpers.valueOrDefault(ticksOpts.fontSize, defaults.global.defaultFontSize);

			return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation);
		},

		/**
		 * @private
		 */
		getLabelCapacity: function(exampleTime) {
			var me = this;

			me._minorFormat = me.options.time.displayFormats.millisecond;	// Pick the longest format for guestimation

			var exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, []);
			var tickLabelWidth = me.getLabelWidth(exampleLabel);
			var innerWidth = me.isHorizontal() ? me.width : me.height;

			return Math.floor(innerWidth / tickLabelWidth);
		}
	});

	Chart.scaleService.registerScaleType('time', TimeScale, defaultConfig);
};

},{"25":25,"45":45,"6":6}]},{},[7])(7)
});;/*!
 * Chart.js
 * http://chartjs.org/
 * Version: 2.7.0
 *
 * Copyright 2017 Nick Downie
 * Released under the MIT license
 * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md
 */
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){

},{}],2:[function(require,module,exports){
/* MIT license */
var colorNames = require(6);

module.exports = {
   getRgba: getRgba,
   getHsla: getHsla,
   getRgb: getRgb,
   getHsl: getHsl,
   getHwb: getHwb,
   getAlpha: getAlpha,

   hexString: hexString,
   rgbString: rgbString,
   rgbaString: rgbaString,
   percentString: percentString,
   percentaString: percentaString,
   hslString: hslString,
   hslaString: hslaString,
   hwbString: hwbString,
   keyword: keyword
}

function getRgba(string) {
   if (!string) {
      return;
   }
   var abbr =  /^#([a-fA-F0-9]{3})$/i,
       hex =  /^#([a-fA-F0-9]{6})$/i,
       rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i,
       per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i,
       keyword = /(\w+)/;

   var rgb = [0, 0, 0],
       a = 1,
       match = string.match(abbr);
   if (match) {
      match = match[1];
      for (var i = 0; i < rgb.length; i++) {
         rgb[i] = parseInt(match[i] + match[i], 16);
      }
   }
   else if (match = string.match(hex)) {
      match = match[1];
      for (var i = 0; i < rgb.length; i++) {
         rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16);
      }
   }
   else if (match = string.match(rgba)) {
      for (var i = 0; i < rgb.length; i++) {
         rgb[i] = parseInt(match[i + 1]);
      }
      a = parseFloat(match[4]);
   }
   else if (match = string.match(per)) {
      for (var i = 0; i < rgb.length; i++) {
         rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
      }
      a = parseFloat(match[4]);
   }
   else if (match = string.match(keyword)) {
      if (match[1] == "transparent") {
         return [0, 0, 0, 0];
      }
      rgb = colorNames[match[1]];
      if (!rgb) {
         return;
      }
   }

   for (var i = 0; i < rgb.length; i++) {
      rgb[i] = scale(rgb[i], 0, 255);
   }
   if (!a && a != 0) {
      a = 1;
   }
   else {
      a = scale(a, 0, 1);
   }
   rgb[3] = a;
   return rgb;
}

function getHsla(string) {
   if (!string) {
      return;
   }
   var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
   var match = string.match(hsl);
   if (match) {
      var alpha = parseFloat(match[4]);
      var h = scale(parseInt(match[1]), 0, 360),
          s = scale(parseFloat(match[2]), 0, 100),
          l = scale(parseFloat(match[3]), 0, 100),
          a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
      return [h, s, l, a];
   }
}

function getHwb(string) {
   if (!string) {
      return;
   }
   var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
   var match = string.match(hwb);
   if (match) {
    var alpha = parseFloat(match[4]);
      var h = scale(parseInt(match[1]), 0, 360),
          w = scale(parseFloat(match[2]), 0, 100),
          b = scale(parseFloat(match[3]), 0, 100),
          a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
      return [h, w, b, a];
   }
}

function getRgb(string) {
   var rgba = getRgba(string);
   return rgba && rgba.slice(0, 3);
}

function getHsl(string) {
  var hsla = getHsla(string);
  return hsla && hsla.slice(0, 3);
}

function getAlpha(string) {
   var vals = getRgba(string);
   if (vals) {
      return vals[3];
   }
   else if (vals = getHsla(string)) {
      return vals[3];
   }
   else if (vals = getHwb(string)) {
      return vals[3];
   }
}

// generators
function hexString(rgb) {
   return "#" + hexDouble(rgb[0]) + hexDouble(rgb[1])
              + hexDouble(rgb[2]);
}

function rgbString(rgba, alpha) {
   if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
      return rgbaString(rgba, alpha);
   }
   return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")";
}

function rgbaString(rgba, alpha) {
   if (alpha === undefined) {
      alpha = (rgba[3] !== undefined ? rgba[3] : 1);
   }
   return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2]
           + ", " + alpha + ")";
}

function percentString(rgba, alpha) {
   if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
      return percentaString(rgba, alpha);
   }
   var r = Math.round(rgba[0]/255 * 100),
       g = Math.round(rgba[1]/255 * 100),
       b = Math.round(rgba[2]/255 * 100);

   return "rgb(" + r + "%, " + g + "%, " + b + "%)";
}

function percentaString(rgba, alpha) {
   var r = Math.round(rgba[0]/255 * 100),
       g = Math.round(rgba[1]/255 * 100),
       b = Math.round(rgba[2]/255 * 100);
   return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")";
}

function hslString(hsla, alpha) {
   if (alpha < 1 || (hsla[3] && hsla[3] < 1)) {
      return hslaString(hsla, alpha);
   }
   return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)";
}

function hslaString(hsla, alpha) {
   if (alpha === undefined) {
      alpha = (hsla[3] !== undefined ? hsla[3] : 1);
   }
   return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, "
           + alpha + ")";
}

// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax
// (hwb have alpha optional & 1 is default value)
function hwbString(hwb, alpha) {
   if (alpha === undefined) {
      alpha = (hwb[3] !== undefined ? hwb[3] : 1);
   }
   return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%"
           + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")";
}

function keyword(rgb) {
  return reverseNames[rgb.slice(0, 3)];
}

// helpers
function scale(num, min, max) {
   return Math.min(Math.max(min, num), max);
}

function hexDouble(num) {
  var str = num.toString(16).toUpperCase();
  return (str.length < 2) ? "0" + str : str;
}


//create a list of reverse color names
var reverseNames = {};
for (var name in colorNames) {
   reverseNames[colorNames[name]] = name;
}

},{"6":6}],3:[function(require,module,exports){
/* MIT license */
var convert = require(5);
var string = require(2);

var Color = function (obj) {
	if (obj instanceof Color) {
		return obj;
	}
	if (!(this instanceof Color)) {
		return new Color(obj);
	}

	this.valid = false;
	this.values = {
		rgb: [0, 0, 0],
		hsl: [0, 0, 0],
		hsv: [0, 0, 0],
		hwb: [0, 0, 0],
		cmyk: [0, 0, 0, 0],
		alpha: 1
	};

	// parse Color() argument
	var vals;
	if (typeof obj === 'string') {
		vals = string.getRgba(obj);
		if (vals) {
			this.setValues('rgb', vals);
		} else if (vals = string.getHsla(obj)) {
			this.setValues('hsl', vals);
		} else if (vals = string.getHwb(obj)) {
			this.setValues('hwb', vals);
		}
	} else if (typeof obj === 'object') {
		vals = obj;
		if (vals.r !== undefined || vals.red !== undefined) {
			this.setValues('rgb', vals);
		} else if (vals.l !== undefined || vals.lightness !== undefined) {
			this.setValues('hsl', vals);
		} else if (vals.v !== undefined || vals.value !== undefined) {
			this.setValues('hsv', vals);
		} else if (vals.w !== undefined || vals.whiteness !== undefined) {
			this.setValues('hwb', vals);
		} else if (vals.c !== undefined || vals.cyan !== undefined) {
			this.setValues('cmyk', vals);
		}
	}
};

Color.prototype = {
	isValid: function () {
		return this.valid;
	},
	rgb: function () {
		return this.setSpace('rgb', arguments);
	},
	hsl: function () {
		return this.setSpace('hsl', arguments);
	},
	hsv: function () {
		return this.setSpace('hsv', arguments);
	},
	hwb: function () {
		return this.setSpace('hwb', arguments);
	},
	cmyk: function () {
		return this.setSpace('cmyk', arguments);
	},

	rgbArray: function () {
		return this.values.rgb;
	},
	hslArray: function () {
		return this.values.hsl;
	},
	hsvArray: function () {
		return this.values.hsv;
	},
	hwbArray: function () {
		var values = this.values;
		if (values.alpha !== 1) {
			return values.hwb.concat([values.alpha]);
		}
		return values.hwb;
	},
	cmykArray: function () {
		return this.values.cmyk;
	},
	rgbaArray: function () {
		var values = this.values;
		return values.rgb.concat([values.alpha]);
	},
	hslaArray: function () {
		var values = this.values;
		return values.hsl.concat([values.alpha]);
	},
	alpha: function (val) {
		if (val === undefined) {
			return this.values.alpha;
		}
		this.setValues('alpha', val);
		return this;
	},

	red: function (val) {
		return this.setChannel('rgb', 0, val);
	},
	green: function (val) {
		return this.setChannel('rgb', 1, val);
	},
	blue: function (val) {
		return this.setChannel('rgb', 2, val);
	},
	hue: function (val) {
		if (val) {
			val %= 360;
			val = val < 0 ? 360 + val : val;
		}
		return this.setChannel('hsl', 0, val);
	},
	saturation: function (val) {
		return this.setChannel('hsl', 1, val);
	},
	lightness: function (val) {
		return this.setChannel('hsl', 2, val);
	},
	saturationv: function (val) {
		return this.setChannel('hsv', 1, val);
	},
	whiteness: function (val) {
		return this.setChannel('hwb', 1, val);
	},
	blackness: function (val) {
		return this.setChannel('hwb', 2, val);
	},
	value: function (val) {
		return this.setChannel('hsv', 2, val);
	},
	cyan: function (val) {
		return this.setChannel('cmyk', 0, val);
	},
	magenta: function (val) {
		return this.setChannel('cmyk', 1, val);
	},
	yellow: function (val) {
		return this.setChannel('cmyk', 2, val);
	},
	black: function (val) {
		return this.setChannel('cmyk', 3, val);
	},

	hexString: function () {
		return string.hexString(this.values.rgb);
	},
	rgbString: function () {
		return string.rgbString(this.values.rgb, this.values.alpha);
	},
	rgbaString: function () {
		return string.rgbaString(this.values.rgb, this.values.alpha);
	},
	percentString: function () {
		return string.percentString(this.values.rgb, this.values.alpha);
	},
	hslString: function () {
		return string.hslString(this.values.hsl, this.values.alpha);
	},
	hslaString: function () {
		return string.hslaString(this.values.hsl, this.values.alpha);
	},
	hwbString: function () {
		return string.hwbString(this.values.hwb, this.values.alpha);
	},
	keyword: function () {
		return string.keyword(this.values.rgb, this.values.alpha);
	},

	rgbNumber: function () {
		var rgb = this.values.rgb;
		return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
	},

	luminosity: function () {
		// http://www.w3.org/TR/WCAG20/#relativeluminancedef
		var rgb = this.values.rgb;
		var lum = [];
		for (var i = 0; i < rgb.length; i++) {
			var chan = rgb[i] / 255;
			lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);
		}
		return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
	},

	contrast: function (color2) {
		// http://www.w3.org/TR/WCAG20/#contrast-ratiodef
		var lum1 = this.luminosity();
		var lum2 = color2.luminosity();
		if (lum1 > lum2) {
			return (lum1 + 0.05) / (lum2 + 0.05);
		}
		return (lum2 + 0.05) / (lum1 + 0.05);
	},

	level: function (color2) {
		var contrastRatio = this.contrast(color2);
		if (contrastRatio >= 7.1) {
			return 'AAA';
		}

		return (contrastRatio >= 4.5) ? 'AA' : '';
	},

	dark: function () {
		// YIQ equation from http://24ways.org/2010/calculating-color-contrast
		var rgb = this.values.rgb;
		var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
		return yiq < 128;
	},

	light: function () {
		return !this.dark();
	},

	negate: function () {
		var rgb = [];
		for (var i = 0; i < 3; i++) {
			rgb[i] = 255 - this.values.rgb[i];
		}
		this.setValues('rgb', rgb);
		return this;
	},

	lighten: function (ratio) {
		var hsl = this.values.hsl;
		hsl[2] += hsl[2] * ratio;
		this.setValues('hsl', hsl);
		return this;
	},

	darken: function (ratio) {
		var hsl = this.values.hsl;
		hsl[2] -= hsl[2] * ratio;
		this.setValues('hsl', hsl);
		return this;
	},

	saturate: function (ratio) {
		var hsl = this.values.hsl;
		hsl[1] += hsl[1] * ratio;
		this.setValues('hsl', hsl);
		return this;
	},

	desaturate: function (ratio) {
		var hsl = this.values.hsl;
		hsl[1] -= hsl[1] * ratio;
		this.setValues('hsl', hsl);
		return this;
	},

	whiten: function (ratio) {
		var hwb = this.values.hwb;
		hwb[1] += hwb[1] * ratio;
		this.setValues('hwb', hwb);
		return this;
	},

	blacken: function (ratio) {
		var hwb = this.values.hwb;
		hwb[2] += hwb[2] * ratio;
		this.setValues('hwb', hwb);
		return this;
	},

	greyscale: function () {
		var rgb = this.values.rgb;
		// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
		var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
		this.setValues('rgb', [val, val, val]);
		return this;
	},

	clearer: function (ratio) {
		var alpha = this.values.alpha;
		this.setValues('alpha', alpha - (alpha * ratio));
		return this;
	},

	opaquer: function (ratio) {
		var alpha = this.values.alpha;
		this.setValues('alpha', alpha + (alpha * ratio));
		return this;
	},

	rotate: function (degrees) {
		var hsl = this.values.hsl;
		var hue = (hsl[0] + degrees) % 360;
		hsl[0] = hue < 0 ? 360 + hue : hue;
		this.setValues('hsl', hsl);
		return this;
	},

	/**
	 * Ported from sass implementation in C
	 * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209
	 */
	mix: function (mixinColor, weight) {
		var color1 = this;
		var color2 = mixinColor;
		var p = weight === undefined ? 0.5 : weight;

		var w = 2 * p - 1;
		var a = color1.alpha() - color2.alpha();

		var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
		var w2 = 1 - w1;

		return this
			.rgb(
				w1 * color1.red() + w2 * color2.red(),
				w1 * color1.green() + w2 * color2.green(),
				w1 * color1.blue() + w2 * color2.blue()
			)
			.alpha(color1.alpha() * p + color2.alpha() * (1 - p));
	},

	toJSON: function () {
		return this.rgb();
	},

	clone: function () {
		// NOTE(SB): using node-clone creates a dependency to Buffer when using browserify,
		// making the final build way to big to embed in Chart.js. So let's do it manually,
		// assuming that values to clone are 1 dimension arrays containing only numbers,
		// except 'alpha' which is a number.
		var result = new Color();
		var source = this.values;
		var target = result.values;
		var value, type;

		for (var prop in source) {
			if (source.hasOwnProperty(prop)) {
				value = source[prop];
				type = ({}).toString.call(value);
				if (type === '[object Array]') {
					target[prop] = value.slice(0);
				} else if (type === '[object Number]') {
					target[prop] = value;
				} else {
					console.error('unexpected color value:', value);
				}
			}
		}

		return result;
	}
};

Color.prototype.spaces = {
	rgb: ['red', 'green', 'blue'],
	hsl: ['hue', 'saturation', 'lightness'],
	hsv: ['hue', 'saturation', 'value'],
	hwb: ['hue', 'whiteness', 'blackness'],
	cmyk: ['cyan', 'magenta', 'yellow', 'black']
};

Color.prototype.maxes = {
	rgb: [255, 255, 255],
	hsl: [360, 100, 100],
	hsv: [360, 100, 100],
	hwb: [360, 100, 100],
	cmyk: [100, 100, 100, 100]
};

Color.prototype.getValues = function (space) {
	var values = this.values;
	var vals = {};

	for (var i = 0; i < space.length; i++) {
		vals[space.charAt(i)] = values[space][i];
	}

	if (values.alpha !== 1) {
		vals.a = values.alpha;
	}

	// {r: 255, g: 255, b: 255, a: 0.4}
	return vals;
};

Color.prototype.setValues = function (space, vals) {
	var values = this.values;
	var spaces = this.spaces;
	var maxes = this.maxes;
	var alpha = 1;
	var i;

	this.valid = true;

	if (space === 'alpha') {
		alpha = vals;
	} else if (vals.length) {
		// [10, 10, 10]
		values[space] = vals.slice(0, space.length);
		alpha = vals[space.length];
	} else if (vals[space.charAt(0)] !== undefined) {
		// {r: 10, g: 10, b: 10}
		for (i = 0; i < space.length; i++) {
			values[space][i] = vals[space.charAt(i)];
		}

		alpha = vals.a;
	} else if (vals[spaces[space][0]] !== undefined) {
		// {red: 10, green: 10, blue: 10}
		var chans = spaces[space];

		for (i = 0; i < space.length; i++) {
			values[space][i] = vals[chans[i]];
		}

		alpha = vals.alpha;
	}

	values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha)));

	if (space === 'alpha') {
		return false;
	}

	var capped;

	// cap values of the space prior converting all values
	for (i = 0; i < space.length; i++) {
		capped = Math.max(0, Math.min(maxes[space][i], values[space][i]));
		values[space][i] = Math.round(capped);
	}

	// convert to all the other color spaces
	for (var sname in spaces) {
		if (sname !== space) {
			values[sname] = convert[space][sname](values[space]);
		}
	}

	return true;
};

Color.prototype.setSpace = function (space, args) {
	var vals = args[0];

	if (vals === undefined) {
		// color.rgb()
		return this.getValues(space);
	}

	// color.rgb(10, 10, 10)
	if (typeof vals === 'number') {
		vals = Array.prototype.slice.call(args);
	}

	this.setValues(space, vals);
	return this;
};

Color.prototype.setChannel = function (space, index, val) {
	var svalues = this.values[space];
	if (val === undefined) {
		// color.red()
		return svalues[index];
	} else if (val === svalues[index]) {
		// color.red(color.red())
		return this;
	}

	// color.red(100)
	svalues[index] = val;
	this.setValues(space, svalues);

	return this;
};

if (typeof window !== 'undefined') {
	window.Color = Color;
}

module.exports = Color;

},{"2":2,"5":5}],4:[function(require,module,exports){
/* MIT license */

module.exports = {
  rgb2hsl: rgb2hsl,
  rgb2hsv: rgb2hsv,
  rgb2hwb: rgb2hwb,
  rgb2cmyk: rgb2cmyk,
  rgb2keyword: rgb2keyword,
  rgb2xyz: rgb2xyz,
  rgb2lab: rgb2lab,
  rgb2lch: rgb2lch,

  hsl2rgb: hsl2rgb,
  hsl2hsv: hsl2hsv,
  hsl2hwb: hsl2hwb,
  hsl2cmyk: hsl2cmyk,
  hsl2keyword: hsl2keyword,

  hsv2rgb: hsv2rgb,
  hsv2hsl: hsv2hsl,
  hsv2hwb: hsv2hwb,
  hsv2cmyk: hsv2cmyk,
  hsv2keyword: hsv2keyword,

  hwb2rgb: hwb2rgb,
  hwb2hsl: hwb2hsl,
  hwb2hsv: hwb2hsv,
  hwb2cmyk: hwb2cmyk,
  hwb2keyword: hwb2keyword,

  cmyk2rgb: cmyk2rgb,
  cmyk2hsl: cmyk2hsl,
  cmyk2hsv: cmyk2hsv,
  cmyk2hwb: cmyk2hwb,
  cmyk2keyword: cmyk2keyword,

  keyword2rgb: keyword2rgb,
  keyword2hsl: keyword2hsl,
  keyword2hsv: keyword2hsv,
  keyword2hwb: keyword2hwb,
  keyword2cmyk: keyword2cmyk,
  keyword2lab: keyword2lab,
  keyword2xyz: keyword2xyz,

  xyz2rgb: xyz2rgb,
  xyz2lab: xyz2lab,
  xyz2lch: xyz2lch,

  lab2xyz: lab2xyz,
  lab2rgb: lab2rgb,
  lab2lch: lab2lch,

  lch2lab: lch2lab,
  lch2xyz: lch2xyz,
  lch2rgb: lch2rgb
}


function rgb2hsl(rgb) {
  var r = rgb[0]/255,
      g = rgb[1]/255,
      b = rgb[2]/255,
      min = Math.min(r, g, b),
      max = Math.max(r, g, b),
      delta = max - min,
      h, s, l;

  if (max == min)
    h = 0;
  else if (r == max)
    h = (g - b) / delta;
  else if (g == max)
    h = 2 + (b - r) / delta;
  else if (b == max)
    h = 4 + (r - g)/ delta;

  h = Math.min(h * 60, 360);

  if (h < 0)
    h += 360;

  l = (min + max) / 2;

  if (max == min)
    s = 0;
  else if (l <= 0.5)
    s = delta / (max + min);
  else
    s = delta / (2 - max - min);

  return [h, s * 100, l * 100];
}

function rgb2hsv(rgb) {
  var r = rgb[0],
      g = rgb[1],
      b = rgb[2],
      min = Math.min(r, g, b),
      max = Math.max(r, g, b),
      delta = max - min,
      h, s, v;

  if (max == 0)
    s = 0;
  else
    s = (delta/max * 1000)/10;

  if (max == min)
    h = 0;
  else if (r == max)
    h = (g - b) / delta;
  else if (g == max)
    h = 2 + (b - r) / delta;
  else if (b == max)
    h = 4 + (r - g) / delta;

  h = Math.min(h * 60, 360);

  if (h < 0)
    h += 360;

  v = ((max / 255) * 1000) / 10;

  return [h, s, v];
}

function rgb2hwb(rgb) {
  var r = rgb[0],
      g = rgb[1],
      b = rgb[2],
      h = rgb2hsl(rgb)[0],
      w = 1/255 * Math.min(r, Math.min(g, b)),
      b = 1 - 1/255 * Math.max(r, Math.max(g, b));

  return [h, w * 100, b * 100];
}

function rgb2cmyk(rgb) {
  var r = rgb[0] / 255,
      g = rgb[1] / 255,
      b = rgb[2] / 255,
      c, m, y, k;

  k = Math.min(1 - r, 1 - g, 1 - b);
  c = (1 - r - k) / (1 - k) || 0;
  m = (1 - g - k) / (1 - k) || 0;
  y = (1 - b - k) / (1 - k) || 0;
  return [c * 100, m * 100, y * 100, k * 100];
}

function rgb2keyword(rgb) {
  return reverseKeywords[JSON.stringify(rgb)];
}

function rgb2xyz(rgb) {
  var r = rgb[0] / 255,
      g = rgb[1] / 255,
      b = rgb[2] / 255;

  // assume sRGB
  r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);
  g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);
  b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);

  var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
  var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
  var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);

  return [x * 100, y *100, z * 100];
}

function rgb2lab(rgb) {
  var xyz = rgb2xyz(rgb),
        x = xyz[0],
        y = xyz[1],
        z = xyz[2],
        l, a, b;

  x /= 95.047;
  y /= 100;
  z /= 108.883;

  x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
  y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
  z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);

  l = (116 * y) - 16;
  a = 500 * (x - y);
  b = 200 * (y - z);

  return [l, a, b];
}

function rgb2lch(args) {
  return lab2lch(rgb2lab(args));
}

function hsl2rgb(hsl) {
  var h = hsl[0] / 360,
      s = hsl[1] / 100,
      l = hsl[2] / 100,
      t1, t2, t3, rgb, val;

  if (s == 0) {
    val = l * 255;
    return [val, val, val];
  }

  if (l < 0.5)
    t2 = l * (1 + s);
  else
    t2 = l + s - l * s;
  t1 = 2 * l - t2;

  rgb = [0, 0, 0];
  for (var i = 0; i < 3; i++) {
    t3 = h + 1 / 3 * - (i - 1);
    t3 < 0 && t3++;
    t3 > 1 && t3--;

    if (6 * t3 < 1)
      val = t1 + (t2 - t1) * 6 * t3;
    else if (2 * t3 < 1)
      val = t2;
    else if (3 * t3 < 2)
      val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
    else
      val = t1;

    rgb[i] = val * 255;
  }

  return rgb;
}

function hsl2hsv(hsl) {
  var h = hsl[0],
      s = hsl[1] / 100,
      l = hsl[2] / 100,
      sv, v;

  if(l === 0) {
      // no need to do calc on black
      // also avoids divide by 0 error
      return [0, 0, 0];
  }

  l *= 2;
  s *= (l <= 1) ? l : 2 - l;
  v = (l + s) / 2;
  sv = (2 * s) / (l + s);
  return [h, sv * 100, v * 100];
}

function hsl2hwb(args) {
  return rgb2hwb(hsl2rgb(args));
}

function hsl2cmyk(args) {
  return rgb2cmyk(hsl2rgb(args));
}

function hsl2keyword(args) {
  return rgb2keyword(hsl2rgb(args));
}


function hsv2rgb(hsv) {
  var h = hsv[0] / 60,
      s = hsv[1] / 100,
      v = hsv[2] / 100,
      hi = Math.floor(h) % 6;

  var f = h - Math.floor(h),
      p = 255 * v * (1 - s),
      q = 255 * v * (1 - (s * f)),
      t = 255 * v * (1 - (s * (1 - f))),
      v = 255 * v;

  switch(hi) {
    case 0:
      return [v, t, p];
    case 1:
      return [q, v, p];
    case 2:
      return [p, v, t];
    case 3:
      return [p, q, v];
    case 4:
      return [t, p, v];
    case 5:
      return [v, p, q];
  }
}

function hsv2hsl(hsv) {
  var h = hsv[0],
      s = hsv[1] / 100,
      v = hsv[2] / 100,
      sl, l;

  l = (2 - s) * v;
  sl = s * v;
  sl /= (l <= 1) ? l : 2 - l;
  sl = sl || 0;
  l /= 2;
  return [h, sl * 100, l * 100];
}

function hsv2hwb(args) {
  return rgb2hwb(hsv2rgb(args))
}

function hsv2cmyk(args) {
  return rgb2cmyk(hsv2rgb(args));
}

function hsv2keyword(args) {
  return rgb2keyword(hsv2rgb(args));
}

// http://dev.w3.org/csswg/css-color/#hwb-to-rgb
function hwb2rgb(hwb) {
  var h = hwb[0] / 360,
      wh = hwb[1] / 100,
      bl = hwb[2] / 100,
      ratio = wh + bl,
      i, v, f, n;

  // wh + bl cant be > 1
  if (ratio > 1) {
    wh /= ratio;
    bl /= ratio;
  }

  i = Math.floor(6 * h);
  v = 1 - bl;
  f = 6 * h - i;
  if ((i & 0x01) != 0) {
    f = 1 - f;
  }
  n = wh + f * (v - wh);  // linear interpolation

  switch (i) {
    default:
    case 6:
    case 0: r = v; g = n; b = wh; break;
    case 1: r = n; g = v; b = wh; break;
    case 2: r = wh; g = v; b = n; break;
    case 3: r = wh; g = n; b = v; break;
    case 4: r = n; g = wh; b = v; break;
    case 5: r = v; g = wh; b = n; break;
  }

  return [r * 255, g * 255, b * 255];
}

function hwb2hsl(args) {
  return rgb2hsl(hwb2rgb(args));
}

function hwb2hsv(args) {
  return rgb2hsv(hwb2rgb(args));
}

function hwb2cmyk(args) {
  return rgb2cmyk(hwb2rgb(args));
}

function hwb2keyword(args) {
  return rgb2keyword(hwb2rgb(args));
}

function cmyk2rgb(cmyk) {
  var c = cmyk[0] / 100,
      m = cmyk[1] / 100,
      y = cmyk[2] / 100,
      k = cmyk[3] / 100,
      r, g, b;

  r = 1 - Math.min(1, c * (1 - k) + k);
  g = 1 - Math.min(1, m * (1 - k) + k);
  b = 1 - Math.min(1, y * (1 - k) + k);
  return [r * 255, g * 255, b * 255];
}

function cmyk2hsl(args) {
  return rgb2hsl(cmyk2rgb(args));
}

function cmyk2hsv(args) {
  return rgb2hsv(cmyk2rgb(args));
}

function cmyk2hwb(args) {
  return rgb2hwb(cmyk2rgb(args));
}

function cmyk2keyword(args) {
  return rgb2keyword(cmyk2rgb(args));
}


function xyz2rgb(xyz) {
  var x = xyz[0] / 100,
      y = xyz[1] / 100,
      z = xyz[2] / 100,
      r, g, b;

  r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
  g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
  b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);

  // assume sRGB
  r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
    : r = (r * 12.92);

  g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
    : g = (g * 12.92);

  b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
    : b = (b * 12.92);

  r = Math.min(Math.max(0, r), 1);
  g = Math.min(Math.max(0, g), 1);
  b = Math.min(Math.max(0, b), 1);

  return [r * 255, g * 255, b * 255];
}

function xyz2lab(xyz) {
  var x = xyz[0],
      y = xyz[1],
      z = xyz[2],
      l, a, b;

  x /= 95.047;
  y /= 100;
  z /= 108.883;

  x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
  y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
  z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);

  l = (116 * y) - 16;
  a = 500 * (x - y);
  b = 200 * (y - z);

  return [l, a, b];
}

function xyz2lch(args) {
  return lab2lch(xyz2lab(args));
}

function lab2xyz(lab) {
  var l = lab[0],
      a = lab[1],
      b = lab[2],
      x, y, z, y2;

  if (l <= 8) {
    y = (l * 100) / 903.3;
    y2 = (7.787 * (y / 100)) + (16 / 116);
  } else {
    y = 100 * Math.pow((l + 16) / 116, 3);
    y2 = Math.pow(y / 100, 1/3);
  }

  x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3);

  z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3);

  return [x, y, z];
}

function lab2lch(lab) {
  var l = lab[0],
      a = lab[1],
      b = lab[2],
      hr, h, c;

  hr = Math.atan2(b, a);
  h = hr * 360 / 2 / Math.PI;
  if (h < 0) {
    h += 360;
  }
  c = Math.sqrt(a * a + b * b);
  return [l, c, h];
}

function lab2rgb(args) {
  return xyz2rgb(lab2xyz(args));
}

function lch2lab(lch) {
  var l = lch[0],
      c = lch[1],
      h = lch[2],
      a, b, hr;

  hr = h / 360 * 2 * Math.PI;
  a = c * Math.cos(hr);
  b = c * Math.sin(hr);
  return [l, a, b];
}

function lch2xyz(args) {
  return lab2xyz(lch2lab(args));
}

function lch2rgb(args) {
  return lab2rgb(lch2lab(args));
}

function keyword2rgb(keyword) {
  return cssKeywords[keyword];
}

function keyword2hsl(args) {
  return rgb2hsl(keyword2rgb(args));
}

function keyword2hsv(args) {
  return rgb2hsv(keyword2rgb(args));
}

function keyword2hwb(args) {
  return rgb2hwb(keyword2rgb(args));
}

function keyword2cmyk(args) {
  return rgb2cmyk(keyword2rgb(args));
}

function keyword2lab(args) {
  return rgb2lab(keyword2rgb(args));
}

function keyword2xyz(args) {
  return rgb2xyz(keyword2rgb(args));
}

var cssKeywords = {
  aliceblue:  [240,248,255],
  antiquewhite: [250,235,215],
  aqua: [0,255,255],
  aquamarine: [127,255,212],
  azure:  [240,255,255],
  beige:  [245,245,220],
  bisque: [255,228,196],
  black:  [0,0,0],
  blanchedalmond: [255,235,205],
  blue: [0,0,255],
  blueviolet: [138,43,226],
  brown:  [165,42,42],
  burlywood:  [222,184,135],
  cadetblue:  [95,158,160],
  chartreuse: [127,255,0],
  chocolate:  [210,105,30],
  coral:  [255,127,80],
  cornflowerblue: [100,149,237],
  cornsilk: [255,248,220],
  crimson:  [220,20,60],
  cyan: [0,255,255],
  darkblue: [0,0,139],
  darkcyan: [0,139,139],
  darkgoldenrod:  [184,134,11],
  darkgray: [169,169,169],
  darkgreen:  [0,100,0],
  darkgrey: [169,169,169],
  darkkhaki:  [189,183,107],
  darkmagenta:  [139,0,139],
  darkolivegreen: [85,107,47],
  darkorange: [255,140,0],
  darkorchid: [153,50,204],
  darkred:  [139,0,0],
  darksalmon: [233,150,122],
  darkseagreen: [143,188,143],
  darkslateblue:  [72,61,139],
  darkslategray:  [47,79,79],
  darkslategrey:  [47,79,79],
  darkturquoise:  [0,206,209],
  darkviolet: [148,0,211],
  deeppink: [255,20,147],
  deepskyblue:  [0,191,255],
  dimgray:  [105,105,105],
  dimgrey:  [105,105,105],
  dodgerblue: [30,144,255],
  firebrick:  [178,34,34],
  floralwhite:  [255,250,240],
  forestgreen:  [34,139,34],
  fuchsia:  [255,0,255],
  gainsboro:  [220,220,220],
  ghostwhite: [248,248,255],
  gold: [255,215,0],
  goldenrod:  [218,165,32],
  gray: [128,128,128],
  green:  [0,128,0],
  greenyellow:  [173,255,47],
  grey: [128,128,128],
  honeydew: [240,255,240],
  hotpink:  [255,105,180],
  indianred:  [205,92,92],
  indigo: [75,0,130],
  ivory:  [255,255,240],
  khaki:  [240,230,140],
  lavender: [230,230,250],
  lavenderblush:  [255,240,245],
  lawngreen:  [124,252,0],
  lemonchiffon: [255,250,205],
  lightblue:  [173,216,230],
  lightcoral: [240,128,128],
  lightcyan:  [224,255,255],
  lightgoldenrodyellow: [250,250,210],
  lightgray:  [211,211,211],
  lightgreen: [144,238,144],
  lightgrey:  [211,211,211],
  lightpink:  [255,182,193],
  lightsalmon:  [255,160,122],
  lightseagreen:  [32,178,170],
  lightskyblue: [135,206,250],
  lightslategray: [119,136,153],
  lightslategrey: [119,136,153],
  lightsteelblue: [176,196,222],
  lightyellow:  [255,255,224],
  lime: [0,255,0],
  limegreen:  [50,205,50],
  linen:  [250,240,230],
  magenta:  [255,0,255],
  maroon: [128,0,0],
  mediumaquamarine: [102,205,170],
  mediumblue: [0,0,205],
  mediumorchid: [186,85,211],
  mediumpurple: [147,112,219],
  mediumseagreen: [60,179,113],
  mediumslateblue:  [123,104,238],
  mediumspringgreen:  [0,250,154],
  mediumturquoise:  [72,209,204],
  mediumvioletred:  [199,21,133],
  midnightblue: [25,25,112],
  mintcream:  [245,255,250],
  mistyrose:  [255,228,225],
  moccasin: [255,228,181],
  navajowhite:  [255,222,173],
  navy: [0,0,128],
  oldlace:  [253,245,230],
  olive:  [128,128,0],
  olivedrab:  [107,142,35],
  orange: [255,165,0],
  orangered:  [255,69,0],
  orchid: [218,112,214],
  palegoldenrod:  [238,232,170],
  palegreen:  [152,251,152],
  paleturquoise:  [175,238,238],
  palevioletred:  [219,112,147],
  papayawhip: [255,239,213],
  peachpuff:  [255,218,185],
  peru: [205,133,63],
  pink: [255,192,203],
  plum: [221,160,221],
  powderblue: [176,224,230],
  purple: [128,0,128],
  rebeccapurple: [102, 51, 153],
  red:  [255,0,0],
  rosybrown:  [188,143,143],
  royalblue:  [65,105,225],
  saddlebrown:  [139,69,19],
  salmon: [250,128,114],
  sandybrown: [244,164,96],
  seagreen: [46,139,87],
  seashell: [255,245,238],
  sienna: [160,82,45],
  silver: [192,192,192],
  skyblue:  [135,206,235],
  slateblue:  [106,90,205],
  slategray:  [112,128,144],
  slategrey:  [112,128,144],
  snow: [255,250,250],
  springgreen:  [0,255,127],
  steelblue:  [70,130,180],
  tan:  [210,180,140],
  teal: [0,128,128],
  thistle:  [216,191,216],
  tomato: [255,99,71],
  turquoise:  [64,224,208],
  violet: [238,130,238],
  wheat:  [245,222,179],
  white:  [255,255,255],
  whitesmoke: [245,245,245],
  yellow: [255,255,0],
  yellowgreen:  [154,205,50]
};

var reverseKeywords = {};
for (var key in cssKeywords) {
  reverseKeywords[JSON.stringify(cssKeywords[key])] = key;
}

},{}],5:[function(require,module,exports){
var conversions = require(4);

var convert = function() {
   return new Converter();
}

for (var func in conversions) {
  // export Raw versions
  convert[func + "Raw"] =  (function(func) {
    // accept array or plain args
    return function(arg) {
      if (typeof arg == "number")
        arg = Array.prototype.slice.call(arguments);
      return conversions[func](arg);
    }
  })(func);

  var pair = /(\w+)2(\w+)/.exec(func),
      from = pair[1],
      to = pair[2];

  // export rgb2hsl and ["rgb"]["hsl"]
  convert[from] = convert[from] || {};

  convert[from][to] = convert[func] = (function(func) { 
    return function(arg) {
      if (typeof arg == "number")
        arg = Array.prototype.slice.call(arguments);
      
      var val = conversions[func](arg);
      if (typeof val == "string" || val === undefined)
        return val; // keyword

      for (var i = 0; i < val.length; i++)
        val[i] = Math.round(val[i]);
      return val;
    }
  })(func);
}


/* Converter does lazy conversion and caching */
var Converter = function() {
   this.convs = {};
};

/* Either get the values for a space or
  set the values for a space, depending on args */
Converter.prototype.routeSpace = function(space, args) {
   var values = args[0];
   if (values === undefined) {
      // color.rgb()
      return this.getValues(space);
   }
   // color.rgb(10, 10, 10)
   if (typeof values == "number") {
      values = Array.prototype.slice.call(args);        
   }

   return this.setValues(space, values);
};
  
/* Set the values for a space, invalidating cache */
Converter.prototype.setValues = function(space, values) {
   this.space = space;
   this.convs = {};
   this.convs[space] = values;
   return this;
};

/* Get the values for a space. If there's already
  a conversion for the space, fetch it, otherwise
  compute it */
Converter.prototype.getValues = function(space) {
   var vals = this.convs[space];
   if (!vals) {
      var fspace = this.space,
          from = this.convs[fspace];
      vals = convert[fspace][space](from);

      this.convs[space] = vals;
   }
  return vals;
};

["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) {
   Converter.prototype[space] = function(vals) {
      return this.routeSpace(space, arguments);
   }
});

module.exports = convert;
},{"4":4}],6:[function(require,module,exports){
'use strict'

module.exports = {
	"aliceblue": [240, 248, 255],
	"antiquewhite": [250, 235, 215],
	"aqua": [0, 255, 255],
	"aquamarine": [127, 255, 212],
	"azure": [240, 255, 255],
	"beige": [245, 245, 220],
	"bisque": [255, 228, 196],
	"black": [0, 0, 0],
	"blanchedalmond": [255, 235, 205],
	"blue": [0, 0, 255],
	"blueviolet": [138, 43, 226],
	"brown": [165, 42, 42],
	"burlywood": [222, 184, 135],
	"cadetblue": [95, 158, 160],
	"chartreuse": [127, 255, 0],
	"chocolate": [210, 105, 30],
	"coral": [255, 127, 80],
	"cornflowerblue": [100, 149, 237],
	"cornsilk": [255, 248, 220],
	"crimson": [220, 20, 60],
	"cyan": [0, 255, 255],
	"darkblue": [0, 0, 139],
	"darkcyan": [0, 139, 139],
	"darkgoldenrod": [184, 134, 11],
	"darkgray": [169, 169, 169],
	"darkgreen": [0, 100, 0],
	"darkgrey": [169, 169, 169],
	"darkkhaki": [189, 183, 107],
	"darkmagenta": [139, 0, 139],
	"darkolivegreen": [85, 107, 47],
	"darkorange": [255, 140, 0],
	"darkorchid": [153, 50, 204],
	"darkred": [139, 0, 0],
	"darksalmon": [233, 150, 122],
	"darkseagreen": [143, 188, 143],
	"darkslateblue": [72, 61, 139],
	"darkslategray": [47, 79, 79],
	"darkslategrey": [47, 79, 79],
	"darkturquoise": [0, 206, 209],
	"darkviolet": [148, 0, 211],
	"deeppink": [255, 20, 147],
	"deepskyblue": [0, 191, 255],
	"dimgray": [105, 105, 105],
	"dimgrey": [105, 105, 105],
	"dodgerblue": [30, 144, 255],
	"firebrick": [178, 34, 34],
	"floralwhite": [255, 250, 240],
	"forestgreen": [34, 139, 34],
	"fuchsia": [255, 0, 255],
	"gainsboro": [220, 220, 220],
	"ghostwhite": [248, 248, 255],
	"gold": [255, 215, 0],
	"goldenrod": [218, 165, 32],
	"gray": [128, 128, 128],
	"green": [0, 128, 0],
	"greenyellow": [173, 255, 47],
	"grey": [128, 128, 128],
	"honeydew": [240, 255, 240],
	"hotpink": [255, 105, 180],
	"indianred": [205, 92, 92],
	"indigo": [75, 0, 130],
	"ivory": [255, 255, 240],
	"khaki": [240, 230, 140],
	"lavender": [230, 230, 250],
	"lavenderblush": [255, 240, 245],
	"lawngreen": [124, 252, 0],
	"lemonchiffon": [255, 250, 205],
	"lightblue": [173, 216, 230],
	"lightcoral": [240, 128, 128],
	"lightcyan": [224, 255, 255],
	"lightgoldenrodyellow": [250, 250, 210],
	"lightgray": [211, 211, 211],
	"lightgreen": [144, 238, 144],
	"lightgrey": [211, 211, 211],
	"lightpink": [255, 182, 193],
	"lightsalmon": [255, 160, 122],
	"lightseagreen": [32, 178, 170],
	"lightskyblue": [135, 206, 250],
	"lightslategray": [119, 136, 153],
	"lightslategrey": [119, 136, 153],
	"lightsteelblue": [176, 196, 222],
	"lightyellow": [255, 255, 224],
	"lime": [0, 255, 0],
	"limegreen": [50, 205, 50],
	"linen": [250, 240, 230],
	"magenta": [255, 0, 255],
	"maroon": [128, 0, 0],
	"mediumaquamarine": [102, 205, 170],
	"mediumblue": [0, 0, 205],
	"mediumorchid": [186, 85, 211],
	"mediumpurple": [147, 112, 219],
	"mediumseagreen": [60, 179, 113],
	"mediumslateblue": [123, 104, 238],
	"mediumspringgreen": [0, 250, 154],
	"mediumturquoise": [72, 209, 204],
	"mediumvioletred": [199, 21, 133],
	"midnightblue": [25, 25, 112],
	"mintcream": [245, 255, 250],
	"mistyrose": [255, 228, 225],
	"moccasin": [255, 228, 181],
	"navajowhite": [255, 222, 173],
	"navy": [0, 0, 128],
	"oldlace": [253, 245, 230],
	"olive": [128, 128, 0],
	"olivedrab": [107, 142, 35],
	"orange": [255, 165, 0],
	"orangered": [255, 69, 0],
	"orchid": [218, 112, 214],
	"palegoldenrod": [238, 232, 170],
	"palegreen": [152, 251, 152],
	"paleturquoise": [175, 238, 238],
	"palevioletred": [219, 112, 147],
	"papayawhip": [255, 239, 213],
	"peachpuff": [255, 218, 185],
	"peru": [205, 133, 63],
	"pink": [255, 192, 203],
	"plum": [221, 160, 221],
	"powderblue": [176, 224, 230],
	"purple": [128, 0, 128],
	"rebeccapurple": [102, 51, 153],
	"red": [255, 0, 0],
	"rosybrown": [188, 143, 143],
	"royalblue": [65, 105, 225],
	"saddlebrown": [139, 69, 19],
	"salmon": [250, 128, 114],
	"sandybrown": [244, 164, 96],
	"seagreen": [46, 139, 87],
	"seashell": [255, 245, 238],
	"sienna": [160, 82, 45],
	"silver": [192, 192, 192],
	"skyblue": [135, 206, 235],
	"slateblue": [106, 90, 205],
	"slategray": [112, 128, 144],
	"slategrey": [112, 128, 144],
	"snow": [255, 250, 250],
	"springgreen": [0, 255, 127],
	"steelblue": [70, 130, 180],
	"tan": [210, 180, 140],
	"teal": [0, 128, 128],
	"thistle": [216, 191, 216],
	"tomato": [255, 99, 71],
	"turquoise": [64, 224, 208],
	"violet": [238, 130, 238],
	"wheat": [245, 222, 179],
	"white": [255, 255, 255],
	"whitesmoke": [245, 245, 245],
	"yellow": [255, 255, 0],
	"yellowgreen": [154, 205, 50]
};

},{}],7:[function(require,module,exports){
/**
 * @namespace Chart
 */
var Chart = require(29)();

Chart.helpers = require(45);

// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests!
require(27)(Chart);

Chart.defaults = require(25);
Chart.Element = require(26);
Chart.elements = require(40);
Chart.Interaction = require(28);
Chart.platform = require(48);

require(31)(Chart);
require(22)(Chart);
require(23)(Chart);
require(24)(Chart);
require(30)(Chart);
require(33)(Chart);
require(32)(Chart);
require(35)(Chart);

require(54)(Chart);
require(52)(Chart);
require(53)(Chart);
require(55)(Chart);
require(56)(Chart);
require(57)(Chart);

// Controllers must be loaded after elements
// See Chart.core.datasetController.dataElementType
require(15)(Chart);
require(16)(Chart);
require(17)(Chart);
require(18)(Chart);
require(19)(Chart);
require(20)(Chart);
require(21)(Chart);

require(8)(Chart);
require(9)(Chart);
require(10)(Chart);
require(11)(Chart);
require(12)(Chart);
require(13)(Chart);
require(14)(Chart);

// Loading built-it plugins
var plugins = [];

plugins.push(
	require(49)(Chart),
	require(50)(Chart),
	require(51)(Chart)
);

Chart.plugins.register(plugins);

Chart.platform.initialize();

module.exports = Chart;
if (typeof window !== 'undefined') {
	window.Chart = Chart;
}

// DEPRECATIONS

/**
 * Provided for backward compatibility, use Chart.helpers.canvas instead.
 * @namespace Chart.canvasHelpers
 * @deprecated since version 2.6.0
 * @todo remove at version 3
 * @private
 */
Chart.canvasHelpers = Chart.helpers.canvas;

},{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"35":35,"40":40,"45":45,"48":48,"49":49,"50":50,"51":51,"52":52,"53":53,"54":54,"55":55,"56":56,"57":57,"8":8,"9":9}],8:[function(require,module,exports){
'use strict';

module.exports = function(Chart) {

	Chart.Bar = function(context, config) {
		config.type = 'bar';

		return new Chart(context, config);
	};

};

},{}],9:[function(require,module,exports){
'use strict';

module.exports = function(Chart) {

	Chart.Bubble = function(context, config) {
		config.type = 'bubble';
		return new Chart(context, config);
	};

};

},{}],10:[function(require,module,exports){
'use strict';

module.exports = function(Chart) {

	Chart.Doughnut = function(context, config) {
		config.type = 'doughnut';

		return new Chart(context, config);
	};

};

},{}],11:[function(require,module,exports){
'use strict';

module.exports = function(Chart) {

	Chart.Line = function(context, config) {
		config.type = 'line';

		return new Chart(context, config);
	};

};

},{}],12:[function(require,module,exports){
'use strict';

module.exports = function(Chart) {

	Chart.PolarArea = function(context, config) {
		config.type = 'polarArea';

		return new Chart(context, config);
	};

};

},{}],13:[function(require,module,exports){
'use strict';

module.exports = function(Chart) {

	Chart.Radar = function(context, config) {
		config.type = 'radar';

		return new Chart(context, config);
	};

};

},{}],14:[function(require,module,exports){
'use strict';

module.exports = function(Chart) {
	Chart.Scatter = function(context, config) {
		config.type = 'scatter';
		return new Chart(context, config);
	};
};

},{}],15:[function(require,module,exports){
'use strict';

var defaults = require(25);
var elements = require(40);
var helpers = require(45);

defaults._set('bar', {
	hover: {
		mode: 'label'
	},

	scales: {
		xAxes: [{
			type: 'category',

			// Specific to Bar Controller
			categoryPercentage: 0.8,
			barPercentage: 0.9,

			// offset settings
			offset: true,

			// grid line settings
			gridLines: {
				offsetGridLines: true
			}
		}],

		yAxes: [{
			type: 'linear'
		}]
	}
});

defaults._set('horizontalBar', {
	hover: {
		mode: 'index',
		axis: 'y'
	},

	scales: {
		xAxes: [{
			type: 'linear',
			position: 'bottom'
		}],

		yAxes: [{
			position: 'left',
			type: 'category',

			// Specific to Horizontal Bar Controller
			categoryPercentage: 0.8,
			barPercentage: 0.9,

			// offset settings
			offset: true,

			// grid line settings
			gridLines: {
				offsetGridLines: true
			}
		}]
	},

	elements: {
		rectangle: {
			borderSkipped: 'left'
		}
	},

	tooltips: {
		callbacks: {
			title: function(item, data) {
				// Pick first xLabel for now
				var title = '';

				if (item.length > 0) {
					if (item[0].yLabel) {
						title = item[0].yLabel;
					} else if (data.labels.length > 0 && item[0].index < data.labels.length) {
						title = data.labels[item[0].index];
					}
				}

				return title;
			},

			label: function(item, data) {
				var datasetLabel = data.datasets[item.datasetIndex].label || '';
				return datasetLabel + ': ' + item.xLabel;
			}
		},
		mode: 'index',
		axis: 'y'
	}
});

module.exports = function(Chart) {

	Chart.controllers.bar = Chart.DatasetController.extend({

		dataElementType: elements.Rectangle,

		initialize: function() {
			var me = this;
			var meta;

			Chart.DatasetController.prototype.initialize.apply(me, arguments);

			meta = me.getMeta();
			meta.stack = me.getDataset().stack;
			meta.bar = true;
		},

		update: function(reset) {
			var me = this;
			var rects = me.getMeta().data;
			var i, ilen;

			me._ruler = me.getRuler();

			for (i = 0, ilen = rects.length; i < ilen; ++i) {
				me.updateElement(rects[i], i, reset);
			}
		},

		updateElement: function(rectangle, index, reset) {
			var me = this;
			var chart = me.chart;
			var meta = me.getMeta();
			var dataset = me.getDataset();
			var custom = rectangle.custom || {};
			var rectangleOptions = chart.options.elements.rectangle;

			rectangle._xScale = me.getScaleForId(meta.xAxisID);
			rectangle._yScale = me.getScaleForId(meta.yAxisID);
			rectangle._datasetIndex = me.index;
			rectangle._index = index;

			rectangle._model = {
				datasetLabel: dataset.label,
				label: chart.data.labels[index],
				borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped,
				backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor),
				borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor),
				borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth)
			};

			me.updateElementGeometry(rectangle, index, reset);

			rectangle.pivot();
		},

		/**
		 * @private
		 */
		updateElementGeometry: function(rectangle, index, reset) {
			var me = this;
			var model = rectangle._model;
			var vscale = me.getValueScale();
			var base = vscale.getBasePixel();
			var horizontal = vscale.isHorizontal();
			var ruler = me._ruler || me.getRuler();
			var vpixels = me.calculateBarValuePixels(me.index, index);
			var ipixels = me.calculateBarIndexPixels(me.index, index, ruler);

			model.horizontal = horizontal;
			model.base = reset ? base : vpixels.base;
			model.x = horizontal ? reset ? base : vpixels.head : ipixels.center;
			model.y = horizontal ? ipixels.center : reset ? base : vpixels.head;
			model.height = horizontal ? ipixels.size : undefined;
			model.width = horizontal ? undefined : ipixels.size;
		},

		/**
		 * @private
		 */
		getValueScaleId: function() {
			return this.getMeta().yAxisID;
		},

		/**
		 * @private
		 */
		getIndexScaleId: function() {
			return this.getMeta().xAxisID;
		},

		/**
		 * @private
		 */
		getValueScale: function() {
			return this.getScaleForId(this.getValueScaleId());
		},

		/**
		 * @private
		 */
		getIndexScale: function() {
			return this.getScaleForId(this.getIndexScaleId());
		},

		/**
		 * Returns the effective number of stacks based on groups and bar visibility.
		 * @private
		 */
		getStackCount: function(last) {
			var me = this;
			var chart = me.chart;
			var scale = me.getIndexScale();
			var stacked = scale.options.stacked;
			var ilen = last === undefined ? chart.data.datasets.length : last + 1;
			var stacks = [];
			var i, meta;

			for (i = 0; i < ilen; ++i) {
				meta = chart.getDatasetMeta(i);
				if (meta.bar && chart.isDatasetVisible(i) &&
					(stacked === false ||
					(stacked === true && stacks.indexOf(meta.stack) === -1) ||
					(stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) {
					stacks.push(meta.stack);
				}
			}

			return stacks.length;
		},

		/**
		 * Returns the stack index for the given dataset based on groups and bar visibility.
		 * @private
		 */
		getStackIndex: function(datasetIndex) {
			return this.getStackCount(datasetIndex) - 1;
		},

		/**
		 * @private
		 */
		getRuler: function() {
			var me = this;
			var scale = me.getIndexScale();
			var stackCount = me.getStackCount();
			var datasetIndex = me.index;
			var pixels = [];
			var isHorizontal = scale.isHorizontal();
			var start = isHorizontal ? scale.left : scale.top;
			var end = start + (isHorizontal ? scale.width : scale.height);
			var i, ilen;

			for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) {
				pixels.push(scale.getPixelForValue(null, i, datasetIndex));
			}

			return {
				pixels: pixels,
				start: start,
				end: end,
				stackCount: stackCount,
				scale: scale
			};
		},

		/**
		 * Note: pixel values are not clamped to the scale area.
		 * @private
		 */
		calculateBarValuePixels: function(datasetIndex, index) {
			var me = this;
			var chart = me.chart;
			var meta = me.getMeta();
			var scale = me.getValueScale();
			var datasets = chart.data.datasets;
			var value = scale.getRightValue(datasets[datasetIndex].data[index]);
			var stacked = scale.options.stacked;
			var stack = meta.stack;
			var start = 0;
			var i, imeta, ivalue, base, head, size;

			if (stacked || (stacked === undefined && stack !== undefined)) {
				for (i = 0; i < datasetIndex; ++i) {
					imeta = chart.getDatasetMeta(i);

					if (imeta.bar &&
						imeta.stack === stack &&
						imeta.controller.getValueScaleId() === scale.id &&
						chart.isDatasetVisible(i)) {

						ivalue = scale.getRightValue(datasets[i].data[index]);
						if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) {
							start += ivalue;
						}
					}
				}
			}

			base = scale.getPixelForValue(start);
			head = scale.getPixelForValue(start + value);
			size = (head - base) / 2;

			return {
				size: size,
				base: base,
				head: head,
				center: head + size / 2
			};
		},

		/**
		 * @private
		 */
		calculateBarIndexPixels: function(datasetIndex, index, ruler) {
			var me = this;
			var options = ruler.scale.options;
			var stackIndex = me.getStackIndex(datasetIndex);
			var pixels = ruler.pixels;
			var base = pixels[index];
			var length = pixels.length;
			var start = ruler.start;
			var end = ruler.end;
			var leftSampleSize, rightSampleSize, leftCategorySize, rightCategorySize, fullBarSize, size;

			if (length === 1) {
				leftSampleSize = base > start ? base - start : end - base;
				rightSampleSize = base < end ? end - base : base - start;
			} else {
				if (index > 0) {
					leftSampleSize = (base - pixels[index - 1]) / 2;
					if (index === length - 1) {
						rightSampleSize = leftSampleSize;
					}
				}
				if (index < length - 1) {
					rightSampleSize = (pixels[index + 1] - base) / 2;
					if (index === 0) {
						leftSampleSize = rightSampleSize;
					}
				}
			}

			leftCategorySize = leftSampleSize * options.categoryPercentage;
			rightCategorySize = rightSampleSize * options.categoryPercentage;
			fullBarSize = (leftCategorySize + rightCategorySize) / ruler.stackCount;
			size = fullBarSize * options.barPercentage;

			size = Math.min(
				helpers.valueOrDefault(options.barThickness, size),
				helpers.valueOrDefault(options.maxBarThickness, Infinity));

			base -= leftCategorySize;
			base += fullBarSize * stackIndex;
			base += (fullBarSize - size) / 2;

			return {
				size: size,
				base: base,
				head: base + size,
				center: base + size / 2
			};
		},

		draw: function() {
			var me = this;
			var chart = me.chart;
			var scale = me.getValueScale();
			var rects = me.getMeta().data;
			var dataset = me.getDataset();
			var ilen = rects.length;
			var i = 0;

			helpers.canvas.clipArea(chart.ctx, chart.chartArea);

			for (; i < ilen; ++i) {
				if (!isNaN(scale.getRightValue(dataset.data[i]))) {
					rects[i].draw();
				}
			}

			helpers.canvas.unclipArea(chart.ctx);
		},

		setHoverStyle: function(rectangle) {
			var dataset = this.chart.data.datasets[rectangle._datasetIndex];
			var index = rectangle._index;
			var custom = rectangle.custom || {};
			var model = rectangle._model;

			model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
			model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor));
			model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
		},

		removeHoverStyle: function(rectangle) {
			var dataset = this.chart.data.datasets[rectangle._datasetIndex];
			var index = rectangle._index;
			var custom = rectangle.custom || {};
			var model = rectangle._model;
			var rectangleElementOptions = this.chart.options.elements.rectangle;

			model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor);
			model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor);
			model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth);
		}
	});

	Chart.controllers.horizontalBar = Chart.controllers.bar.extend({
		/**
		 * @private
		 */
		getValueScaleId: function() {
			return this.getMeta().xAxisID;
		},

		/**
		 * @private
		 */
		getIndexScaleId: function() {
			return this.getMeta().yAxisID;
		}
	});
};

},{"25":25,"40":40,"45":45}],16:[function(require,module,exports){
'use strict';

var defaults = require(25);
var elements = require(40);
var helpers = require(45);

defaults._set('bubble', {
	hover: {
		mode: 'single'
	},

	scales: {
		xAxes: [{
			type: 'linear', // bubble should probably use a linear scale by default
			position: 'bottom',
			id: 'x-axis-0' // need an ID so datasets can reference the scale
		}],
		yAxes: [{
			type: 'linear',
			position: 'left',
			id: 'y-axis-0'
		}]
	},

	tooltips: {
		callbacks: {
			title: function() {
				// Title doesn't make sense for scatter since we format the data as a point
				return '';
			},
			label: function(item, data) {
				var datasetLabel = data.datasets[item.datasetIndex].label || '';
				var dataPoint = data.datasets[item.datasetIndex].data[item.index];
				return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')';
			}
		}
	}
});


module.exports = function(Chart) {

	Chart.controllers.bubble = Chart.DatasetController.extend({
		/**
		 * @protected
		 */
		dataElementType: elements.Point,

		/**
		 * @protected
		 */
		update: function(reset) {
			var me = this;
			var meta = me.getMeta();
			var points = meta.data;

			// Update Points
			helpers.each(points, function(point, index) {
				me.updateElement(point, index, reset);
			});
		},

		/**
		 * @protected
		 */
		updateElement: function(point, index, reset) {
			var me = this;
			var meta = me.getMeta();
			var custom = point.custom || {};
			var xScale = me.getScaleForId(meta.xAxisID);
			var yScale = me.getScaleForId(meta.yAxisID);
			var options = me._resolveElementOptions(point, index);
			var data = me.getDataset().data[index];
			var dsIndex = me.index;

			var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex);
			var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex);

			point._xScale = xScale;
			point._yScale = yScale;
			point._options = options;
			point._datasetIndex = dsIndex;
			point._index = index;
			point._model = {
				backgroundColor: options.backgroundColor,
				borderColor: options.borderColor,
				borderWidth: options.borderWidth,
				hitRadius: options.hitRadius,
				pointStyle: options.pointStyle,
				radius: reset ? 0 : options.radius,
				skip: custom.skip || isNaN(x) || isNaN(y),
				x: x,
				y: y,
			};

			point.pivot();
		},

		/**
		 * @protected
		 */
		setHoverStyle: function(point) {
			var model = point._model;
			var options = point._options;

			model.backgroundColor = helpers.valueOrDefault(options.hoverBackgroundColor, helpers.getHoverColor(options.backgroundColor));
			model.borderColor = helpers.valueOrDefault(options.hoverBorderColor, helpers.getHoverColor(options.borderColor));
			model.borderWidth = helpers.valueOrDefault(options.hoverBorderWidth, options.borderWidth);
			model.radius = options.radius + options.hoverRadius;
		},

		/**
		 * @protected
		 */
		removeHoverStyle: function(point) {
			var model = point._model;
			var options = point._options;

			model.backgroundColor = options.backgroundColor;
			model.borderColor = options.borderColor;
			model.borderWidth = options.borderWidth;
			model.radius = options.radius;
		},

		/**
		 * @private
		 */
		_resolveElementOptions: function(point, index) {
			var me = this;
			var chart = me.chart;
			var datasets = chart.data.datasets;
			var dataset = datasets[me.index];
			var custom = point.custom || {};
			var options = chart.options.elements.point;
			var resolve = helpers.options.resolve;
			var data = dataset.data[index];
			var values = {};
			var i, ilen, key;

			// Scriptable options
			var context = {
				chart: chart,
				dataIndex: index,
				dataset: dataset,
				datasetIndex: me.index
			};

			var keys = [
				'backgroundColor',
				'borderColor',
				'borderWidth',
				'hoverBackgroundColor',
				'hoverBorderColor',
				'hoverBorderWidth',
				'hoverRadius',
				'hitRadius',
				'pointStyle'
			];

			for (i = 0, ilen = keys.length; i < ilen; ++i) {
				key = keys[i];
				values[key] = resolve([
					custom[key],
					dataset[key],
					options[key]
				], context, index);
			}

			// Custom radius resolution
			values.radius = resolve([
				custom.radius,
				data ? data.r : undefined,
				dataset.radius,
				options.radius
			], context, index);

			return values;
		}
	});
};

},{"25":25,"40":40,"45":45}],17:[function(require,module,exports){
'use strict';

var defaults = require(25);
var elements = require(40);
var helpers = require(45);

defaults._set('doughnut', {
	animation: {
		// Boolean - Whether we animate the rotation of the Doughnut
		animateRotate: true,
		// Boolean - Whether we animate scaling the Doughnut from the centre
		animateScale: false
	},
	hover: {
		mode: 'single'
	},
	legendCallback: function(chart) {
		var text = [];
		text.push('<ul class="' + chart.id + '-legend">');

		var data = chart.data;
		var datasets = data.datasets;
		var labels = data.labels;

		if (datasets.length) {
			for (var i = 0; i < datasets[0].data.length; ++i) {
				text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
				if (labels[i]) {
					text.push(labels[i]);
				}
				text.push('</li>');
			}
		}

		text.push('</ul>');
		return text.join('');
	},
	legend: {
		labels: {
			generateLabels: function(chart) {
				var data = chart.data;
				if (data.labels.length && data.datasets.length) {
					return data.labels.map(function(label, i) {
						var meta = chart.getDatasetMeta(0);
						var ds = data.datasets[0];
						var arc = meta.data[i];
						var custom = arc && arc.custom || {};
						var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
						var arcOpts = chart.options.elements.arc;
						var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
						var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
						var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);

						return {
							text: label,
							fillStyle: fill,
							strokeStyle: stroke,
							lineWidth: bw,
							hidden: isNaN(ds.data[i]) || meta.data[i].hidden,

							// Extra data used for toggling the correct item
							index: i
						};
					});
				}
				return [];
			}
		},

		onClick: function(e, legendItem) {
			var index = legendItem.index;
			var chart = this.chart;
			var i, ilen, meta;

			for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
				meta = chart.getDatasetMeta(i);
				// toggle visibility of index if exists
				if (meta.data[index]) {
					meta.data[index].hidden = !meta.data[index].hidden;
				}
			}

			chart.update();
		}
	},

	// The percentage of the chart that we cut out of the middle.
	cutoutPercentage: 50,

	// The rotation of the chart, where the first data arc begins.
	rotation: Math.PI * -0.5,

	// The total circumference of the chart.
	circumference: Math.PI * 2.0,

	// Need to override these to give a nice default
	tooltips: {
		callbacks: {
			title: function() {
				return '';
			},
			label: function(tooltipItem, data) {
				var dataLabel = data.labels[tooltipItem.index];
				var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];

				if (helpers.isArray(dataLabel)) {
					// show value on first line of multiline label
					// need to clone because we are changing the value
					dataLabel = dataLabel.slice();
					dataLabel[0] += value;
				} else {
					dataLabel += value;
				}

				return dataLabel;
			}
		}
	}
});

defaults._set('pie', helpers.clone(defaults.doughnut));
defaults._set('pie', {
	cutoutPercentage: 0
});

module.exports = function(Chart) {

	Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({

		dataElementType: elements.Arc,

		linkScales: helpers.noop,

		// Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
		getRingIndex: function(datasetIndex) {
			var ringIndex = 0;

			for (var j = 0; j < datasetIndex; ++j) {
				if (this.chart.isDatasetVisible(j)) {
					++ringIndex;
				}
			}

			return ringIndex;
		},

		update: function(reset) {
			var me = this;
			var chart = me.chart;
			var chartArea = chart.chartArea;
			var opts = chart.options;
			var arcOpts = opts.elements.arc;
			var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth;
			var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth;
			var minSize = Math.min(availableWidth, availableHeight);
			var offset = {x: 0, y: 0};
			var meta = me.getMeta();
			var cutoutPercentage = opts.cutoutPercentage;
			var circumference = opts.circumference;

			// If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
			if (circumference < Math.PI * 2.0) {
				var startAngle = opts.rotation % (Math.PI * 2.0);
				startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
				var endAngle = startAngle + circumference;
				var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)};
				var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)};
				var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);
				var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);
				var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);
				var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);
				var cutout = cutoutPercentage / 100.0;
				var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))};
				var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))};
				var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};
				minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
				offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
			}

			chart.borderWidth = me.getMaxBorderWidth(meta.data);
			chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);
			chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
			chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
			chart.offsetX = offset.x * chart.outerRadius;
			chart.offsetY = offset.y * chart.outerRadius;

			meta.total = me.calculateTotal();

			me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index));
			me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0);

			helpers.each(meta.data, function(arc, index) {
				me.updateElement(arc, index, reset);
			});
		},

		updateElement: function(arc, index, reset) {
			var me = this;
			var chart = me.chart;
			var chartArea = chart.chartArea;
			var opts = chart.options;
			var animationOpts = opts.animation;
			var centerX = (chartArea.left + chartArea.right) / 2;
			var centerY = (chartArea.top + chartArea.bottom) / 2;
			var startAngle = opts.rotation; // non reset case handled later
			var endAngle = opts.rotation; // non reset case handled later
			var dataset = me.getDataset();
			var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI));
			var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
			var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
			var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;

			helpers.extend(arc, {
				// Utility
				_datasetIndex: me.index,
				_index: index,

				// Desired view properties
				_model: {
					x: centerX + chart.offsetX,
					y: centerY + chart.offsetY,
					startAngle: startAngle,
					endAngle: endAngle,
					circumference: circumference,
					outerRadius: outerRadius,
					innerRadius: innerRadius,
					label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
				}
			});

			var model = arc._model;
			// Resets the visual styles
			this.removeHoverStyle(arc);

			// Set correct angles if not resetting
			if (!reset || !animationOpts.animateRotate) {
				if (index === 0) {
					model.startAngle = opts.rotation;
				} else {
					model.startAngle = me.getMeta().data[index - 1]._model.endAngle;
				}

				model.endAngle = model.startAngle + model.circumference;
			}

			arc.pivot();
		},

		removeHoverStyle: function(arc) {
			Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc);
		},

		calculateTotal: function() {
			var dataset = this.getDataset();
			var meta = this.getMeta();
			var total = 0;
			var value;

			helpers.each(meta.data, function(element, index) {
				value = dataset.data[index];
				if (!isNaN(value) && !element.hidden) {
					total += Math.abs(value);
				}
			});

			/* if (total === 0) {
				total = NaN;
			}*/

			return total;
		},

		calculateCircumference: function(value) {
			var total = this.getMeta().total;
			if (total > 0 && !isNaN(value)) {
				return (Math.PI * 2.0) * (value / total);
			}
			return 0;
		},

		// gets the max border or hover width to properly scale pie charts
		getMaxBorderWidth: function(arcs) {
			var max = 0;
			var index = this.index;
			var length = arcs.length;
			var borderWidth;
			var hoverWidth;

			for (var i = 0; i < length; i++) {
				borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0;
				hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0;

				max = borderWidth > max ? borderWidth : max;
				max = hoverWidth > max ? hoverWidth : max;
			}
			return max;
		}
	});
};

},{"25":25,"40":40,"45":45}],18:[function(require,module,exports){
'use strict';

var defaults = require(25);
var elements = require(40);
var helpers = require(45);

defaults._set('line', {
	showLines: true,
	spanGaps: false,

	hover: {
		mode: 'label'
	},

	scales: {
		xAxes: [{
			type: 'category',
			id: 'x-axis-0'
		}],
		yAxes: [{
			type: 'linear',
			id: 'y-axis-0'
		}]
	}
});

module.exports = function(Chart) {

	function lineEnabled(dataset, options) {
		return helpers.valueOrDefault(dataset.showLine, options.showLines);
	}

	Chart.controllers.line = Chart.DatasetController.extend({

		datasetElementType: elements.Line,

		dataElementType: elements.Point,

		update: function(reset) {
			var me = this;
			var meta = me.getMeta();
			var line = meta.dataset;
			var points = meta.data || [];
			var options = me.chart.options;
			var lineElementOptions = options.elements.line;
			var scale = me.getScaleForId(meta.yAxisID);
			var i, ilen, custom;
			var dataset = me.getDataset();
			var showLine = lineEnabled(dataset, options);

			// Update Line
			if (showLine) {
				custom = line.custom || {};

				// Compatibility: If the properties are defined with only the old name, use those values
				if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
					dataset.lineTension = dataset.tension;
				}

				// Utility
				line._scale = scale;
				line._datasetIndex = me.index;
				// Data
				line._children = points;
				// Model
				line._model = {
					// Appearance
					// The default behavior of lines is to break at null values, according
					// to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158
					// This option gives lines the ability to span gaps
					spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps,
					tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension),
					backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
					borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
					borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
					borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
					borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
					borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
					borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
					fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
					steppedLine: custom.steppedLine ? custom.steppedLine : helpers.valueOrDefault(dataset.steppedLine, lineElementOptions.stepped),
					cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.valueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode),
				};

				line.pivot();
			}

			// Update Points
			for (i = 0, ilen = points.length; i < ilen; ++i) {
				me.updateElement(points[i], i, reset);
			}

			if (showLine && line._model.tension !== 0) {
				me.updateBezierControlPoints();
			}

			// Now pivot the point for animation
			for (i = 0, ilen = points.length; i < ilen; ++i) {
				points[i].pivot();
			}
		},

		getPointBackgroundColor: function(point, index) {
			var backgroundColor = this.chart.options.elements.point.backgroundColor;
			var dataset = this.getDataset();
			var custom = point.custom || {};

			if (custom.backgroundColor) {
				backgroundColor = custom.backgroundColor;
			} else if (dataset.pointBackgroundColor) {
				backgroundColor = helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor);
			} else if (dataset.backgroundColor) {
				backgroundColor = dataset.backgroundColor;
			}

			return backgroundColor;
		},

		getPointBorderColor: function(point, index) {
			var borderColor = this.chart.options.elements.point.borderColor;
			var dataset = this.getDataset();
			var custom = point.custom || {};

			if (custom.borderColor) {
				borderColor = custom.borderColor;
			} else if (dataset.pointBorderColor) {
				borderColor = helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor);
			} else if (dataset.borderColor) {
				borderColor = dataset.borderColor;
			}

			return borderColor;
		},

		getPointBorderWidth: function(point, index) {
			var borderWidth = this.chart.options.elements.point.borderWidth;
			var dataset = this.getDataset();
			var custom = point.custom || {};

			if (!isNaN(custom.borderWidth)) {
				borderWidth = custom.borderWidth;
			} else if (!isNaN(dataset.pointBorderWidth) || helpers.isArray(dataset.pointBorderWidth)) {
				borderWidth = helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth);
			} else if (!isNaN(dataset.borderWidth)) {
				borderWidth = dataset.borderWidth;
			}

			return borderWidth;
		},

		updateElement: function(point, index, reset) {
			var me = this;
			var meta = me.getMeta();
			var custom = point.custom || {};
			var dataset = me.getDataset();
			var datasetIndex = me.index;
			var value = dataset.data[index];
			var yScale = me.getScaleForId(meta.yAxisID);
			var xScale = me.getScaleForId(meta.xAxisID);
			var pointOptions = me.chart.options.elements.point;
			var x, y;

			// Compatibility: If the properties are defined with only the old name, use those values
			if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
				dataset.pointRadius = dataset.radius;
			}
			if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
				dataset.pointHitRadius = dataset.hitRadius;
			}

			x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex);
			y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex);

			// Utility
			point._xScale = xScale;
			point._yScale = yScale;
			point._datasetIndex = datasetIndex;
			point._index = index;

			// Desired view properties
			point._model = {
				x: x,
				y: y,
				skip: custom.skip || isNaN(x) || isNaN(y),
				// Appearance
				radius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius),
				pointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),
				backgroundColor: me.getPointBackgroundColor(point, index),
				borderColor: me.getPointBorderColor(point, index),
				borderWidth: me.getPointBorderWidth(point, index),
				tension: meta.dataset._model ? meta.dataset._model.tension : 0,
				steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false,
				// Tooltip
				hitRadius: custom.hitRadius || helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius)
			};
		},

		calculatePointY: function(value, index, datasetIndex) {
			var me = this;
			var chart = me.chart;
			var meta = me.getMeta();
			var yScale = me.getScaleForId(meta.yAxisID);
			var sumPos = 0;
			var sumNeg = 0;
			var i, ds, dsMeta;

			if (yScale.options.stacked) {
				for (i = 0; i < datasetIndex; i++) {
					ds = chart.data.datasets[i];
					dsMeta = chart.getDatasetMeta(i);
					if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) {
						var stackedRightValue = Number(yScale.getRightValue(ds.data[index]));
						if (stackedRightValue < 0) {
							sumNeg += stackedRightValue || 0;
						} else {
							sumPos += stackedRightValue || 0;
						}
					}
				}

				var rightValue = Number(yScale.getRightValue(value));
				if (rightValue < 0) {
					return yScale.getPixelForValue(sumNeg + rightValue);
				}
				return yScale.getPixelForValue(sumPos + rightValue);
			}

			return yScale.getPixelForValue(value);
		},

		updateBezierControlPoints: function() {
			var me = this;
			var meta = me.getMeta();
			var area = me.chart.chartArea;
			var points = (meta.data || []);
			var i, ilen, point, model, controlPoints;

			// Only consider points that are drawn in case the spanGaps option is used
			if (meta.dataset._model.spanGaps) {
				points = points.filter(function(pt) {
					return !pt._model.skip;
				});
			}

			function capControlPoint(pt, min, max) {
				return Math.max(Math.min(pt, max), min);
			}

			if (meta.dataset._model.cubicInterpolationMode === 'monotone') {
				helpers.splineCurveMonotone(points);
			} else {
				for (i = 0, ilen = points.length; i < ilen; ++i) {
					point = points[i];
					model = point._model;
					controlPoints = helpers.splineCurve(
						helpers.previousItem(points, i)._model,
						model,
						helpers.nextItem(points, i)._model,
						meta.dataset._model.tension
					);
					model.controlPointPreviousX = controlPoints.previous.x;
					model.controlPointPreviousY = controlPoints.previous.y;
					model.controlPointNextX = controlPoints.next.x;
					model.controlPointNextY = controlPoints.next.y;
				}
			}

			if (me.chart.options.elements.line.capBezierPoints) {
				for (i = 0, ilen = points.length; i < ilen; ++i) {
					model = points[i]._model;
					model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);
					model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
					model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);
					model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);
				}
			}
		},

		draw: function() {
			var me = this;
			var chart = me.chart;
			var meta = me.getMeta();
			var points = meta.data || [];
			var area = chart.chartArea;
			var ilen = points.length;
			var i = 0;

			helpers.canvas.clipArea(chart.ctx, area);

			if (lineEnabled(me.getDataset(), chart.options)) {
				meta.dataset.draw();
			}

			helpers.canvas.unclipArea(chart.ctx);

			// Draw the points
			for (; i < ilen; ++i) {
				points[i].draw(area);
			}
		},

		setHoverStyle: function(point) {
			// Point
			var dataset = this.chart.data.datasets[point._datasetIndex];
			var index = point._index;
			var custom = point.custom || {};
			var model = point._model;

			model.radius = custom.hoverRadius || helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
			model.backgroundColor = custom.hoverBackgroundColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
			model.borderColor = custom.hoverBorderColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
			model.borderWidth = custom.hoverBorderWidth || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
		},

		removeHoverStyle: function(point) {
			var me = this;
			var dataset = me.chart.data.datasets[point._datasetIndex];
			var index = point._index;
			var custom = point.custom || {};
			var model = point._model;

			// Compatibility: If the properties are defined with only the old name, use those values
			if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
				dataset.pointRadius = dataset.radius;
			}

			model.radius = custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius);
			model.backgroundColor = me.getPointBackgroundColor(point, index);
			model.borderColor = me.getPointBorderColor(point, index);
			model.borderWidth = me.getPointBorderWidth(point, index);
		}
	});
};

},{"25":25,"40":40,"45":45}],19:[function(require,module,exports){
'use strict';

var defaults = require(25);
var elements = require(40);
var helpers = require(45);

defaults._set('polarArea', {
	scale: {
		type: 'radialLinear',
		angleLines: {
			display: false
		},
		gridLines: {
			circular: true
		},
		pointLabels: {
			display: false
		},
		ticks: {
			beginAtZero: true
		}
	},

	// Boolean - Whether to animate the rotation of the chart
	animation: {
		animateRotate: true,
		animateScale: true
	},

	startAngle: -0.5 * Math.PI,
	legendCallback: function(chart) {
		var text = [];
		text.push('<ul class="' + chart.id + '-legend">');

		var data = chart.data;
		var datasets = data.datasets;
		var labels = data.labels;

		if (datasets.length) {
			for (var i = 0; i < datasets[0].data.length; ++i) {
				text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
				if (labels[i]) {
					text.push(labels[i]);
				}
				text.push('</li>');
			}
		}

		text.push('</ul>');
		return text.join('');
	},
	legend: {
		labels: {
			generateLabels: function(chart) {
				var data = chart.data;
				if (data.labels.length && data.datasets.length) {
					return data.labels.map(function(label, i) {
						var meta = chart.getDatasetMeta(0);
						var ds = data.datasets[0];
						var arc = meta.data[i];
						var custom = arc.custom || {};
						var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
						var arcOpts = chart.options.elements.arc;
						var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
						var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
						var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);

						return {
							text: label,
							fillStyle: fill,
							strokeStyle: stroke,
							lineWidth: bw,
							hidden: isNaN(ds.data[i]) || meta.data[i].hidden,

							// Extra data used for toggling the correct item
							index: i
						};
					});
				}
				return [];
			}
		},

		onClick: function(e, legendItem) {
			var index = legendItem.index;
			var chart = this.chart;
			var i, ilen, meta;

			for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
				meta = chart.getDatasetMeta(i);
				meta.data[index].hidden = !meta.data[index].hidden;
			}

			chart.update();
		}
	},

	// Need to override these to give a nice default
	tooltips: {
		callbacks: {
			title: function() {
				return '';
			},
			label: function(item, data) {
				return data.labels[item.index] + ': ' + item.yLabel;
			}
		}
	}
});

module.exports = function(Chart) {

	Chart.controllers.polarArea = Chart.DatasetController.extend({

		dataElementType: elements.Arc,

		linkScales: helpers.noop,

		update: function(reset) {
			var me = this;
			var chart = me.chart;
			var chartArea = chart.chartArea;
			var meta = me.getMeta();
			var opts = chart.options;
			var arcOpts = opts.elements.arc;
			var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
			chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0);
			chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
			chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();

			me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);
			me.innerRadius = me.outerRadius - chart.radiusLength;

			meta.count = me.countVisibleElements();

			helpers.each(meta.data, function(arc, index) {
				me.updateElement(arc, index, reset);
			});
		},

		updateElement: function(arc, index, reset) {
			var me = this;
			var chart = me.chart;
			var dataset = me.getDataset();
			var opts = chart.options;
			var animationOpts = opts.animation;
			var scale = chart.scale;
			var labels = chart.data.labels;

			var circumference = me.calculateCircumference(dataset.data[index]);
			var centerX = scale.xCenter;
			var centerY = scale.yCenter;

			// If there is NaN data before us, we need to calculate the starting angle correctly.
			// We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data
			var visibleCount = 0;
			var meta = me.getMeta();
			for (var i = 0; i < index; ++i) {
				if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) {
					++visibleCount;
				}
			}

			// var negHalfPI = -0.5 * Math.PI;
			var datasetStartAngle = opts.startAngle;
			var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
			var startAngle = datasetStartAngle + (circumference * visibleCount);
			var endAngle = startAngle + (arc.hidden ? 0 : circumference);

			var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);

			helpers.extend(arc, {
				// Utility
				_datasetIndex: me.index,
				_index: index,
				_scale: scale,

				// Desired view properties
				_model: {
					x: centerX,
					y: centerY,
					innerRadius: 0,
					outerRadius: reset ? resetRadius : distance,
					startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle,
					endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle,
					label: helpers.valueAtIndexOrDefault(labels, index, labels[index])
				}
			});

			// Apply border and fill style
			me.removeHoverStyle(arc);

			arc.pivot();
		},

		removeHoverStyle: function(arc) {
			Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc);
		},

		countVisibleElements: function() {
			var dataset = this.getDataset();
			var meta = this.getMeta();
			var count = 0;

			helpers.each(meta.data, function(element, index) {
				if (!isNaN(dataset.data[index]) && !element.hidden) {
					count++;
				}
			});

			return count;
		},

		calculateCircumference: function(value) {
			var count = this.getMeta().count;
			if (count > 0 && !isNaN(value)) {
				return (2 * Math.PI) / count;
			}
			return 0;
		}
	});
};

},{"25":25,"40":40,"45":45}],20:[function(require,module,exports){
'use strict';

var defaults = require(25);
var elements = require(40);
var helpers = require(45);

defaults._set('radar', {
	scale: {
		type: 'radialLinear'
	},
	elements: {
		line: {
			tension: 0 // no bezier in radar
		}
	}
});

module.exports = function(Chart) {

	Chart.controllers.radar = Chart.DatasetController.extend({

		datasetElementType: elements.Line,

		dataElementType: elements.Point,

		linkScales: helpers.noop,

		update: function(reset) {
			var me = this;
			var meta = me.getMeta();
			var line = meta.dataset;
			var points = meta.data;
			var custom = line.custom || {};
			var dataset = me.getDataset();
			var lineElementOptions = me.chart.options.elements.line;
			var scale = me.chart.scale;

			// Compatibility: If the properties are defined with only the old name, use those values
			if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
				dataset.lineTension = dataset.tension;
			}

			helpers.extend(meta.dataset, {
				// Utility
				_datasetIndex: me.index,
				_scale: scale,
				// Data
				_children: points,
				_loop: true,
				// Model
				_model: {
					// Appearance
					tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension),
					backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
					borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
					borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
					fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
					borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
					borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
					borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
					borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
				}
			});

			meta.dataset.pivot();

			// Update Points
			helpers.each(points, function(point, index) {
				me.updateElement(point, index, reset);
			}, me);

			// Update bezier control points
			me.updateBezierControlPoints();
		},
		updateElement: function(point, index, reset) {
			var me = this;
			var custom = point.custom || {};
			var dataset = me.getDataset();
			var scale = me.chart.scale;
			var pointElementOptions = me.chart.options.elements.point;
			var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]);

			// Compatibility: If the properties are defined with only the old name, use those values
			if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
				dataset.pointRadius = dataset.radius;
			}
			if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
				dataset.pointHitRadius = dataset.hitRadius;
			}

			helpers.extend(point, {
				// Utility
				_datasetIndex: me.index,
				_index: index,
				_scale: scale,

				// Desired view properties
				_model: {
					x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
					y: reset ? scale.yCenter : pointPosition.y,

					// Appearance
					tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension),
					radius: custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius),
					backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor),
					borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor),
					borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth),
					pointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle),

					// Tooltip
					hitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius)
				}
			});

			point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y));
		},
		updateBezierControlPoints: function() {
			var chartArea = this.chart.chartArea;
			var meta = this.getMeta();

			helpers.each(meta.data, function(point, index) {
				var model = point._model;
				var controlPoints = helpers.splineCurve(
					helpers.previousItem(meta.data, index, true)._model,
					model,
					helpers.nextItem(meta.data, index, true)._model,
					model.tension
				);

				// Prevent the bezier going outside of the bounds of the graph
				model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left);
				model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top);

				model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left);
				model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top);

				// Now pivot the point for animation
				point.pivot();
			});
		},

		setHoverStyle: function(point) {
			// Point
			var dataset = this.chart.data.datasets[point._datasetIndex];
			var custom = point.custom || {};
			var index = point._index;
			var model = point._model;

			model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
			model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
			model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
			model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
		},

		removeHoverStyle: function(point) {
			var dataset = this.chart.data.datasets[point._datasetIndex];
			var custom = point.custom || {};
			var index = point._index;
			var model = point._model;
			var pointElementOptions = this.chart.options.elements.point;

			model.radius = custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius);
			model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor);
			model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor);
			model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth);
		}
	});
};

},{"25":25,"40":40,"45":45}],21:[function(require,module,exports){
'use strict';

var defaults = require(25);

defaults._set('scatter', {
	hover: {
		mode: 'single'
	},

	scales: {
		xAxes: [{
			id: 'x-axis-1',    // need an ID so datasets can reference the scale
			type: 'linear',    // scatter should not use a category axis
			position: 'bottom'
		}],
		yAxes: [{
			id: 'y-axis-1',
			type: 'linear',
			position: 'left'
		}]
	},

	showLines: false,

	tooltips: {
		callbacks: {
			title: function() {
				return '';     // doesn't make sense for scatter since data are formatted as a point
			},
			label: function(item) {
				return '(' + item.xLabel + ', ' + item.yLabel + ')';
			}
		}
	}
});

module.exports = function(Chart) {

	// Scatter charts use line controllers
	Chart.controllers.scatter = Chart.controllers.line;

};

},{"25":25}],22:[function(require,module,exports){
/* global window: false */
'use strict';

var defaults = require(25);
var Element = require(26);
var helpers = require(45);

defaults._set('global', {
	animation: {
		duration: 1000,
		easing: 'easeOutQuart',
		onProgress: helpers.noop,
		onComplete: helpers.noop
	}
});

module.exports = function(Chart) {

	Chart.Animation = Element.extend({
		chart: null, // the animation associated chart instance
		currentStep: 0, // the current animation step
		numSteps: 60, // default number of steps
		easing: '', // the easing to use for this animation
		render: null, // render function used by the animation service

		onAnimationProgress: null, // user specified callback to fire on each step of the animation
		onAnimationComplete: null, // user specified callback to fire when the animation finishes
	});

	Chart.animationService = {
		frameDuration: 17,
		animations: [],
		dropFrames: 0,
		request: null,

		/**
		 * @param {Chart} chart - The chart to animate.
		 * @param {Chart.Animation} animation - The animation that we will animate.
		 * @param {Number} duration - The animation duration in ms.
		 * @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions
		 */
		addAnimation: function(chart, animation, duration, lazy) {
			var animations = this.animations;
			var i, ilen;

			animation.chart = chart;

			if (!lazy) {
				chart.animating = true;
			}

			for (i = 0, ilen = animations.length; i < ilen; ++i) {
				if (animations[i].chart === chart) {
					animations[i] = animation;
					return;
				}
			}

			animations.push(animation);

			// If there are no animations queued, manually kickstart a digest, for lack of a better word
			if (animations.length === 1) {
				this.requestAnimationFrame();
			}
		},

		cancelAnimation: function(chart) {
			var index = helpers.findIndex(this.animations, function(animation) {
				return animation.chart === chart;
			});

			if (index !== -1) {
				this.animations.splice(index, 1);
				chart.animating = false;
			}
		},

		requestAnimationFrame: function() {
			var me = this;
			if (me.request === null) {
				// Skip animation frame requests until the active one is executed.
				// This can happen when processing mouse events, e.g. 'mousemove'
				// and 'mouseout' events will trigger multiple renders.
				me.request = helpers.requestAnimFrame.call(window, function() {
					me.request = null;
					me.startDigest();
				});
			}
		},

		/**
		 * @private
		 */
		startDigest: function() {
			var me = this;
			var startTime = Date.now();
			var framesToDrop = 0;

			if (me.dropFrames > 1) {
				framesToDrop = Math.floor(me.dropFrames);
				me.dropFrames = me.dropFrames % 1;
			}

			me.advance(1 + framesToDrop);

			var endTime = Date.now();

			me.dropFrames += (endTime - startTime) / me.frameDuration;

			// Do we have more stuff to animate?
			if (me.animations.length > 0) {
				me.requestAnimationFrame();
			}
		},

		/**
		 * @private
		 */
		advance: function(count) {
			var animations = this.animations;
			var animation, chart;
			var i = 0;

			while (i < animations.length) {
				animation = animations[i];
				chart = animation.chart;

				animation.currentStep = (animation.currentStep || 0) + count;
				animation.currentStep = Math.min(animation.currentStep, animation.numSteps);

				helpers.callback(animation.render, [chart, animation], chart);
				helpers.callback(animation.onAnimationProgress, [animation], chart);

				if (animation.currentStep >= animation.numSteps) {
					helpers.callback(animation.onAnimationComplete, [animation], chart);
					chart.animating = false;
					animations.splice(i, 1);
				} else {
					++i;
				}
			}
		}
	};

	/**
	 * Provided for backward compatibility, use Chart.Animation instead
	 * @prop Chart.Animation#animationObject
	 * @deprecated since version 2.6.0
	 * @todo remove at version 3
	 */
	Object.defineProperty(Chart.Animation.prototype, 'animationObject', {
		get: function() {
			return this;
		}
	});

	/**
	 * Provided for backward compatibility, use Chart.Animation#chart instead
	 * @prop Chart.Animation#chartInstance
	 * @deprecated since version 2.6.0
	 * @todo remove at version 3
	 */
	Object.defineProperty(Chart.Animation.prototype, 'chartInstance', {
		get: function() {
			return this.chart;
		},
		set: function(value) {
			this.chart = value;
		}
	});

};

},{"25":25,"26":26,"45":45}],23:[function(require,module,exports){
'use strict';

var defaults = require(25);
var helpers = require(45);
var Interaction = require(28);
var platform = require(48);

module.exports = function(Chart) {
	var plugins = Chart.plugins;

	// Create a dictionary of chart types, to allow for extension of existing types
	Chart.types = {};

	// Store a reference to each instance - allowing us to globally resize chart instances on window resize.
	// Destroy method on the chart will remove the instance of the chart from this reference.
	Chart.instances = {};

	// Controllers available for dataset visualization eg. bar, line, slice, etc.
	Chart.controllers = {};

	/**
	 * Initializes the given config with global and chart default values.
	 */
	function initConfig(config) {
		config = config || {};

		// Do NOT use configMerge() for the data object because this method merges arrays
		// and so would change references to labels and datasets, preventing data updates.
		var data = config.data = config.data || {};
		data.datasets = data.datasets || [];
		data.labels = data.labels || [];

		config.options = helpers.configMerge(
			defaults.global,
			defaults[config.type],
			config.options || {});

		return config;
	}

	/**
	 * Updates the config of the chart
	 * @param chart {Chart} chart to update the options for
	 */
	function updateConfig(chart) {
		var newOptions = chart.options;

		// Update Scale(s) with options
		if (newOptions.scale) {
			chart.scale.options = newOptions.scale;
		} else if (newOptions.scales) {
			newOptions.scales.xAxes.concat(newOptions.scales.yAxes).forEach(function(scaleOptions) {
				chart.scales[scaleOptions.id].options = scaleOptions;
			});
		}

		// Tooltip
		chart.tooltip._options = newOptions.tooltips;
	}

	function positionIsHorizontal(position) {
		return position === 'top' || position === 'bottom';
	}

	helpers.extend(Chart.prototype, /** @lends Chart */ {
		/**
		 * @private
		 */
		construct: function(item, config) {
			var me = this;

			config = initConfig(config);

			var context = platform.acquireContext(item, config);
			var canvas = context && context.canvas;
			var height = canvas && canvas.height;
			var width = canvas && canvas.width;

			me.id = helpers.uid();
			me.ctx = context;
			me.canvas = canvas;
			me.config = config;
			me.width = width;
			me.height = height;
			me.aspectRatio = height ? width / height : null;
			me.options = config.options;
			me._bufferedRender = false;

			/**
			 * Provided for backward compatibility, Chart and Chart.Controller have been merged,
			 * the "instance" still need to be defined since it might be called from plugins.
			 * @prop Chart#chart
			 * @deprecated since version 2.6.0
			 * @todo remove at version 3
			 * @private
			 */
			me.chart = me;
			me.controller = me; // chart.chart.controller #inception

			// Add the chart instance to the global namespace
			Chart.instances[me.id] = me;

			// Define alias to the config data: `chart.data === chart.config.data`
			Object.defineProperty(me, 'data', {
				get: function() {
					return me.config.data;
				},
				set: function(value) {
					me.config.data = value;
				}
			});

			if (!context || !canvas) {
				// The given item is not a compatible context2d element, let's return before finalizing
				// the chart initialization but after setting basic chart / controller properties that
				// can help to figure out that the chart is not valid (e.g chart.canvas !== null);
				// https://github.com/chartjs/Chart.js/issues/2807
				console.error("Failed to create chart: can't acquire context from the given item");
				return;
			}

			me.initialize();
			me.update();
		},

		/**
		 * @private
		 */
		initialize: function() {
			var me = this;

			// Before init plugin notification
			plugins.notify(me, 'beforeInit');

			helpers.retinaScale(me, me.options.devicePixelRatio);

			me.bindEvents();

			if (me.options.responsive) {
				// Initial resize before chart draws (must be silent to preserve initial animations).
				me.resize(true);
			}

			// Make sure scales have IDs and are built before we build any controllers.
			me.ensureScalesHaveIDs();
			me.buildScales();
			me.initToolTip();

			// After init plugin notification
			plugins.notify(me, 'afterInit');

			return me;
		},

		clear: function() {
			helpers.canvas.clear(this);
			return this;
		},

		stop: function() {
			// Stops any current animation loop occurring
			Chart.animationService.cancelAnimation(this);
			return this;
		},

		resize: function(silent) {
			var me = this;
			var options = me.options;
			var canvas = me.canvas;
			var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null;

			// the canvas render width and height will be casted to integers so make sure that
			// the canvas display style uses the same integer values to avoid blurring effect.

			// Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collased
			var newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas)));
			var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas)));

			if (me.width === newWidth && me.height === newHeight) {
				return;
			}

			canvas.width = me.width = newWidth;
			canvas.height = me.height = newHeight;
			canvas.style.width = newWidth + 'px';
			canvas.style.height = newHeight + 'px';

			helpers.retinaScale(me, options.devicePixelRatio);

			if (!silent) {
				// Notify any plugins about the resize
				var newSize = {width: newWidth, height: newHeight};
				plugins.notify(me, 'resize', [newSize]);

				// Notify of resize
				if (me.options.onResize) {
					me.options.onResize(me, newSize);
				}

				me.stop();
				me.update(me.options.responsiveAnimationDuration);
			}
		},

		ensureScalesHaveIDs: function() {
			var options = this.options;
			var scalesOptions = options.scales || {};
			var scaleOptions = options.scale;

			helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) {
				xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index);
			});

			helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) {
				yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index);
			});

			if (scaleOptions) {
				scaleOptions.id = scaleOptions.id || 'scale';
			}
		},

		/**
		 * Builds a map of scale ID to scale object for future lookup.
		 */
		buildScales: function() {
			var me = this;
			var options = me.options;
			var scales = me.scales = {};
			var items = [];

			if (options.scales) {
				items = items.concat(
					(options.scales.xAxes || []).map(function(xAxisOptions) {
						return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'};
					}),
					(options.scales.yAxes || []).map(function(yAxisOptions) {
						return {options: yAxisOptions, dtype: 'linear', dposition: 'left'};
					})
				);
			}

			if (options.scale) {
				items.push({
					options: options.scale,
					dtype: 'radialLinear',
					isDefault: true,
					dposition: 'chartArea'
				});
			}

			helpers.each(items, function(item) {
				var scaleOptions = item.options;
				var scaleType = helpers.valueOrDefault(scaleOptions.type, item.dtype);
				var scaleClass = Chart.scaleService.getScaleConstructor(scaleType);
				if (!scaleClass) {
					return;
				}

				if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) {
					scaleOptions.position = item.dposition;
				}

				var scale = new scaleClass({
					id: scaleOptions.id,
					options: scaleOptions,
					ctx: me.ctx,
					chart: me
				});

				scales[scale.id] = scale;
				scale.mergeTicksOptions();

				// TODO(SB): I think we should be able to remove this custom case (options.scale)
				// and consider it as a regular scale part of the "scales"" map only! This would
				// make the logic easier and remove some useless? custom code.
				if (item.isDefault) {
					me.scale = scale;
				}
			});

			Chart.scaleService.addScalesToLayout(this);
		},

		buildOrUpdateControllers: function() {
			var me = this;
			var types = [];
			var newControllers = [];

			helpers.each(me.data.datasets, function(dataset, datasetIndex) {
				var meta = me.getDatasetMeta(datasetIndex);
				var type = dataset.type || me.config.type;

				if (meta.type && meta.type !== type) {
					me.destroyDatasetMeta(datasetIndex);
					meta = me.getDatasetMeta(datasetIndex);
				}
				meta.type = type;

				types.push(meta.type);

				if (meta.controller) {
					meta.controller.updateIndex(datasetIndex);
				} else {
					var ControllerClass = Chart.controllers[meta.type];
					if (ControllerClass === undefined) {
						throw new Error('"' + meta.type + '" is not a chart type.');
					}

					meta.controller = new ControllerClass(me, datasetIndex);
					newControllers.push(meta.controller);
				}
			}, me);

			return newControllers;
		},

		/**
		 * Reset the elements of all datasets
		 * @private
		 */
		resetElements: function() {
			var me = this;
			helpers.each(me.data.datasets, function(dataset, datasetIndex) {
				me.getDatasetMeta(datasetIndex).controller.reset();
			}, me);
		},

		/**
		* Resets the chart back to it's state before the initial animation
		*/
		reset: function() {
			this.resetElements();
			this.tooltip.initialize();
		},

		update: function(config) {
			var me = this;

			if (!config || typeof config !== 'object') {
				// backwards compatibility
				config = {
					duration: config,
					lazy: arguments[1]
				};
			}

			updateConfig(me);

			if (plugins.notify(me, 'beforeUpdate') === false) {
				return;
			}

			// In case the entire data object changed
			me.tooltip._data = me.data;

			// Make sure dataset controllers are updated and new controllers are reset
			var newControllers = me.buildOrUpdateControllers();

			// Make sure all dataset controllers have correct meta data counts
			helpers.each(me.data.datasets, function(dataset, datasetIndex) {
				me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements();
			}, me);

			me.updateLayout();

			// Can only reset the new controllers after the scales have been updated
			helpers.each(newControllers, function(controller) {
				controller.reset();
			});

			me.updateDatasets();

			// Do this before render so that any plugins that need final scale updates can use it
			plugins.notify(me, 'afterUpdate');

			if (me._bufferedRender) {
				me._bufferedRequest = {
					duration: config.duration,
					easing: config.easing,
					lazy: config.lazy
				};
			} else {
				me.render(config);
			}
		},

		/**
		 * Updates the chart layout unless a plugin returns `false` to the `beforeLayout`
		 * hook, in which case, plugins will not be called on `afterLayout`.
		 * @private
		 */
		updateLayout: function() {
			var me = this;

			if (plugins.notify(me, 'beforeLayout') === false) {
				return;
			}

			Chart.layoutService.update(this, this.width, this.height);

			/**
			 * Provided for backward compatibility, use `afterLayout` instead.
			 * @method IPlugin#afterScaleUpdate
			 * @deprecated since version 2.5.0
			 * @todo remove at version 3
			 * @private
			 */
			plugins.notify(me, 'afterScaleUpdate');
			plugins.notify(me, 'afterLayout');
		},

		/**
		 * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate`
		 * hook, in which case, plugins will not be called on `afterDatasetsUpdate`.
		 * @private
		 */
		updateDatasets: function() {
			var me = this;

			if (plugins.notify(me, 'beforeDatasetsUpdate') === false) {
				return;
			}

			for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
				me.updateDataset(i);
			}

			plugins.notify(me, 'afterDatasetsUpdate');
		},

		/**
		 * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate`
		 * hook, in which case, plugins will not be called on `afterDatasetUpdate`.
		 * @private
		 */
		updateDataset: function(index) {
			var me = this;
			var meta = me.getDatasetMeta(index);
			var args = {
				meta: meta,
				index: index
			};

			if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) {
				return;
			}

			meta.controller.update();

			plugins.notify(me, 'afterDatasetUpdate', [args]);
		},

		render: function(config) {
			var me = this;

			if (!config || typeof config !== 'object') {
				// backwards compatibility
				config = {
					duration: config,
					lazy: arguments[1]
				};
			}

			var duration = config.duration;
			var lazy = config.lazy;

			if (plugins.notify(me, 'beforeRender') === false) {
				return;
			}

			var animationOptions = me.options.animation;
			var onComplete = function(animation) {
				plugins.notify(me, 'afterRender');
				helpers.callback(animationOptions && animationOptions.onComplete, [animation], me);
			};

			if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) {
				var animation = new Chart.Animation({
					numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps
					easing: config.easing || animationOptions.easing,

					render: function(chart, animationObject) {
						var easingFunction = helpers.easing.effects[animationObject.easing];
						var currentStep = animationObject.currentStep;
						var stepDecimal = currentStep / animationObject.numSteps;

						chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep);
					},

					onAnimationProgress: animationOptions.onProgress,
					onAnimationComplete: onComplete
				});

				Chart.animationService.addAnimation(me, animation, duration, lazy);
			} else {
				me.draw();

				// See https://github.com/chartjs/Chart.js/issues/3781
				onComplete(new Chart.Animation({numSteps: 0, chart: me}));
			}

			return me;
		},

		draw: function(easingValue) {
			var me = this;

			me.clear();

			if (helpers.isNullOrUndef(easingValue)) {
				easingValue = 1;
			}

			me.transition(easingValue);

			if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) {
				return;
			}

			// Draw all the scales
			helpers.each(me.boxes, function(box) {
				box.draw(me.chartArea);
			}, me);

			if (me.scale) {
				me.scale.draw();
			}

			me.drawDatasets(easingValue);

			// Finally draw the tooltip
			me.tooltip.draw();

			plugins.notify(me, 'afterDraw', [easingValue]);
		},

		/**
		 * @private
		 */
		transition: function(easingValue) {
			var me = this;

			for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) {
				if (me.isDatasetVisible(i)) {
					me.getDatasetMeta(i).controller.transition(easingValue);
				}
			}

			me.tooltip.transition(easingValue);
		},

		/**
		 * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw`
		 * hook, in which case, plugins will not be called on `afterDatasetsDraw`.
		 * @private
		 */
		drawDatasets: function(easingValue) {
			var me = this;

			if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) {
				return;
			}

			// Draw datasets reversed to support proper line stacking
			for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) {
				if (me.isDatasetVisible(i)) {
					me.drawDataset(i, easingValue);
				}
			}

			plugins.notify(me, 'afterDatasetsDraw', [easingValue]);
		},

		/**
		 * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw`
		 * hook, in which case, plugins will not be called on `afterDatasetDraw`.
		 * @private
		 */
		drawDataset: function(index, easingValue) {
			var me = this;
			var meta = me.getDatasetMeta(index);
			var args = {
				meta: meta,
				index: index,
				easingValue: easingValue
			};

			if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) {
				return;
			}

			meta.controller.draw(easingValue);

			plugins.notify(me, 'afterDatasetDraw', [args]);
		},

		// Get the single element that was clicked on
		// @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
		getElementAtEvent: function(e) {
			return Interaction.modes.single(this, e);
		},

		getElementsAtEvent: function(e) {
			return Interaction.modes.label(this, e, {intersect: true});
		},

		getElementsAtXAxis: function(e) {
			return Interaction.modes['x-axis'](this, e, {intersect: true});
		},

		getElementsAtEventForMode: function(e, mode, options) {
			var method = Interaction.modes[mode];
			if (typeof method === 'function') {
				return method(this, e, options);
			}

			return [];
		},

		getDatasetAtEvent: function(e) {
			return Interaction.modes.dataset(this, e, {intersect: true});
		},

		getDatasetMeta: function(datasetIndex) {
			var me = this;
			var dataset = me.data.datasets[datasetIndex];
			if (!dataset._meta) {
				dataset._meta = {};
			}

			var meta = dataset._meta[me.id];
			if (!meta) {
				meta = dataset._meta[me.id] = {
					type: null,
					data: [],
					dataset: null,
					controller: null,
					hidden: null,			// See isDatasetVisible() comment
					xAxisID: null,
					yAxisID: null
				};
			}

			return meta;
		},

		getVisibleDatasetCount: function() {
			var count = 0;
			for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
				if (this.isDatasetVisible(i)) {
					count++;
				}
			}
			return count;
		},

		isDatasetVisible: function(datasetIndex) {
			var meta = this.getDatasetMeta(datasetIndex);

			// meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false,
			// the dataset.hidden value is ignored, else if null, the dataset hidden state is returned.
			return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden;
		},

		generateLegend: function() {
			return this.options.legendCallback(this);
		},

		/**
		 * @private
		 */
		destroyDatasetMeta: function(datasetIndex) {
			var id = this.id;
			var dataset = this.data.datasets[datasetIndex];
			var meta = dataset._meta && dataset._meta[id];

			if (meta) {
				meta.controller.destroy();
				delete dataset._meta[id];
			}
		},

		destroy: function() {
			var me = this;
			var canvas = me.canvas;
			var i, ilen;

			me.stop();

			// dataset controllers need to cleanup associated data
			for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
				me.destroyDatasetMeta(i);
			}

			if (canvas) {
				me.unbindEvents();
				helpers.canvas.clear(me);
				platform.releaseContext(me.ctx);
				me.canvas = null;
				me.ctx = null;
			}

			plugins.notify(me, 'destroy');

			delete Chart.instances[me.id];
		},

		toBase64Image: function() {
			return this.canvas.toDataURL.apply(this.canvas, arguments);
		},

		initToolTip: function() {
			var me = this;
			me.tooltip = new Chart.Tooltip({
				_chart: me,
				_chartInstance: me, // deprecated, backward compatibility
				_data: me.data,
				_options: me.options.tooltips
			}, me);
		},

		/**
		 * @private
		 */
		bindEvents: function() {
			var me = this;
			var listeners = me._listeners = {};
			var listener = function() {
				me.eventHandler.apply(me, arguments);
			};

			helpers.each(me.options.events, function(type) {
				platform.addEventListener(me, type, listener);
				listeners[type] = listener;
			});

			// Elements used to detect size change should not be injected for non responsive charts.
			// See https://github.com/chartjs/Chart.js/issues/2210
			if (me.options.responsive) {
				listener = function() {
					me.resize();
				};

				platform.addEventListener(me, 'resize', listener);
				listeners.resize = listener;
			}
		},

		/**
		 * @private
		 */
		unbindEvents: function() {
			var me = this;
			var listeners = me._listeners;
			if (!listeners) {
				return;
			}

			delete me._listeners;
			helpers.each(listeners, function(listener, type) {
				platform.removeEventListener(me, type, listener);
			});
		},

		updateHoverStyle: function(elements, mode, enabled) {
			var method = enabled ? 'setHoverStyle' : 'removeHoverStyle';
			var element, i, ilen;

			for (i = 0, ilen = elements.length; i < ilen; ++i) {
				element = elements[i];
				if (element) {
					this.getDatasetMeta(element._datasetIndex).controller[method](element);
				}
			}
		},

		/**
		 * @private
		 */
		eventHandler: function(e) {
			var me = this;
			var tooltip = me.tooltip;

			if (plugins.notify(me, 'beforeEvent', [e]) === false) {
				return;
			}

			// Buffer any update calls so that renders do not occur
			me._bufferedRender = true;
			me._bufferedRequest = null;

			var changed = me.handleEvent(e);
			changed |= tooltip && tooltip.handleEvent(e);

			plugins.notify(me, 'afterEvent', [e]);

			var bufferedRequest = me._bufferedRequest;
			if (bufferedRequest) {
				// If we have an update that was triggered, we need to do a normal render
				me.render(bufferedRequest);
			} else if (changed && !me.animating) {
				// If entering, leaving, or changing elements, animate the change via pivot
				me.stop();

				// We only need to render at this point. Updating will cause scales to be
				// recomputed generating flicker & using more memory than necessary.
				me.render(me.options.hover.animationDuration, true);
			}

			me._bufferedRender = false;
			me._bufferedRequest = null;

			return me;
		},

		/**
		 * Handle an event
		 * @private
		 * @param {IEvent} event the event to handle
		 * @return {Boolean} true if the chart needs to re-render
		 */
		handleEvent: function(e) {
			var me = this;
			var options = me.options || {};
			var hoverOptions = options.hover;
			var changed = false;

			me.lastActive = me.lastActive || [];

			// Find Active Elements for hover and tooltips
			if (e.type === 'mouseout') {
				me.active = [];
			} else {
				me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions);
			}

			// Invoke onHover hook
			// Need to call with native event here to not break backwards compatibility
			helpers.callback(options.onHover || options.hover.onHover, [e.native, me.active], me);

			if (e.type === 'mouseup' || e.type === 'click') {
				if (options.onClick) {
					// Use e.native here for backwards compatibility
					options.onClick.call(me, e.native, me.active);
				}
			}

			// Remove styling for last active (even if it may still be active)
			if (me.lastActive.length) {
				me.updateHoverStyle(me.lastActive, hoverOptions.mode, false);
			}

			// Built in hover styling
			if (me.active.length && hoverOptions.mode) {
				me.updateHoverStyle(me.active, hoverOptions.mode, true);
			}

			changed = !helpers.arrayEquals(me.active, me.lastActive);

			// Remember Last Actives
			me.lastActive = me.active;

			return changed;
		}
	});

	/**
	 * Provided for backward compatibility, use Chart instead.
	 * @class Chart.Controller
	 * @deprecated since version 2.6.0
	 * @todo remove at version 3
	 * @private
	 */
	Chart.Controller = Chart;
};

},{"25":25,"28":28,"45":45,"48":48}],24:[function(require,module,exports){
'use strict';

var helpers = require(45);

module.exports = function(Chart) {

	var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];

	/**
	 * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',
	 * 'unshift') and notify the listener AFTER the array has been altered. Listeners are
	 * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments.
	 */
	function listenArrayEvents(array, listener) {
		if (array._chartjs) {
			array._chartjs.listeners.push(listener);
			return;
		}

		Object.defineProperty(array, '_chartjs', {
			configurable: true,
			enumerable: false,
			value: {
				listeners: [listener]
			}
		});

		arrayEvents.forEach(function(key) {
			var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1);
			var base = array[key];

			Object.defineProperty(array, key, {
				configurable: true,
				enumerable: false,
				value: function() {
					var args = Array.prototype.slice.call(arguments);
					var res = base.apply(this, args);

					helpers.each(array._chartjs.listeners, function(object) {
						if (typeof object[method] === 'function') {
							object[method].apply(object, args);
						}
					});

					return res;
				}
			});
		});
	}

	/**
	 * Removes the given array event listener and cleanup extra attached properties (such as
	 * the _chartjs stub and overridden methods) if array doesn't have any more listeners.
	 */
	function unlistenArrayEvents(array, listener) {
		var stub = array._chartjs;
		if (!stub) {
			return;
		}

		var listeners = stub.listeners;
		var index = listeners.indexOf(listener);
		if (index !== -1) {
			listeners.splice(index, 1);
		}

		if (listeners.length > 0) {
			return;
		}

		arrayEvents.forEach(function(key) {
			delete array[key];
		});

		delete array._chartjs;
	}

	// Base class for all dataset controllers (line, bar, etc)
	Chart.DatasetController = function(chart, datasetIndex) {
		this.initialize(chart, datasetIndex);
	};

	helpers.extend(Chart.DatasetController.prototype, {

		/**
		 * Element type used to generate a meta dataset (e.g. Chart.element.Line).
		 * @type {Chart.core.element}
		 */
		datasetElementType: null,

		/**
		 * Element type used to generate a meta data (e.g. Chart.element.Point).
		 * @type {Chart.core.element}
		 */
		dataElementType: null,

		initialize: function(chart, datasetIndex) {
			var me = this;
			me.chart = chart;
			me.index = datasetIndex;
			me.linkScales();
			me.addElements();
		},

		updateIndex: function(datasetIndex) {
			this.index = datasetIndex;
		},

		linkScales: function() {
			var me = this;
			var meta = me.getMeta();
			var dataset = me.getDataset();

			if (meta.xAxisID === null) {
				meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id;
			}
			if (meta.yAxisID === null) {
				meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id;
			}
		},

		getDataset: function() {
			return this.chart.data.datasets[this.index];
		},

		getMeta: function() {
			return this.chart.getDatasetMeta(this.index);
		},

		getScaleForId: function(scaleID) {
			return this.chart.scales[scaleID];
		},

		reset: function() {
			this.update(true);
		},

		/**
		 * @private
		 */
		destroy: function() {
			if (this._data) {
				unlistenArrayEvents(this._data, this);
			}
		},

		createMetaDataset: function() {
			var me = this;
			var type = me.datasetElementType;
			return type && new type({
				_chart: me.chart,
				_datasetIndex: me.index
			});
		},

		createMetaData: function(index) {
			var me = this;
			var type = me.dataElementType;
			return type && new type({
				_chart: me.chart,
				_datasetIndex: me.index,
				_index: index
			});
		},

		addElements: function() {
			var me = this;
			var meta = me.getMeta();
			var data = me.getDataset().data || [];
			var metaData = meta.data;
			var i, ilen;

			for (i = 0, ilen = data.length; i < ilen; ++i) {
				metaData[i] = metaData[i] || me.createMetaData(i);
			}

			meta.dataset = meta.dataset || me.createMetaDataset();
		},

		addElementAndReset: function(index) {
			var element = this.createMetaData(index);
			this.getMeta().data.splice(index, 0, element);
			this.updateElement(element, index, true);
		},

		buildOrUpdateElements: function() {
			var me = this;
			var dataset = me.getDataset();
			var data = dataset.data || (dataset.data = []);

			// In order to correctly handle data addition/deletion animation (an thus simulate
			// real-time charts), we need to monitor these data modifications and synchronize
			// the internal meta data accordingly.
			if (me._data !== data) {
				if (me._data) {
					// This case happens when the user replaced the data array instance.
					unlistenArrayEvents(me._data, me);
				}

				listenArrayEvents(data, me);
				me._data = data;
			}

			// Re-sync meta data in case the user replaced the data array or if we missed
			// any updates and so make sure that we handle number of datapoints changing.
			me.resyncElements();
		},

		update: helpers.noop,

		transition: function(easingValue) {
			var meta = this.getMeta();
			var elements = meta.data || [];
			var ilen = elements.length;
			var i = 0;

			for (; i < ilen; ++i) {
				elements[i].transition(easingValue);
			}

			if (meta.dataset) {
				meta.dataset.transition(easingValue);
			}
		},

		draw: function() {
			var meta = this.getMeta();
			var elements = meta.data || [];
			var ilen = elements.length;
			var i = 0;

			if (meta.dataset) {
				meta.dataset.draw();
			}

			for (; i < ilen; ++i) {
				elements[i].draw();
			}
		},

		removeHoverStyle: function(element, elementOpts) {
			var dataset = this.chart.data.datasets[element._datasetIndex];
			var index = element._index;
			var custom = element.custom || {};
			var valueOrDefault = helpers.valueAtIndexOrDefault;
			var model = element._model;

			model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
			model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
			model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
		},

		setHoverStyle: function(element) {
			var dataset = this.chart.data.datasets[element._datasetIndex];
			var index = element._index;
			var custom = element.custom || {};
			var valueOrDefault = helpers.valueAtIndexOrDefault;
			var getHoverColor = helpers.getHoverColor;
			var model = element._model;

			model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor));
			model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor));
			model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
		},

		/**
		 * @private
		 */
		resyncElements: function() {
			var me = this;
			var meta = me.getMeta();
			var data = me.getDataset().data;
			var numMeta = meta.data.length;
			var numData = data.length;

			if (numData < numMeta) {
				meta.data.splice(numData, numMeta - numData);
			} else if (numData > numMeta) {
				me.insertElements(numMeta, numData - numMeta);
			}
		},

		/**
		 * @private
		 */
		insertElements: function(start, count) {
			for (var i = 0; i < count; ++i) {
				this.addElementAndReset(start + i);
			}
		},

		/**
		 * @private
		 */
		onDataPush: function() {
			this.insertElements(this.getDataset().data.length - 1, arguments.length);
		},

		/**
		 * @private
		 */
		onDataPop: function() {
			this.getMeta().data.pop();
		},

		/**
		 * @private
		 */
		onDataShift: function() {
			this.getMeta().data.shift();
		},

		/**
		 * @private
		 */
		onDataSplice: function(start, count) {
			this.getMeta().data.splice(start, count);
			this.insertElements(start, arguments.length - 2);
		},

		/**
		 * @private
		 */
		onDataUnshift: function() {
			this.insertElements(0, arguments.length);
		}
	});

	Chart.DatasetController.extend = helpers.inherits;
};

},{"45":45}],25:[function(require,module,exports){
'use strict';

var helpers = require(45);

module.exports = {
	/**
	 * @private
	 */
	_set: function(scope, values) {
		return helpers.merge(this[scope] || (this[scope] = {}), values);
	}
};

},{"45":45}],26:[function(require,module,exports){
'use strict';

var color = require(3);
var helpers = require(45);

function interpolate(start, view, model, ease) {
	var keys = Object.keys(model);
	var i, ilen, key, actual, origin, target, type, c0, c1;

	for (i = 0, ilen = keys.length; i < ilen; ++i) {
		key = keys[i];

		target = model[key];

		// if a value is added to the model after pivot() has been called, the view
		// doesn't contain it, so let's initialize the view to the target value.
		if (!view.hasOwnProperty(key)) {
			view[key] = target;
		}

		actual = view[key];

		if (actual === target || key[0] === '_') {
			continue;
		}

		if (!start.hasOwnProperty(key)) {
			start[key] = actual;
		}

		origin = start[key];

		type = typeof target;

		if (type === typeof origin) {
			if (type === 'string') {
				c0 = color(origin);
				if (c0.valid) {
					c1 = color(target);
					if (c1.valid) {
						view[key] = c1.mix(c0, ease).rgbString();
						continue;
					}
				}
			} else if (type === 'number' && isFinite(origin) && isFinite(target)) {
				view[key] = origin + (target - origin) * ease;
				continue;
			}
		}

		view[key] = target;
	}
}

var Element = function(configuration) {
	helpers.extend(this, configuration);
	this.initialize.apply(this, arguments);
};

helpers.extend(Element.prototype, {

	initialize: function() {
		this.hidden = false;
	},

	pivot: function() {
		var me = this;
		if (!me._view) {
			me._view = helpers.clone(me._model);
		}
		me._start = {};
		return me;
	},

	transition: function(ease) {
		var me = this;
		var model = me._model;
		var start = me._start;
		var view = me._view;

		// No animation -> No Transition
		if (!model || ease === 1) {
			me._view = model;
			me._start = null;
			return me;
		}

		if (!view) {
			view = me._view = {};
		}

		if (!start) {
			start = me._start = {};
		}

		interpolate(start, view, model, ease);

		return me;
	},

	tooltipPosition: function() {
		return {
			x: this._model.x,
			y: this._model.y
		};
	},

	hasValue: function() {
		return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y);
	}
});

Element.extend = helpers.inherits;

module.exports = Element;

},{"3":3,"45":45}],27:[function(require,module,exports){
/* global window: false */
/* global document: false */
'use strict';

var color = require(3);
var defaults = require(25);
var helpers = require(45);

module.exports = function(Chart) {

	// -- Basic js utility methods

	helpers.extend = function(base) {
		var setFn = function(value, key) {
			base[key] = value;
		};
		for (var i = 1, ilen = arguments.length; i < ilen; i++) {
			helpers.each(arguments[i], setFn);
		}
		return base;
	};

	helpers.configMerge = function(/* objects ... */) {
		return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), {
			merger: function(key, target, source, options) {
				var tval = target[key] || {};
				var sval = source[key];

				if (key === 'scales') {
					// scale config merging is complex. Add our own function here for that
					target[key] = helpers.scaleMerge(tval, sval);
				} else if (key === 'scale') {
					// used in polar area & radar charts since there is only one scale
					target[key] = helpers.merge(tval, [Chart.scaleService.getScaleDefaults(sval.type), sval]);
				} else {
					helpers._merger(key, target, source, options);
				}
			}
		});
	};

	helpers.scaleMerge = function(/* objects ... */) {
		return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), {
			merger: function(key, target, source, options) {
				if (key === 'xAxes' || key === 'yAxes') {
					var slen = source[key].length;
					var i, type, scale;

					if (!target[key]) {
						target[key] = [];
					}

					for (i = 0; i < slen; ++i) {
						scale = source[key][i];
						type = helpers.valueOrDefault(scale.type, key === 'xAxes' ? 'category' : 'linear');

						if (i >= target[key].length) {
							target[key].push({});
						}

						if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) {
							// new/untyped scale or type changed: let's apply the new defaults
							// then merge source scale to correctly overwrite the defaults.
							helpers.merge(target[key][i], [Chart.scaleService.getScaleDefaults(type), scale]);
						} else {
							// scales type are the same
							helpers.merge(target[key][i], scale);
						}
					}
				} else {
					helpers._merger(key, target, source, options);
				}
			}
		});
	};

	helpers.where = function(collection, filterCallback) {
		if (helpers.isArray(collection) && Array.prototype.filter) {
			return collection.filter(filterCallback);
		}
		var filtered = [];

		helpers.each(collection, function(item) {
			if (filterCallback(item)) {
				filtered.push(item);
			}
		});

		return filtered;
	};
	helpers.findIndex = Array.prototype.findIndex ?
		function(array, callback, scope) {
			return array.findIndex(callback, scope);
		} :
		function(array, callback, scope) {
			scope = scope === undefined ? array : scope;
			for (var i = 0, ilen = array.length; i < ilen; ++i) {
				if (callback.call(scope, array[i], i, array)) {
					return i;
				}
			}
			return -1;
		};
	helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {
		// Default to start of the array
		if (helpers.isNullOrUndef(startIndex)) {
			startIndex = -1;
		}
		for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
			var currentItem = arrayToSearch[i];
			if (filterCallback(currentItem)) {
				return currentItem;
			}
		}
	};
	helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {
		// Default to end of the array
		if (helpers.isNullOrUndef(startIndex)) {
			startIndex = arrayToSearch.length;
		}
		for (var i = startIndex - 1; i >= 0; i--) {
			var currentItem = arrayToSearch[i];
			if (filterCallback(currentItem)) {
				return currentItem;
			}
		}
	};
	helpers.inherits = function(extensions) {
		// Basic javascript inheritance based on the model created in Backbone.js
		var me = this;
		var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() {
			return me.apply(this, arguments);
		};

		var Surrogate = function() {
			this.constructor = ChartElement;
		};
		Surrogate.prototype = me.prototype;
		ChartElement.prototype = new Surrogate();

		ChartElement.extend = helpers.inherits;

		if (extensions) {
			helpers.extend(ChartElement.prototype, extensions);
		}

		ChartElement.__super__ = me.prototype;

		return ChartElement;
	};
	// -- Math methods
	helpers.isNumber = function(n) {
		return !isNaN(parseFloat(n)) && isFinite(n);
	};
	helpers.almostEquals = function(x, y, epsilon) {
		return Math.abs(x - y) < epsilon;
	};
	helpers.almostWhole = function(x, epsilon) {
		var rounded = Math.round(x);
		return (((rounded - epsilon) < x) && ((rounded + epsilon) > x));
	};
	helpers.max = function(array) {
		return array.reduce(function(max, value) {
			if (!isNaN(value)) {
				return Math.max(max, value);
			}
			return max;
		}, Number.NEGATIVE_INFINITY);
	};
	helpers.min = function(array) {
		return array.reduce(function(min, value) {
			if (!isNaN(value)) {
				return Math.min(min, value);
			}
			return min;
		}, Number.POSITIVE_INFINITY);
	};
	helpers.sign = Math.sign ?
		function(x) {
			return Math.sign(x);
		} :
		function(x) {
			x = +x; // convert to a number
			if (x === 0 || isNaN(x)) {
				return x;
			}
			return x > 0 ? 1 : -1;
		};
	helpers.log10 = Math.log10 ?
		function(x) {
			return Math.log10(x);
		} :
		function(x) {
			return Math.log(x) / Math.LN10;
		};
	helpers.toRadians = function(degrees) {
		return degrees * (Math.PI / 180);
	};
	helpers.toDegrees = function(radians) {
		return radians * (180 / Math.PI);
	};
	// Gets the angle from vertical upright to the point about a centre.
	helpers.getAngleFromPoint = function(centrePoint, anglePoint) {
		var distanceFromXCenter = anglePoint.x - centrePoint.x;
		var distanceFromYCenter = anglePoint.y - centrePoint.y;
		var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);

		var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);

		if (angle < (-0.5 * Math.PI)) {
			angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
		}

		return {
			angle: angle,
			distance: radialDistanceFromCenter
		};
	};
	helpers.distanceBetweenPoints = function(pt1, pt2) {
		return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
	};
	helpers.aliasPixel = function(pixelWidth) {
		return (pixelWidth % 2 === 0) ? 0 : 0.5;
	};
	helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) {
		// Props to Rob Spencer at scaled innovation for his post on splining between points
		// http://scaledinnovation.com/analytics/splines/aboutSplines.html

		// This function must also respect "skipped" points

		var previous = firstPoint.skip ? middlePoint : firstPoint;
		var current = middlePoint;
		var next = afterPoint.skip ? middlePoint : afterPoint;

		var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2));
		var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2));

		var s01 = d01 / (d01 + d12);
		var s12 = d12 / (d01 + d12);

		// If all points are the same, s01 & s02 will be inf
		s01 = isNaN(s01) ? 0 : s01;
		s12 = isNaN(s12) ? 0 : s12;

		var fa = t * s01; // scaling factor for triangle Ta
		var fb = t * s12;

		return {
			previous: {
				x: current.x - fa * (next.x - previous.x),
				y: current.y - fa * (next.y - previous.y)
			},
			next: {
				x: current.x + fb * (next.x - previous.x),
				y: current.y + fb * (next.y - previous.y)
			}
		};
	};
	helpers.EPSILON = Number.EPSILON || 1e-14;
	helpers.splineCurveMonotone = function(points) {
		// This function calculates Bézier control points in a similar way than |splineCurve|,
		// but preserves monotonicity of the provided data and ensures no local extremums are added
		// between the dataset discrete points due to the interpolation.
		// See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation

		var pointsWithTangents = (points || []).map(function(point) {
			return {
				model: point._model,
				deltaK: 0,
				mK: 0
			};
		});

		// Calculate slopes (deltaK) and initialize tangents (mK)
		var pointsLen = pointsWithTangents.length;
		var i, pointBefore, pointCurrent, pointAfter;
		for (i = 0; i < pointsLen; ++i) {
			pointCurrent = pointsWithTangents[i];
			if (pointCurrent.model.skip) {
				continue;
			}

			pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
			pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
			if (pointAfter && !pointAfter.model.skip) {
				var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x);

				// In the case of two points that appear at the same x pixel, slopeDeltaX is 0
				pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0;
			}

			if (!pointBefore || pointBefore.model.skip) {
				pointCurrent.mK = pointCurrent.deltaK;
			} else if (!pointAfter || pointAfter.model.skip) {
				pointCurrent.mK = pointBefore.deltaK;
			} else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) {
				pointCurrent.mK = 0;
			} else {
				pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2;
			}
		}

		// Adjust tangents to ensure monotonic properties
		var alphaK, betaK, tauK, squaredMagnitude;
		for (i = 0; i < pointsLen - 1; ++i) {
			pointCurrent = pointsWithTangents[i];
			pointAfter = pointsWithTangents[i + 1];
			if (pointCurrent.model.skip || pointAfter.model.skip) {
				continue;
			}

			if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) {
				pointCurrent.mK = pointAfter.mK = 0;
				continue;
			}

			alphaK = pointCurrent.mK / pointCurrent.deltaK;
			betaK = pointAfter.mK / pointCurrent.deltaK;
			squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
			if (squaredMagnitude <= 9) {
				continue;
			}

			tauK = 3 / Math.sqrt(squaredMagnitude);
			pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK;
			pointAfter.mK = betaK * tauK * pointCurrent.deltaK;
		}

		// Compute control points
		var deltaX;
		for (i = 0; i < pointsLen; ++i) {
			pointCurrent = pointsWithTangents[i];
			if (pointCurrent.model.skip) {
				continue;
			}

			pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
			pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
			if (pointBefore && !pointBefore.model.skip) {
				deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3;
				pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX;
				pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK;
			}
			if (pointAfter && !pointAfter.model.skip) {
				deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3;
				pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX;
				pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK;
			}
		}
	};
	helpers.nextItem = function(collection, index, loop) {
		if (loop) {
			return index >= collection.length - 1 ? collection[0] : collection[index + 1];
		}
		return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1];
	};
	helpers.previousItem = function(collection, index, loop) {
		if (loop) {
			return index <= 0 ? collection[collection.length - 1] : collection[index - 1];
		}
		return index <= 0 ? collection[0] : collection[index - 1];
	};
	// Implementation of the nice number algorithm used in determining where axis labels will go
	helpers.niceNum = function(range, round) {
		var exponent = Math.floor(helpers.log10(range));
		var fraction = range / Math.pow(10, exponent);
		var niceFraction;

		if (round) {
			if (fraction < 1.5) {
				niceFraction = 1;
			} else if (fraction < 3) {
				niceFraction = 2;
			} else if (fraction < 7) {
				niceFraction = 5;
			} else {
				niceFraction = 10;
			}
		} else if (fraction <= 1.0) {
			niceFraction = 1;
		} else if (fraction <= 2) {
			niceFraction = 2;
		} else if (fraction <= 5) {
			niceFraction = 5;
		} else {
			niceFraction = 10;
		}

		return niceFraction * Math.pow(10, exponent);
	};
	// Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
	helpers.requestAnimFrame = (function() {
		if (typeof window === 'undefined') {
			return function(callback) {
				callback();
			};
		}
		return window.requestAnimationFrame ||
			window.webkitRequestAnimationFrame ||
			window.mozRequestAnimationFrame ||
			window.oRequestAnimationFrame ||
			window.msRequestAnimationFrame ||
			function(callback) {
				return window.setTimeout(callback, 1000 / 60);
			};
	}());
	// -- DOM methods
	helpers.getRelativePosition = function(evt, chart) {
		var mouseX, mouseY;
		var e = evt.originalEvent || evt;
		var canvas = evt.currentTarget || evt.srcElement;
		var boundingRect = canvas.getBoundingClientRect();

		var touches = e.touches;
		if (touches && touches.length > 0) {
			mouseX = touches[0].clientX;
			mouseY = touches[0].clientY;

		} else {
			mouseX = e.clientX;
			mouseY = e.clientY;
		}

		// Scale mouse coordinates into canvas coordinates
		// by following the pattern laid out by 'jerryj' in the comments of
		// http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
		var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left'));
		var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top'));
		var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right'));
		var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom'));
		var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight;
		var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom;

		// We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However
		// the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here
		mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio);
		mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio);

		return {
			x: mouseX,
			y: mouseY
		};

	};

	// Private helper function to convert max-width/max-height values that may be percentages into a number
	function parseMaxStyle(styleValue, node, parentProperty) {
		var valueInPixels;
		if (typeof styleValue === 'string') {
			valueInPixels = parseInt(styleValue, 10);

			if (styleValue.indexOf('%') !== -1) {
				// percentage * size in dimension
				valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
			}
		} else {
			valueInPixels = styleValue;
		}

		return valueInPixels;
	}

	/**
	 * Returns if the given value contains an effective constraint.
	 * @private
	 */
	function isConstrainedValue(value) {
		return value !== undefined && value !== null && value !== 'none';
	}

	// Private helper to get a constraint dimension
	// @param domNode : the node to check the constraint on
	// @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight)
	// @param percentageProperty : property of parent to use when calculating width as a percentage
	// @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser
	function getConstraintDimension(domNode, maxStyle, percentageProperty) {
		var view = document.defaultView;
		var parentNode = domNode.parentNode;
		var constrainedNode = view.getComputedStyle(domNode)[maxStyle];
		var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle];
		var hasCNode = isConstrainedValue(constrainedNode);
		var hasCContainer = isConstrainedValue(constrainedContainer);
		var infinity = Number.POSITIVE_INFINITY;

		if (hasCNode || hasCContainer) {
			return Math.min(
				hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity,
				hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity);
		}

		return 'none';
	}
	// returns Number or undefined if no constraint
	helpers.getConstraintWidth = function(domNode) {
		return getConstraintDimension(domNode, 'max-width', 'clientWidth');
	};
	// returns Number or undefined if no constraint
	helpers.getConstraintHeight = function(domNode) {
		return getConstraintDimension(domNode, 'max-height', 'clientHeight');
	};
	helpers.getMaximumWidth = function(domNode) {
		var container = domNode.parentNode;
		if (!container) {
			return domNode.clientWidth;
		}

		var paddingLeft = parseInt(helpers.getStyle(container, 'padding-left'), 10);
		var paddingRight = parseInt(helpers.getStyle(container, 'padding-right'), 10);
		var w = container.clientWidth - paddingLeft - paddingRight;
		var cw = helpers.getConstraintWidth(domNode);
		return isNaN(cw) ? w : Math.min(w, cw);
	};
	helpers.getMaximumHeight = function(domNode) {
		var container = domNode.parentNode;
		if (!container) {
			return domNode.clientHeight;
		}

		var paddingTop = parseInt(helpers.getStyle(container, 'padding-top'), 10);
		var paddingBottom = parseInt(helpers.getStyle(container, 'padding-bottom'), 10);
		var h = container.clientHeight - paddingTop - paddingBottom;
		var ch = helpers.getConstraintHeight(domNode);
		return isNaN(ch) ? h : Math.min(h, ch);
	};
	helpers.getStyle = function(el, property) {
		return el.currentStyle ?
			el.currentStyle[property] :
			document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
	};
	helpers.retinaScale = function(chart, forceRatio) {
		var pixelRatio = chart.currentDevicePixelRatio = forceRatio || window.devicePixelRatio || 1;
		if (pixelRatio === 1) {
			return;
		}

		var canvas = chart.canvas;
		var height = chart.height;
		var width = chart.width;

		canvas.height = height * pixelRatio;
		canvas.width = width * pixelRatio;
		chart.ctx.scale(pixelRatio, pixelRatio);

		// If no style has been set on the canvas, the render size is used as display size,
		// making the chart visually bigger, so let's enforce it to the "correct" values.
		// See https://github.com/chartjs/Chart.js/issues/3575
		canvas.style.height = height + 'px';
		canvas.style.width = width + 'px';
	};
	// -- Canvas methods
	helpers.fontString = function(pixelSize, fontStyle, fontFamily) {
		return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
	};
	helpers.longestText = function(ctx, font, arrayOfThings, cache) {
		cache = cache || {};
		var data = cache.data = cache.data || {};
		var gc = cache.garbageCollect = cache.garbageCollect || [];

		if (cache.font !== font) {
			data = cache.data = {};
			gc = cache.garbageCollect = [];
			cache.font = font;
		}

		ctx.font = font;
		var longest = 0;
		helpers.each(arrayOfThings, function(thing) {
			// Undefined strings and arrays should not be measured
			if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) {
				longest = helpers.measureText(ctx, data, gc, longest, thing);
			} else if (helpers.isArray(thing)) {
				// if it is an array lets measure each element
				// to do maybe simplify this function a bit so we can do this more recursively?
				helpers.each(thing, function(nestedThing) {
					// Undefined strings and arrays should not be measured
					if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) {
						longest = helpers.measureText(ctx, data, gc, longest, nestedThing);
					}
				});
			}
		});

		var gcLen = gc.length / 2;
		if (gcLen > arrayOfThings.length) {
			for (var i = 0; i < gcLen; i++) {
				delete data[gc[i]];
			}
			gc.splice(0, gcLen);
		}
		return longest;
	};
	helpers.measureText = function(ctx, data, gc, longest, string) {
		var textWidth = data[string];
		if (!textWidth) {
			textWidth = data[string] = ctx.measureText(string).width;
			gc.push(string);
		}
		if (textWidth > longest) {
			longest = textWidth;
		}
		return longest;
	};
	helpers.numberOfLabelLines = function(arrayOfThings) {
		var numberOfLines = 1;
		helpers.each(arrayOfThings, function(thing) {
			if (helpers.isArray(thing)) {
				if (thing.length > numberOfLines) {
					numberOfLines = thing.length;
				}
			}
		});
		return numberOfLines;
	};

	helpers.color = !color ?
		function(value) {
			console.error('Color.js not found!');
			return value;
		} :
		function(value) {
			/* global CanvasGradient */
			if (value instanceof CanvasGradient) {
				value = defaults.global.defaultColor;
			}

			return color(value);
		};

	helpers.getHoverColor = function(colorValue) {
		/* global CanvasPattern */
		return (colorValue instanceof CanvasPattern) ?
			colorValue :
			helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString();
	};
};

},{"25":25,"3":3,"45":45}],28:[function(require,module,exports){
'use strict';

var helpers = require(45);

/**
 * Helper function to get relative position for an event
 * @param {Event|IEvent} event - The event to get the position for
 * @param {Chart} chart - The chart
 * @returns {Point} the event position
 */
function getRelativePosition(e, chart) {
	if (e.native) {
		return {
			x: e.x,
			y: e.y
		};
	}

	return helpers.getRelativePosition(e, chart);
}

/**
 * Helper function to traverse all of the visible elements in the chart
 * @param chart {chart} the chart
 * @param handler {Function} the callback to execute for each visible item
 */
function parseVisibleItems(chart, handler) {
	var datasets = chart.data.datasets;
	var meta, i, j, ilen, jlen;

	for (i = 0, ilen = datasets.length; i < ilen; ++i) {
		if (!chart.isDatasetVisible(i)) {
			continue;
		}

		meta = chart.getDatasetMeta(i);
		for (j = 0, jlen = meta.data.length; j < jlen; ++j) {
			var element = meta.data[j];
			if (!element._view.skip) {
				handler(element);
			}
		}
	}
}

/**
 * Helper function to get the items that intersect the event position
 * @param items {ChartElement[]} elements to filter
 * @param position {Point} the point to be nearest to
 * @return {ChartElement[]} the nearest items
 */
function getIntersectItems(chart, position) {
	var elements = [];

	parseVisibleItems(chart, function(element) {
		if (element.inRange(position.x, position.y)) {
			elements.push(element);
		}
	});

	return elements;
}

/**
 * Helper function to get the items nearest to the event position considering all visible items in teh chart
 * @param chart {Chart} the chart to look at elements from
 * @param position {Point} the point to be nearest to
 * @param intersect {Boolean} if true, only consider items that intersect the position
 * @param distanceMetric {Function} function to provide the distance between points
 * @return {ChartElement[]} the nearest items
 */
function getNearestItems(chart, position, intersect, distanceMetric) {
	var minDistance = Number.POSITIVE_INFINITY;
	var nearestItems = [];

	parseVisibleItems(chart, function(element) {
		if (intersect && !element.inRange(position.x, position.y)) {
			return;
		}

		var center = element.getCenterPoint();
		var distance = distanceMetric(position, center);

		if (distance < minDistance) {
			nearestItems = [element];
			minDistance = distance;
		} else if (distance === minDistance) {
			// Can have multiple items at the same distance in which case we sort by size
			nearestItems.push(element);
		}
	});

	return nearestItems;
}

/**
 * Get a distance metric function for two points based on the
 * axis mode setting
 * @param {String} axis the axis mode. x|y|xy
 */
function getDistanceMetricForAxis(axis) {
	var useX = axis.indexOf('x') !== -1;
	var useY = axis.indexOf('y') !== -1;

	return function(pt1, pt2) {
		var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;
		var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;
		return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
	};
}

function indexMode(chart, e, options) {
	var position = getRelativePosition(e, chart);
	// Default axis for index mode is 'x' to match old behaviour
	options.axis = options.axis || 'x';
	var distanceMetric = getDistanceMetricForAxis(options.axis);
	var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
	var elements = [];

	if (!items.length) {
		return [];
	}

	chart.data.datasets.forEach(function(dataset, datasetIndex) {
		if (chart.isDatasetVisible(datasetIndex)) {
			var meta = chart.getDatasetMeta(datasetIndex);
			var element = meta.data[items[0]._index];

			// don't count items that are skipped (null data)
			if (element && !element._view.skip) {
				elements.push(element);
			}
		}
	});

	return elements;
}

/**
 * @interface IInteractionOptions
 */
/**
 * If true, only consider items that intersect the point
 * @name IInterfaceOptions#boolean
 * @type Boolean
 */

/**
 * Contains interaction related functions
 * @namespace Chart.Interaction
 */
module.exports = {
	// Helper function for different modes
	modes: {
		single: function(chart, e) {
			var position = getRelativePosition(e, chart);
			var elements = [];

			parseVisibleItems(chart, function(element) {
				if (element.inRange(position.x, position.y)) {
					elements.push(element);
					return elements;
				}
			});

			return elements.slice(0, 1);
		},

		/**
		 * @function Chart.Interaction.modes.label
		 * @deprecated since version 2.4.0
		 * @todo remove at version 3
		 * @private
		 */
		label: indexMode,

		/**
		 * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something
		 * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item
		 * @function Chart.Interaction.modes.index
		 * @since v2.4.0
		 * @param chart {chart} the chart we are returning items from
		 * @param e {Event} the event we are find things at
		 * @param options {IInteractionOptions} options to use during interaction
		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
		 */
		index: indexMode,

		/**
		 * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something
		 * If the options.intersect is false, we find the nearest item and return the items in that dataset
		 * @function Chart.Interaction.modes.dataset
		 * @param chart {chart} the chart we are returning items from
		 * @param e {Event} the event we are find things at
		 * @param options {IInteractionOptions} options to use during interaction
		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
		 */
		dataset: function(chart, e, options) {
			var position = getRelativePosition(e, chart);
			options.axis = options.axis || 'xy';
			var distanceMetric = getDistanceMetricForAxis(options.axis);
			var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);

			if (items.length > 0) {
				items = chart.getDatasetMeta(items[0]._datasetIndex).data;
			}

			return items;
		},

		/**
		 * @function Chart.Interaction.modes.x-axis
		 * @deprecated since version 2.4.0. Use index mode and intersect == true
		 * @todo remove at version 3
		 * @private
		 */
		'x-axis': function(chart, e) {
			return indexMode(chart, e, {intersect: true});
		},

		/**
		 * Point mode returns all elements that hit test based on the event position
		 * of the event
		 * @function Chart.Interaction.modes.intersect
		 * @param chart {chart} the chart we are returning items from
		 * @param e {Event} the event we are find things at
		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
		 */
		point: function(chart, e) {
			var position = getRelativePosition(e, chart);
			return getIntersectItems(chart, position);
		},

		/**
		 * nearest mode returns the element closest to the point
		 * @function Chart.Interaction.modes.intersect
		 * @param chart {chart} the chart we are returning items from
		 * @param e {Event} the event we are find things at
		 * @param options {IInteractionOptions} options to use
		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
		 */
		nearest: function(chart, e, options) {
			var position = getRelativePosition(e, chart);
			options.axis = options.axis || 'xy';
			var distanceMetric = getDistanceMetricForAxis(options.axis);
			var nearestItems = getNearestItems(chart, position, options.intersect, distanceMetric);

			// We have multiple items at the same distance from the event. Now sort by smallest
			if (nearestItems.length > 1) {
				nearestItems.sort(function(a, b) {
					var sizeA = a.getArea();
					var sizeB = b.getArea();
					var ret = sizeA - sizeB;

					if (ret === 0) {
						// if equal sort by dataset index
						ret = a._datasetIndex - b._datasetIndex;
					}

					return ret;
				});
			}

			// Return only 1 item
			return nearestItems.slice(0, 1);
		},

		/**
		 * x mode returns the elements that hit-test at the current x coordinate
		 * @function Chart.Interaction.modes.x
		 * @param chart {chart} the chart we are returning items from
		 * @param e {Event} the event we are find things at
		 * @param options {IInteractionOptions} options to use
		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
		 */
		x: function(chart, e, options) {
			var position = getRelativePosition(e, chart);
			var items = [];
			var intersectsItem = false;

			parseVisibleItems(chart, function(element) {
				if (element.inXRange(position.x)) {
					items.push(element);
				}

				if (element.inRange(position.x, position.y)) {
					intersectsItem = true;
				}
			});

			// If we want to trigger on an intersect and we don't have any items
			// that intersect the position, return nothing
			if (options.intersect && !intersectsItem) {
				items = [];
			}
			return items;
		},

		/**
		 * y mode returns the elements that hit-test at the current y coordinate
		 * @function Chart.Interaction.modes.y
		 * @param chart {chart} the chart we are returning items from
		 * @param e {Event} the event we are find things at
		 * @param options {IInteractionOptions} options to use
		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
		 */
		y: function(chart, e, options) {
			var position = getRelativePosition(e, chart);
			var items = [];
			var intersectsItem = false;

			parseVisibleItems(chart, function(element) {
				if (element.inYRange(position.y)) {
					items.push(element);
				}

				if (element.inRange(position.x, position.y)) {
					intersectsItem = true;
				}
			});

			// If we want to trigger on an intersect and we don't have any items
			// that intersect the position, return nothing
			if (options.intersect && !intersectsItem) {
				items = [];
			}
			return items;
		}
	}
};

},{"45":45}],29:[function(require,module,exports){
'use strict';

var defaults = require(25);

defaults._set('global', {
	responsive: true,
	responsiveAnimationDuration: 0,
	maintainAspectRatio: true,
	events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
	hover: {
		onHover: null,
		mode: 'nearest',
		intersect: true,
		animationDuration: 400
	},
	onClick: null,
	defaultColor: 'rgba(0,0,0,0.1)',
	defaultFontColor: '#666',
	defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
	defaultFontSize: 12,
	defaultFontStyle: 'normal',
	showLines: true,

	// Element defaults defined in element extensions
	elements: {},

	// Layout options such as padding
	layout: {
		padding: {
			top: 0,
			right: 0,
			bottom: 0,
			left: 0
		}
	}
});

module.exports = function() {

	// Occupy the global variable of Chart, and create a simple base class
	var Chart = function(item, config) {
		this.construct(item, config);
		return this;
	};

	Chart.Chart = Chart;

	return Chart;
};

},{"25":25}],30:[function(require,module,exports){
'use strict';

var helpers = require(45);

module.exports = function(Chart) {

	function filterByPosition(array, position) {
		return helpers.where(array, function(v) {
			return v.position === position;
		});
	}

	function sortByWeight(array, reverse) {
		array.forEach(function(v, i) {
			v._tmpIndex_ = i;
			return v;
		});
		array.sort(function(a, b) {
			var v0 = reverse ? b : a;
			var v1 = reverse ? a : b;
			return v0.weight === v1.weight ?
				v0._tmpIndex_ - v1._tmpIndex_ :
				v0.weight - v1.weight;
		});
		array.forEach(function(v) {
			delete v._tmpIndex_;
		});
	}

	/**
	 * @interface ILayoutItem
	 * @prop {String} position - The position of the item in the chart layout. Possible values are
	 * 'left', 'top', 'right', 'bottom', and 'chartArea'
	 * @prop {Number} weight - The weight used to sort the item. Higher weights are further away from the chart area
	 * @prop {Boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down
	 * @prop {Function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom)
	 * @prop {Function} update - Takes two parameters: width and height. Returns size of item
	 * @prop {Function} getPadding -  Returns an object with padding on the edges
	 * @prop {Number} width - Width of item. Must be valid after update()
	 * @prop {Number} height - Height of item. Must be valid after update()
	 * @prop {Number} left - Left edge of the item. Set by layout system and cannot be used in update
	 * @prop {Number} top - Top edge of the item. Set by layout system and cannot be used in update
	 * @prop {Number} right - Right edge of the item. Set by layout system and cannot be used in update
	 * @prop {Number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update
	 */

	// The layout service is very self explanatory.  It's responsible for the layout within a chart.
	// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need
	// It is this service's responsibility of carrying out that layout.
	Chart.layoutService = {
		defaults: {},

		/**
		 * Register a box to a chart.
		 * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title.
		 * @param {Chart} chart - the chart to use
		 * @param {ILayoutItem} item - the item to add to be layed out
		 */
		addBox: function(chart, item) {
			if (!chart.boxes) {
				chart.boxes = [];
			}

			// initialize item with default values
			item.fullWidth = item.fullWidth || false;
			item.position = item.position || 'top';
			item.weight = item.weight || 0;

			chart.boxes.push(item);
		},

		/**
		 * Remove a layoutItem from a chart
		 * @param {Chart} chart - the chart to remove the box from
		 * @param {Object} layoutItem - the item to remove from the layout
		 */
		removeBox: function(chart, layoutItem) {
			var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;
			if (index !== -1) {
				chart.boxes.splice(index, 1);
			}
		},

		/**
		 * Sets (or updates) options on the given `item`.
		 * @param {Chart} chart - the chart in which the item lives (or will be added to)
		 * @param {Object} item - the item to configure with the given options
		 * @param {Object} options - the new item options.
		 */
		configure: function(chart, item, options) {
			var props = ['fullWidth', 'position', 'weight'];
			var ilen = props.length;
			var i = 0;
			var prop;

			for (; i < ilen; ++i) {
				prop = props[i];
				if (options.hasOwnProperty(prop)) {
					item[prop] = options[prop];
				}
			}
		},

		/**
		 * Fits boxes of the given chart into the given size by having each box measure itself
		 * then running a fitting algorithm
		 * @param {Chart} chart - the chart
		 * @param {Number} width - the width to fit into
		 * @param {Number} height - the height to fit into
		 */
		update: function(chart, width, height) {
			if (!chart) {
				return;
			}

			var layoutOptions = chart.options.layout || {};
			var padding = helpers.options.toPadding(layoutOptions.padding);
			var leftPadding = padding.left;
			var rightPadding = padding.right;
			var topPadding = padding.top;
			var bottomPadding = padding.bottom;

			var leftBoxes = filterByPosition(chart.boxes, 'left');
			var rightBoxes = filterByPosition(chart.boxes, 'right');
			var topBoxes = filterByPosition(chart.boxes, 'top');
			var bottomBoxes = filterByPosition(chart.boxes, 'bottom');
			var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea');

			// Sort boxes by weight. A higher weight is further away from the chart area
			sortByWeight(leftBoxes, true);
			sortByWeight(rightBoxes, false);
			sortByWeight(topBoxes, true);
			sortByWeight(bottomBoxes, false);

			// Essentially we now have any number of boxes on each of the 4 sides.
			// Our canvas looks like the following.
			// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
			// B1 is the bottom axis
			// There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays
			// These locations are single-box locations only, when trying to register a chartArea location that is already taken,
			// an error will be thrown.
			//
			// |----------------------------------------------------|
			// |                  T1 (Full Width)                   |
			// |----------------------------------------------------|
			// |    |    |                 T2                  |    |
			// |    |----|-------------------------------------|----|
			// |    |    | C1 |                           | C2 |    |
			// |    |    |----|                           |----|    |
			// |    |    |                                     |    |
			// | L1 | L2 |           ChartArea (C0)            | R1 |
			// |    |    |                                     |    |
			// |    |    |----|                           |----|    |
			// |    |    | C3 |                           | C4 |    |
			// |    |----|-------------------------------------|----|
			// |    |    |                 B1                  |    |
			// |----------------------------------------------------|
			// |                  B2 (Full Width)                   |
			// |----------------------------------------------------|
			//
			// What we do to find the best sizing, we do the following
			// 1. Determine the minimum size of the chart area.
			// 2. Split the remaining width equally between each vertical axis
			// 3. Split the remaining height equally between each horizontal axis
			// 4. Give each layout the maximum size it can be. The layout will return it's minimum size
			// 5. Adjust the sizes of each axis based on it's minimum reported size.
			// 6. Refit each axis
			// 7. Position each axis in the final location
			// 8. Tell the chart the final location of the chart area
			// 9. Tell any axes that overlay the chart area the positions of the chart area

			// Step 1
			var chartWidth = width - leftPadding - rightPadding;
			var chartHeight = height - topPadding - bottomPadding;
			var chartAreaWidth = chartWidth / 2; // min 50%
			var chartAreaHeight = chartHeight / 2; // min 50%

			// Step 2
			var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length);

			// Step 3
			var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length);

			// Step 4
			var maxChartAreaWidth = chartWidth;
			var maxChartAreaHeight = chartHeight;
			var minBoxSizes = [];

			function getMinimumBoxSize(box) {
				var minSize;
				var isHorizontal = box.isHorizontal();

				if (isHorizontal) {
					minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight);
					maxChartAreaHeight -= minSize.height;
				} else {
					minSize = box.update(verticalBoxWidth, chartAreaHeight);
					maxChartAreaWidth -= minSize.width;
				}

				minBoxSizes.push({
					horizontal: isHorizontal,
					minSize: minSize,
					box: box,
				});
			}

			helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize);

			// If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478)
			var maxHorizontalLeftPadding = 0;
			var maxHorizontalRightPadding = 0;
			var maxVerticalTopPadding = 0;
			var maxVerticalBottomPadding = 0;

			helpers.each(topBoxes.concat(bottomBoxes), function(horizontalBox) {
				if (horizontalBox.getPadding) {
					var boxPadding = horizontalBox.getPadding();
					maxHorizontalLeftPadding = Math.max(maxHorizontalLeftPadding, boxPadding.left);
					maxHorizontalRightPadding = Math.max(maxHorizontalRightPadding, boxPadding.right);
				}
			});

			helpers.each(leftBoxes.concat(rightBoxes), function(verticalBox) {
				if (verticalBox.getPadding) {
					var boxPadding = verticalBox.getPadding();
					maxVerticalTopPadding = Math.max(maxVerticalTopPadding, boxPadding.top);
					maxVerticalBottomPadding = Math.max(maxVerticalBottomPadding, boxPadding.bottom);
				}
			});

			// At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could
			// be if the axes are drawn at their minimum sizes.
			// Steps 5 & 6
			var totalLeftBoxesWidth = leftPadding;
			var totalRightBoxesWidth = rightPadding;
			var totalTopBoxesHeight = topPadding;
			var totalBottomBoxesHeight = bottomPadding;

			// Function to fit a box
			function fitBox(box) {
				var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) {
					return minBox.box === box;
				});

				if (minBoxSize) {
					if (box.isHorizontal()) {
						var scaleMargin = {
							left: Math.max(totalLeftBoxesWidth, maxHorizontalLeftPadding),
							right: Math.max(totalRightBoxesWidth, maxHorizontalRightPadding),
							top: 0,
							bottom: 0
						};

						// Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends
						// on the margin. Sometimes they need to increase in size slightly
						box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin);
					} else {
						box.update(minBoxSize.minSize.width, maxChartAreaHeight);
					}
				}
			}

			// Update, and calculate the left and right margins for the horizontal boxes
			helpers.each(leftBoxes.concat(rightBoxes), fitBox);

			helpers.each(leftBoxes, function(box) {
				totalLeftBoxesWidth += box.width;
			});

			helpers.each(rightBoxes, function(box) {
				totalRightBoxesWidth += box.width;
			});

			// Set the Left and Right margins for the horizontal boxes
			helpers.each(topBoxes.concat(bottomBoxes), fitBox);

			// Figure out how much margin is on the top and bottom of the vertical boxes
			helpers.each(topBoxes, function(box) {
				totalTopBoxesHeight += box.height;
			});

			helpers.each(bottomBoxes, function(box) {
				totalBottomBoxesHeight += box.height;
			});

			function finalFitVerticalBox(box) {
				var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) {
					return minSize.box === box;
				});

				var scaleMargin = {
					left: 0,
					right: 0,
					top: totalTopBoxesHeight,
					bottom: totalBottomBoxesHeight
				};

				if (minBoxSize) {
					box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin);
				}
			}

			// Let the left layout know the final margin
			helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox);

			// Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance)
			totalLeftBoxesWidth = leftPadding;
			totalRightBoxesWidth = rightPadding;
			totalTopBoxesHeight = topPadding;
			totalBottomBoxesHeight = bottomPadding;

			helpers.each(leftBoxes, function(box) {
				totalLeftBoxesWidth += box.width;
			});

			helpers.each(rightBoxes, function(box) {
				totalRightBoxesWidth += box.width;
			});

			helpers.each(topBoxes, function(box) {
				totalTopBoxesHeight += box.height;
			});
			helpers.each(bottomBoxes, function(box) {
				totalBottomBoxesHeight += box.height;
			});

			// We may be adding some padding to account for rotated x axis labels
			var leftPaddingAddition = Math.max(maxHorizontalLeftPadding - totalLeftBoxesWidth, 0);
			totalLeftBoxesWidth += leftPaddingAddition;
			totalRightBoxesWidth += Math.max(maxHorizontalRightPadding - totalRightBoxesWidth, 0);

			var topPaddingAddition = Math.max(maxVerticalTopPadding - totalTopBoxesHeight, 0);
			totalTopBoxesHeight += topPaddingAddition;
			totalBottomBoxesHeight += Math.max(maxVerticalBottomPadding - totalBottomBoxesHeight, 0);

			// Figure out if our chart area changed. This would occur if the dataset layout label rotation
			// changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
			// without calling `fit` again
			var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight;
			var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth;

			if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) {
				helpers.each(leftBoxes, function(box) {
					box.height = newMaxChartAreaHeight;
				});

				helpers.each(rightBoxes, function(box) {
					box.height = newMaxChartAreaHeight;
				});

				helpers.each(topBoxes, function(box) {
					if (!box.fullWidth) {
						box.width = newMaxChartAreaWidth;
					}
				});

				helpers.each(bottomBoxes, function(box) {
					if (!box.fullWidth) {
						box.width = newMaxChartAreaWidth;
					}
				});

				maxChartAreaHeight = newMaxChartAreaHeight;
				maxChartAreaWidth = newMaxChartAreaWidth;
			}

			// Step 7 - Position the boxes
			var left = leftPadding + leftPaddingAddition;
			var top = topPadding + topPaddingAddition;

			function placeBox(box) {
				if (box.isHorizontal()) {
					box.left = box.fullWidth ? leftPadding : totalLeftBoxesWidth;
					box.right = box.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth;
					box.top = top;
					box.bottom = top + box.height;

					// Move to next point
					top = box.bottom;

				} else {

					box.left = left;
					box.right = left + box.width;
					box.top = totalTopBoxesHeight;
					box.bottom = totalTopBoxesHeight + maxChartAreaHeight;

					// Move to next point
					left = box.right;
				}
			}

			helpers.each(leftBoxes.concat(topBoxes), placeBox);

			// Account for chart width and height
			left += maxChartAreaWidth;
			top += maxChartAreaHeight;

			helpers.each(rightBoxes, placeBox);
			helpers.each(bottomBoxes, placeBox);

			// Step 8
			chart.chartArea = {
				left: totalLeftBoxesWidth,
				top: totalTopBoxesHeight,
				right: totalLeftBoxesWidth + maxChartAreaWidth,
				bottom: totalTopBoxesHeight + maxChartAreaHeight
			};

			// Step 9
			helpers.each(chartAreaBoxes, function(box) {
				box.left = chart.chartArea.left;
				box.top = chart.chartArea.top;
				box.right = chart.chartArea.right;
				box.bottom = chart.chartArea.bottom;

				box.update(maxChartAreaWidth, maxChartAreaHeight);
			});
		}
	};
};

},{"45":45}],31:[function(require,module,exports){
'use strict';

var defaults = require(25);
var Element = require(26);
var helpers = require(45);

defaults._set('global', {
	plugins: {}
});

module.exports = function(Chart) {

	/**
	 * The plugin service singleton
	 * @namespace Chart.plugins
	 * @since 2.1.0
	 */
	Chart.plugins = {
		/**
		 * Globally registered plugins.
		 * @private
		 */
		_plugins: [],

		/**
		 * This identifier is used to invalidate the descriptors cache attached to each chart
		 * when a global plugin is registered or unregistered. In this case, the cache ID is
		 * incremented and descriptors are regenerated during following API calls.
		 * @private
		 */
		_cacheId: 0,

		/**
		 * Registers the given plugin(s) if not already registered.
		 * @param {Array|Object} plugins plugin instance(s).
		 */
		register: function(plugins) {
			var p = this._plugins;
			([]).concat(plugins).forEach(function(plugin) {
				if (p.indexOf(plugin) === -1) {
					p.push(plugin);
				}
			});

			this._cacheId++;
		},

		/**
		 * Unregisters the given plugin(s) only if registered.
		 * @param {Array|Object} plugins plugin instance(s).
		 */
		unregister: function(plugins) {
			var p = this._plugins;
			([]).concat(plugins).forEach(function(plugin) {
				var idx = p.indexOf(plugin);
				if (idx !== -1) {
					p.splice(idx, 1);
				}
			});

			this._cacheId++;
		},

		/**
		 * Remove all registered plugins.
		 * @since 2.1.5
		 */
		clear: function() {
			this._plugins = [];
			this._cacheId++;
		},

		/**
		 * Returns the number of registered plugins?
		 * @returns {Number}
		 * @since 2.1.5
		 */
		count: function() {
			return this._plugins.length;
		},

		/**
		 * Returns all registered plugin instances.
		 * @returns {Array} array of plugin objects.
		 * @since 2.1.5
		 */
		getAll: function() {
			return this._plugins;
		},

		/**
		 * Calls enabled plugins for `chart` on the specified hook and with the given args.
		 * This method immediately returns as soon as a plugin explicitly returns false. The
		 * returned value can be used, for instance, to interrupt the current action.
		 * @param {Object} chart - The chart instance for which plugins should be called.
		 * @param {String} hook - The name of the plugin method to call (e.g. 'beforeUpdate').
		 * @param {Array} [args] - Extra arguments to apply to the hook call.
		 * @returns {Boolean} false if any of the plugins return false, else returns true.
		 */
		notify: function(chart, hook, args) {
			var descriptors = this.descriptors(chart);
			var ilen = descriptors.length;
			var i, descriptor, plugin, params, method;

			for (i = 0; i < ilen; ++i) {
				descriptor = descriptors[i];
				plugin = descriptor.plugin;
				method = plugin[hook];
				if (typeof method === 'function') {
					params = [chart].concat(args || []);
					params.push(descriptor.options);
					if (method.apply(plugin, params) === false) {
						return false;
					}
				}
			}

			return true;
		},

		/**
		 * Returns descriptors of enabled plugins for the given chart.
		 * @returns {Array} [{ plugin, options }]
		 * @private
		 */
		descriptors: function(chart) {
			var cache = chart._plugins || (chart._plugins = {});
			if (cache.id === this._cacheId) {
				return cache.descriptors;
			}

			var plugins = [];
			var descriptors = [];
			var config = (chart && chart.config) || {};
			var options = (config.options && config.options.plugins) || {};

			this._plugins.concat(config.plugins || []).forEach(function(plugin) {
				var idx = plugins.indexOf(plugin);
				if (idx !== -1) {
					return;
				}

				var id = plugin.id;
				var opts = options[id];
				if (opts === false) {
					return;
				}

				if (opts === true) {
					opts = helpers.clone(defaults.global.plugins[id]);
				}

				plugins.push(plugin);
				descriptors.push({
					plugin: plugin,
					options: opts || {}
				});
			});

			cache.descriptors = descriptors;
			cache.id = this._cacheId;
			return descriptors;
		}
	};

	/**
	 * Plugin extension hooks.
	 * @interface IPlugin
	 * @since 2.1.0
	 */
	/**
	 * @method IPlugin#beforeInit
	 * @desc Called before initializing `chart`.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#afterInit
	 * @desc Called after `chart` has been initialized and before the first update.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#beforeUpdate
	 * @desc Called before updating `chart`. If any plugin returns `false`, the update
	 * is cancelled (and thus subsequent render(s)) until another `update` is triggered.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 * @returns {Boolean} `false` to cancel the chart update.
	 */
	/**
	 * @method IPlugin#afterUpdate
	 * @desc Called after `chart` has been updated and before rendering. Note that this
	 * hook will not be called if the chart update has been previously cancelled.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#beforeDatasetsUpdate
 	 * @desc Called before updating the `chart` datasets. If any plugin returns `false`,
	 * the datasets update is cancelled until another `update` is triggered.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 * @returns {Boolean} false to cancel the datasets update.
	 * @since version 2.1.5
	 */
	/**
	 * @method IPlugin#afterDatasetsUpdate
	 * @desc Called after the `chart` datasets have been updated. Note that this hook
	 * will not be called if the datasets update has been previously cancelled.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 * @since version 2.1.5
	 */
	/**
	 * @method IPlugin#beforeDatasetUpdate
 	 * @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin
	 * returns `false`, the datasets update is cancelled until another `update` is triggered.
	 * @param {Chart} chart - The chart instance.
	 * @param {Object} args - The call arguments.
	 * @param {Number} args.index - The dataset index.
	 * @param {Object} args.meta - The dataset metadata.
	 * @param {Object} options - The plugin options.
	 * @returns {Boolean} `false` to cancel the chart datasets drawing.
	 */
	/**
	 * @method IPlugin#afterDatasetUpdate
 	 * @desc Called after the `chart` datasets at the given `args.index` has been updated. Note
	 * that this hook will not be called if the datasets update has been previously cancelled.
	 * @param {Chart} chart - The chart instance.
	 * @param {Object} args - The call arguments.
	 * @param {Number} args.index - The dataset index.
	 * @param {Object} args.meta - The dataset metadata.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#beforeLayout
	 * @desc Called before laying out `chart`. If any plugin returns `false`,
	 * the layout update is cancelled until another `update` is triggered.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 * @returns {Boolean} `false` to cancel the chart layout.
	 */
	/**
	 * @method IPlugin#afterLayout
	 * @desc Called after the `chart` has been layed out. Note that this hook will not
	 * be called if the layout update has been previously cancelled.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#beforeRender
	 * @desc Called before rendering `chart`. If any plugin returns `false`,
	 * the rendering is cancelled until another `render` is triggered.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 * @returns {Boolean} `false` to cancel the chart rendering.
	 */
	/**
	 * @method IPlugin#afterRender
	 * @desc Called after the `chart` has been fully rendered (and animation completed). Note
	 * that this hook will not be called if the rendering has been previously cancelled.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#beforeDraw
	 * @desc Called before drawing `chart` at every animation frame specified by the given
	 * easing value. If any plugin returns `false`, the frame drawing is cancelled until
	 * another `render` is triggered.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
	 * @param {Object} options - The plugin options.
	 * @returns {Boolean} `false` to cancel the chart drawing.
	 */
	/**
	 * @method IPlugin#afterDraw
	 * @desc Called after the `chart` has been drawn for the specific easing value. Note
	 * that this hook will not be called if the drawing has been previously cancelled.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#beforeDatasetsDraw
 	 * @desc Called before drawing the `chart` datasets. If any plugin returns `false`,
	 * the datasets drawing is cancelled until another `render` is triggered.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
	 * @param {Object} options - The plugin options.
	 * @returns {Boolean} `false` to cancel the chart datasets drawing.
	 */
	/**
	 * @method IPlugin#afterDatasetsDraw
	 * @desc Called after the `chart` datasets have been drawn. Note that this hook
	 * will not be called if the datasets drawing has been previously cancelled.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#beforeDatasetDraw
 	 * @desc Called before drawing the `chart` dataset at the given `args.index` (datasets
	 * are drawn in the reverse order). If any plugin returns `false`, the datasets drawing
	 * is cancelled until another `render` is triggered.
	 * @param {Chart} chart - The chart instance.
	 * @param {Object} args - The call arguments.
	 * @param {Number} args.index - The dataset index.
	 * @param {Object} args.meta - The dataset metadata.
	 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
	 * @param {Object} options - The plugin options.
	 * @returns {Boolean} `false` to cancel the chart datasets drawing.
	 */
	/**
	 * @method IPlugin#afterDatasetDraw
 	 * @desc Called after the `chart` datasets at the given `args.index` have been drawn
	 * (datasets are drawn in the reverse order). Note that this hook will not be called
	 * if the datasets drawing has been previously cancelled.
	 * @param {Chart} chart - The chart instance.
	 * @param {Object} args - The call arguments.
	 * @param {Number} args.index - The dataset index.
	 * @param {Object} args.meta - The dataset metadata.
	 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#beforeEvent
 	 * @desc Called before processing the specified `event`. If any plugin returns `false`,
	 * the event will be discarded.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {IEvent} event - The event object.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#afterEvent
	 * @desc Called after the `event` has been consumed. Note that this hook
	 * will not be called if the `event` has been previously discarded.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {IEvent} event - The event object.
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#resize
	 * @desc Called after the chart as been resized.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Number} size - The new canvas display size (eq. canvas.style width & height).
	 * @param {Object} options - The plugin options.
	 */
	/**
	 * @method IPlugin#destroy
	 * @desc Called after the chart as been destroyed.
	 * @param {Chart.Controller} chart - The chart instance.
	 * @param {Object} options - The plugin options.
	 */

	/**
	 * Provided for backward compatibility, use Chart.plugins instead
	 * @namespace Chart.pluginService
	 * @deprecated since version 2.1.5
	 * @todo remove at version 3
	 * @private
	 */
	Chart.pluginService = Chart.plugins;

	/**
	 * Provided for backward compatibility, inheriting from Chart.PlugingBase has no
	 * effect, instead simply create/register plugins via plain JavaScript objects.
	 * @interface Chart.PluginBase
	 * @deprecated since version 2.5.0
	 * @todo remove at version 3
	 * @private
	 */
	Chart.PluginBase = Element.extend({});
};

},{"25":25,"26":26,"45":45}],32:[function(require,module,exports){
'use strict';

var defaults = require(25);
var Element = require(26);
var helpers = require(45);
var Ticks = require(34);

defaults._set('scale', {
	display: true,
	position: 'left',
	offset: false,

	// grid line settings
	gridLines: {
		display: true,
		color: 'rgba(0, 0, 0, 0.1)',
		lineWidth: 1,
		drawBorder: true,
		drawOnChartArea: true,
		drawTicks: true,
		tickMarkLength: 10,
		zeroLineWidth: 1,
		zeroLineColor: 'rgba(0,0,0,0.25)',
		zeroLineBorderDash: [],
		zeroLineBorderDashOffset: 0.0,
		offsetGridLines: false,
		borderDash: [],
		borderDashOffset: 0.0
	},

	// scale label
	scaleLabel: {
		// display property
		display: false,

		// actual label
		labelString: '',

		// line height
		lineHeight: 1.2,

		// top/bottom padding
		padding: {
			top: 4,
			bottom: 4
		}
	},

	// label settings
	ticks: {
		beginAtZero: false,
		minRotation: 0,
		maxRotation: 50,
		mirror: false,
		padding: 0,
		reverse: false,
		display: true,
		autoSkip: true,
		autoSkipPadding: 0,
		labelOffset: 0,
		// We pass through arrays to be rendered as multiline labels, we convert Others to strings here.
		callback: Ticks.formatters.values,
		minor: {},
		major: {}
	}
});

function labelsFromTicks(ticks) {
	var labels = [];
	var i, ilen;

	for (i = 0, ilen = ticks.length; i < ilen; ++i) {
		labels.push(ticks[i].label);
	}

	return labels;
}

function getLineValue(scale, index, offsetGridLines) {
	var lineValue = scale.getPixelForTick(index);

	if (offsetGridLines) {
		if (index === 0) {
			lineValue -= (scale.getPixelForTick(1) - lineValue) / 2;
		} else {
			lineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2;
		}
	}
	return lineValue;
}

module.exports = function(Chart) {

	function computeTextSize(context, tick, font) {
		return helpers.isArray(tick) ?
			helpers.longestText(context, font, tick) :
			context.measureText(tick).width;
	}

	function parseFontOptions(options) {
		var valueOrDefault = helpers.valueOrDefault;
		var globalDefaults = defaults.global;
		var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize);
		var style = valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle);
		var family = valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily);

		return {
			size: size,
			style: style,
			family: family,
			font: helpers.fontString(size, style, family)
		};
	}

	function parseLineHeight(options) {
		return helpers.options.toLineHeight(
			helpers.valueOrDefault(options.lineHeight, 1.2),
			helpers.valueOrDefault(options.fontSize, defaults.global.defaultFontSize));
	}

	Chart.Scale = Element.extend({
		/**
		 * Get the padding needed for the scale
		 * @method getPadding
		 * @private
		 * @returns {Padding} the necessary padding
		 */
		getPadding: function() {
			var me = this;
			return {
				left: me.paddingLeft || 0,
				top: me.paddingTop || 0,
				right: me.paddingRight || 0,
				bottom: me.paddingBottom || 0
			};
		},

		/**
		 * Returns the scale tick objects ({label, major})
		 * @since 2.7
		 */
		getTicks: function() {
			return this._ticks;
		},

		// These methods are ordered by lifecyle. Utilities then follow.
		// Any function defined here is inherited by all scale types.
		// Any function can be extended by the scale type

		mergeTicksOptions: function() {
			var ticks = this.options.ticks;
			if (ticks.minor === false) {
				ticks.minor = {
					display: false
				};
			}
			if (ticks.major === false) {
				ticks.major = {
					display: false
				};
			}
			for (var key in ticks) {
				if (key !== 'major' && key !== 'minor') {
					if (typeof ticks.minor[key] === 'undefined') {
						ticks.minor[key] = ticks[key];
					}
					if (typeof ticks.major[key] === 'undefined') {
						ticks.major[key] = ticks[key];
					}
				}
			}
		},
		beforeUpdate: function() {
			helpers.callback(this.options.beforeUpdate, [this]);
		},
		update: function(maxWidth, maxHeight, margins) {
			var me = this;
			var i, ilen, labels, label, ticks, tick;

			// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
			me.beforeUpdate();

			// Absorb the master measurements
			me.maxWidth = maxWidth;
			me.maxHeight = maxHeight;
			me.margins = helpers.extend({
				left: 0,
				right: 0,
				top: 0,
				bottom: 0
			}, margins);
			me.longestTextCache = me.longestTextCache || {};

			// Dimensions
			me.beforeSetDimensions();
			me.setDimensions();
			me.afterSetDimensions();

			// Data min/max
			me.beforeDataLimits();
			me.determineDataLimits();
			me.afterDataLimits();

			// Ticks - `this.ticks` is now DEPRECATED!
			// Internal ticks are now stored as objects in the PRIVATE `this._ticks` member
			// and must not be accessed directly from outside this class. `this.ticks` being
			// around for long time and not marked as private, we can't change its structure
			// without unexpected breaking changes. If you need to access the scale ticks,
			// use scale.getTicks() instead.

			me.beforeBuildTicks();

			// New implementations should return an array of objects but for BACKWARD COMPAT,
			// we still support no return (`this.ticks` internally set by calling this method).
			ticks = me.buildTicks() || [];

			me.afterBuildTicks();

			me.beforeTickToLabelConversion();

			// New implementations should return the formatted tick labels but for BACKWARD
			// COMPAT, we still support no return (`this.ticks` internally changed by calling
			// this method and supposed to contain only string values).
			labels = me.convertTicksToLabels(ticks) || me.ticks;

			me.afterTickToLabelConversion();

			me.ticks = labels;   // BACKWARD COMPATIBILITY

			// IMPORTANT: from this point, we consider that `this.ticks` will NEVER change!

			// BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`)
			for (i = 0, ilen = labels.length; i < ilen; ++i) {
				label = labels[i];
				tick = ticks[i];
				if (!tick) {
					ticks.push(tick = {
						label: label,
						major: false
					});
				} else {
					tick.label = label;
				}
			}

			me._ticks = ticks;

			// Tick Rotation
			me.beforeCalculateTickRotation();
			me.calculateTickRotation();
			me.afterCalculateTickRotation();
			// Fit
			me.beforeFit();
			me.fit();
			me.afterFit();
			//
			me.afterUpdate();

			return me.minSize;

		},
		afterUpdate: function() {
			helpers.callback(this.options.afterUpdate, [this]);
		},

		//

		beforeSetDimensions: function() {
			helpers.callback(this.options.beforeSetDimensions, [this]);
		},
		setDimensions: function() {
			var me = this;
			// Set the unconstrained dimension before label rotation
			if (me.isHorizontal()) {
				// Reset position before calculating rotation
				me.width = me.maxWidth;
				me.left = 0;
				me.right = me.width;
			} else {
				me.height = me.maxHeight;

				// Reset position before calculating rotation
				me.top = 0;
				me.bottom = me.height;
			}

			// Reset padding
			me.paddingLeft = 0;
			me.paddingTop = 0;
			me.paddingRight = 0;
			me.paddingBottom = 0;
		},
		afterSetDimensions: function() {
			helpers.callback(this.options.afterSetDimensions, [this]);
		},

		// Data limits
		beforeDataLimits: function() {
			helpers.callback(this.options.beforeDataLimits, [this]);
		},
		determineDataLimits: helpers.noop,
		afterDataLimits: function() {
			helpers.callback(this.options.afterDataLimits, [this]);
		},

		//
		beforeBuildTicks: function() {
			helpers.callback(this.options.beforeBuildTicks, [this]);
		},
		buildTicks: helpers.noop,
		afterBuildTicks: function() {
			helpers.callback(this.options.afterBuildTicks, [this]);
		},

		beforeTickToLabelConversion: function() {
			helpers.callback(this.options.beforeTickToLabelConversion, [this]);
		},
		convertTicksToLabels: function() {
			var me = this;
			// Convert ticks to strings
			var tickOpts = me.options.ticks;
			me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this);
		},
		afterTickToLabelConversion: function() {
			helpers.callback(this.options.afterTickToLabelConversion, [this]);
		},

		//

		beforeCalculateTickRotation: function() {
			helpers.callback(this.options.beforeCalculateTickRotation, [this]);
		},
		calculateTickRotation: function() {
			var me = this;
			var context = me.ctx;
			var tickOpts = me.options.ticks;
			var labels = labelsFromTicks(me._ticks);

			// Get the width of each grid by calculating the difference
			// between x offsets between 0 and 1.
			var tickFont = parseFontOptions(tickOpts);
			context.font = tickFont.font;

			var labelRotation = tickOpts.minRotation || 0;

			if (labels.length && me.options.display && me.isHorizontal()) {
				var originalLabelWidth = helpers.longestText(context, tickFont.font, labels, me.longestTextCache);
				var labelWidth = originalLabelWidth;
				var cosRotation, sinRotation;

				// Allow 3 pixels x2 padding either side for label readability
				var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6;

				// Max label rotation can be set or default to 90 - also act as a loop counter
				while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) {
					var angleRadians = helpers.toRadians(labelRotation);
					cosRotation = Math.cos(angleRadians);
					sinRotation = Math.sin(angleRadians);

					if (sinRotation * originalLabelWidth > me.maxHeight) {
						// go back one step
						labelRotation--;
						break;
					}

					labelRotation++;
					labelWidth = cosRotation * originalLabelWidth;
				}
			}

			me.labelRotation = labelRotation;
		},
		afterCalculateTickRotation: function() {
			helpers.callback(this.options.afterCalculateTickRotation, [this]);
		},

		//

		beforeFit: function() {
			helpers.callback(this.options.beforeFit, [this]);
		},
		fit: function() {
			var me = this;
			// Reset
			var minSize = me.minSize = {
				width: 0,
				height: 0
			};

			var labels = labelsFromTicks(me._ticks);

			var opts = me.options;
			var tickOpts = opts.ticks;
			var scaleLabelOpts = opts.scaleLabel;
			var gridLineOpts = opts.gridLines;
			var display = opts.display;
			var isHorizontal = me.isHorizontal();

			var tickFont = parseFontOptions(tickOpts);
			var tickMarkLength = opts.gridLines.tickMarkLength;

			// Width
			if (isHorizontal) {
				// subtract the margins to line up with the chartArea if we are a full width scale
				minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth;
			} else {
				minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
			}

			// height
			if (isHorizontal) {
				minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
			} else {
				minSize.height = me.maxHeight; // fill all the height
			}

			// Are we showing a title for the scale?
			if (scaleLabelOpts.display && display) {
				var scaleLabelLineHeight = parseLineHeight(scaleLabelOpts);
				var scaleLabelPadding = helpers.options.toPadding(scaleLabelOpts.padding);
				var deltaHeight = scaleLabelLineHeight + scaleLabelPadding.height;

				if (isHorizontal) {
					minSize.height += deltaHeight;
				} else {
					minSize.width += deltaHeight;
				}
			}

			// Don't bother fitting the ticks if we are not showing them
			if (tickOpts.display && display) {
				var largestTextWidth = helpers.longestText(me.ctx, tickFont.font, labels, me.longestTextCache);
				var tallestLabelHeightInLines = helpers.numberOfLabelLines(labels);
				var lineSpace = tickFont.size * 0.5;
				var tickPadding = me.options.ticks.padding;

				if (isHorizontal) {
					// A horizontal axis is more constrained by the height.
					me.longestLabelWidth = largestTextWidth;

					var angleRadians = helpers.toRadians(me.labelRotation);
					var cosRotation = Math.cos(angleRadians);
					var sinRotation = Math.sin(angleRadians);

					// TODO - improve this calculation
					var labelHeight = (sinRotation * largestTextWidth)
						+ (tickFont.size * tallestLabelHeightInLines)
						+ (lineSpace * (tallestLabelHeightInLines - 1))
						+ lineSpace; // padding

					minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding);

					me.ctx.font = tickFont.font;
					var firstLabelWidth = computeTextSize(me.ctx, labels[0], tickFont.font);
					var lastLabelWidth = computeTextSize(me.ctx, labels[labels.length - 1], tickFont.font);

					// Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned
					// which means that the right padding is dominated by the font height
					if (me.labelRotation !== 0) {
						me.paddingLeft = opts.position === 'bottom' ? (cosRotation * firstLabelWidth) + 3 : (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges
						me.paddingRight = opts.position === 'bottom' ? (cosRotation * lineSpace) + 3 : (cosRotation * lastLabelWidth) + 3;
					} else {
						me.paddingLeft = firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges
						me.paddingRight = lastLabelWidth / 2 + 3;
					}
				} else {
					// A vertical axis is more constrained by the width. Labels are the
					// dominant factor here, so get that length first and account for padding
					if (tickOpts.mirror) {
						largestTextWidth = 0;
					} else {
						// use lineSpace for consistency with horizontal axis
						// tickPadding is not implemented for horizontal
						largestTextWidth += tickPadding + lineSpace;
					}

					minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth);

					me.paddingTop = tickFont.size / 2;
					me.paddingBottom = tickFont.size / 2;
				}
			}

			me.handleMargins();

			me.width = minSize.width;
			me.height = minSize.height;
		},

		/**
		 * Handle margins and padding interactions
		 * @private
		 */
		handleMargins: function() {
			var me = this;
			if (me.margins) {
				me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0);
				me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0);
				me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0);
				me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0);
			}
		},

		afterFit: function() {
			helpers.callback(this.options.afterFit, [this]);
		},

		// Shared Methods
		isHorizontal: function() {
			return this.options.position === 'top' || this.options.position === 'bottom';
		},
		isFullWidth: function() {
			return (this.options.fullWidth);
		},

		// Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not
		getRightValue: function(rawValue) {
			// Null and undefined values first
			if (helpers.isNullOrUndef(rawValue)) {
				return NaN;
			}
			// isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values
			if (typeof rawValue === 'number' && !isFinite(rawValue)) {
				return NaN;
			}
			// If it is in fact an object, dive in one more level
			if (rawValue) {
				if (this.isHorizontal()) {
					if (rawValue.x !== undefined) {
						return this.getRightValue(rawValue.x);
					}
				} else if (rawValue.y !== undefined) {
					return this.getRightValue(rawValue.y);
				}
			}

			// Value is good, return it
			return rawValue;
		},

		// Used to get the value to display in the tooltip for the data at the given index
		// function getLabelForIndex(index, datasetIndex)
		getLabelForIndex: helpers.noop,

		// Used to get data value locations.  Value can either be an index or a numerical value
		getPixelForValue: helpers.noop,

		// Used to get the data value from a given pixel. This is the inverse of getPixelForValue
		getValueForPixel: helpers.noop,

		// Used for tick location, should
		getPixelForTick: function(index) {
			var me = this;
			var offset = me.options.offset;
			if (me.isHorizontal()) {
				var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
				var tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
				var pixel = (tickWidth * index) + me.paddingLeft;

				if (offset) {
					pixel += tickWidth / 2;
				}

				var finalVal = me.left + Math.round(pixel);
				finalVal += me.isFullWidth() ? me.margins.left : 0;
				return finalVal;
			}
			var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
			return me.top + (index * (innerHeight / (me._ticks.length - 1)));
		},

		// Utility for getting the pixel location of a percentage of scale
		getPixelForDecimal: function(decimal) {
			var me = this;
			if (me.isHorizontal()) {
				var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
				var valueOffset = (innerWidth * decimal) + me.paddingLeft;

				var finalVal = me.left + Math.round(valueOffset);
				finalVal += me.isFullWidth() ? me.margins.left : 0;
				return finalVal;
			}
			return me.top + (decimal * me.height);
		},

		getBasePixel: function() {
			return this.getPixelForValue(this.getBaseValue());
		},

		getBaseValue: function() {
			var me = this;
			var min = me.min;
			var max = me.max;

			return me.beginAtZero ? 0 :
				min < 0 && max < 0 ? max :
				min > 0 && max > 0 ? min :
				0;
		},

		/**
		 * Returns a subset of ticks to be plotted to avoid overlapping labels.
		 * @private
		 */
		_autoSkip: function(ticks) {
			var skipRatio;
			var me = this;
			var isHorizontal = me.isHorizontal();
			var optionTicks = me.options.ticks.minor;
			var tickCount = ticks.length;
			var labelRotationRadians = helpers.toRadians(me.labelRotation);
			var cosRotation = Math.cos(labelRotationRadians);
			var longestRotatedLabel = me.longestLabelWidth * cosRotation;
			var result = [];
			var i, tick, shouldSkip;

			// figure out the maximum number of gridlines to show
			var maxTicks;
			if (optionTicks.maxTicksLimit) {
				maxTicks = optionTicks.maxTicksLimit;
			}

			if (isHorizontal) {
				skipRatio = false;

				if ((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount > (me.width - (me.paddingLeft + me.paddingRight))) {
					skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount) / (me.width - (me.paddingLeft + me.paddingRight)));
				}

				// if they defined a max number of optionTicks,
				// increase skipRatio until that number is met
				if (maxTicks && tickCount > maxTicks) {
					skipRatio = Math.max(skipRatio, Math.floor(tickCount / maxTicks));
				}
			}

			for (i = 0; i < tickCount; i++) {
				tick = ticks[i];

				// Since we always show the last tick,we need may need to hide the last shown one before
				shouldSkip = (skipRatio > 1 && i % skipRatio > 0) || (i % skipRatio === 0 && i + skipRatio >= tickCount);
				if (shouldSkip && i !== tickCount - 1 || helpers.isNullOrUndef(tick.label)) {
					// leave tick in place but make sure it's not displayed (#4635)
					delete tick.label;
				}
				result.push(tick);
			}
			return result;
		},

		// Actually draw the scale on the canvas
		// @param {rectangle} chartArea : the area of the chart to draw full grid lines on
		draw: function(chartArea) {
			var me = this;
			var options = me.options;
			if (!options.display) {
				return;
			}

			var context = me.ctx;
			var globalDefaults = defaults.global;
			var optionTicks = options.ticks.minor;
			var optionMajorTicks = options.ticks.major || optionTicks;
			var gridLines = options.gridLines;
			var scaleLabel = options.scaleLabel;

			var isRotated = me.labelRotation !== 0;
			var isHorizontal = me.isHorizontal();

			var ticks = optionTicks.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks();
			var tickFontColor = helpers.valueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor);
			var tickFont = parseFontOptions(optionTicks);
			var majorTickFontColor = helpers.valueOrDefault(optionMajorTicks.fontColor, globalDefaults.defaultFontColor);
			var majorTickFont = parseFontOptions(optionMajorTicks);

			var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0;

			var scaleLabelFontColor = helpers.valueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor);
			var scaleLabelFont = parseFontOptions(scaleLabel);
			var scaleLabelPadding = helpers.options.toPadding(scaleLabel.padding);
			var labelRotationRadians = helpers.toRadians(me.labelRotation);

			var itemsToDraw = [];

			var xTickStart = options.position === 'right' ? me.left : me.right - tl;
			var xTickEnd = options.position === 'right' ? me.left + tl : me.right;
			var yTickStart = options.position === 'bottom' ? me.top : me.bottom - tl;
			var yTickEnd = options.position === 'bottom' ? me.top + tl : me.bottom;

			helpers.each(ticks, function(tick, index) {
				// autoskipper skipped this tick (#4635)
				if (tick.label === undefined) {
					return;
				}

				var label = tick.label;
				var lineWidth, lineColor, borderDash, borderDashOffset;
				if (index === me.zeroLineIndex && options.offset === gridLines.offsetGridLines) {
					// Draw the first index specially
					lineWidth = gridLines.zeroLineWidth;
					lineColor = gridLines.zeroLineColor;
					borderDash = gridLines.zeroLineBorderDash;
					borderDashOffset = gridLines.zeroLineBorderDashOffset;
				} else {
					lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, index);
					lineColor = helpers.valueAtIndexOrDefault(gridLines.color, index);
					borderDash = helpers.valueOrDefault(gridLines.borderDash, globalDefaults.borderDash);
					borderDashOffset = helpers.valueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset);
				}

				// Common properties
				var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY;
				var textAlign = 'middle';
				var textBaseline = 'middle';
				var tickPadding = optionTicks.padding;

				if (isHorizontal) {
					var labelYOffset = tl + tickPadding;

					if (options.position === 'bottom') {
						// bottom
						textBaseline = !isRotated ? 'top' : 'middle';
						textAlign = !isRotated ? 'center' : 'right';
						labelY = me.top + labelYOffset;
					} else {
						// top
						textBaseline = !isRotated ? 'bottom' : 'middle';
						textAlign = !isRotated ? 'center' : 'left';
						labelY = me.bottom - labelYOffset;
					}

					var xLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1);
					if (xLineValue < me.left) {
						lineColor = 'rgba(0,0,0,0)';
					}
					xLineValue += helpers.aliasPixel(lineWidth);

					labelX = me.getPixelForTick(index) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option)

					tx1 = tx2 = x1 = x2 = xLineValue;
					ty1 = yTickStart;
					ty2 = yTickEnd;
					y1 = chartArea.top;
					y2 = chartArea.bottom;
				} else {
					var isLeft = options.position === 'left';
					var labelXOffset;

					if (optionTicks.mirror) {
						textAlign = isLeft ? 'left' : 'right';
						labelXOffset = tickPadding;
					} else {
						textAlign = isLeft ? 'right' : 'left';
						labelXOffset = tl + tickPadding;
					}

					labelX = isLeft ? me.right - labelXOffset : me.left + labelXOffset;

					var yLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1);
					if (yLineValue < me.top) {
						lineColor = 'rgba(0,0,0,0)';
					}
					yLineValue += helpers.aliasPixel(lineWidth);

					labelY = me.getPixelForTick(index) + optionTicks.labelOffset;

					tx1 = xTickStart;
					tx2 = xTickEnd;
					x1 = chartArea.left;
					x2 = chartArea.right;
					ty1 = ty2 = y1 = y2 = yLineValue;
				}

				itemsToDraw.push({
					tx1: tx1,
					ty1: ty1,
					tx2: tx2,
					ty2: ty2,
					x1: x1,
					y1: y1,
					x2: x2,
					y2: y2,
					labelX: labelX,
					labelY: labelY,
					glWidth: lineWidth,
					glColor: lineColor,
					glBorderDash: borderDash,
					glBorderDashOffset: borderDashOffset,
					rotation: -1 * labelRotationRadians,
					label: label,
					major: tick.major,
					textBaseline: textBaseline,
					textAlign: textAlign
				});
			});

			// Draw all of the tick labels, tick marks, and grid lines at the correct places
			helpers.each(itemsToDraw, function(itemToDraw) {
				if (gridLines.display) {
					context.save();
					context.lineWidth = itemToDraw.glWidth;
					context.strokeStyle = itemToDraw.glColor;
					if (context.setLineDash) {
						context.setLineDash(itemToDraw.glBorderDash);
						context.lineDashOffset = itemToDraw.glBorderDashOffset;
					}

					context.beginPath();

					if (gridLines.drawTicks) {
						context.moveTo(itemToDraw.tx1, itemToDraw.ty1);
						context.lineTo(itemToDraw.tx2, itemToDraw.ty2);
					}

					if (gridLines.drawOnChartArea) {
						context.moveTo(itemToDraw.x1, itemToDraw.y1);
						context.lineTo(itemToDraw.x2, itemToDraw.y2);
					}

					context.stroke();
					context.restore();
				}

				if (optionTicks.display) {
					// Make sure we draw text in the correct color and font
					context.save();
					context.translate(itemToDraw.labelX, itemToDraw.labelY);
					context.rotate(itemToDraw.rotation);
					context.font = itemToDraw.major ? majorTickFont.font : tickFont.font;
					context.fillStyle = itemToDraw.major ? majorTickFontColor : tickFontColor;
					context.textBaseline = itemToDraw.textBaseline;
					context.textAlign = itemToDraw.textAlign;

					var label = itemToDraw.label;
					if (helpers.isArray(label)) {
						for (var i = 0, y = 0; i < label.length; ++i) {
							// We just make sure the multiline element is a string here..
							context.fillText('' + label[i], 0, y);
							// apply same lineSpacing as calculated @ L#320
							y += (tickFont.size * 1.5);
						}
					} else {
						context.fillText(label, 0, 0);
					}
					context.restore();
				}
			});

			if (scaleLabel.display) {
				// Draw the scale label
				var scaleLabelX;
				var scaleLabelY;
				var rotation = 0;
				var halfLineHeight = parseLineHeight(scaleLabel) / 2;

				if (isHorizontal) {
					scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width
					scaleLabelY = options.position === 'bottom'
						? me.bottom - halfLineHeight - scaleLabelPadding.bottom
						: me.top + halfLineHeight + scaleLabelPadding.top;
				} else {
					var isLeft = options.position === 'left';
					scaleLabelX = isLeft
						? me.left + halfLineHeight + scaleLabelPadding.top
						: me.right - halfLineHeight - scaleLabelPadding.top;
					scaleLabelY = me.top + ((me.bottom - me.top) / 2);
					rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
				}

				context.save();
				context.translate(scaleLabelX, scaleLabelY);
				context.rotate(rotation);
				context.textAlign = 'center';
				context.textBaseline = 'middle';
				context.fillStyle = scaleLabelFontColor; // render in correct colour
				context.font = scaleLabelFont.font;
				context.fillText(scaleLabel.labelString, 0, 0);
				context.restore();
			}

			if (gridLines.drawBorder) {
				// Draw the line at the edge of the axis
				context.lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, 0);
				context.strokeStyle = helpers.valueAtIndexOrDefault(gridLines.color, 0);
				var x1 = me.left;
				var x2 = me.right;
				var y1 = me.top;
				var y2 = me.bottom;

				var aliasPixel = helpers.aliasPixel(context.lineWidth);
				if (isHorizontal) {
					y1 = y2 = options.position === 'top' ? me.bottom : me.top;
					y1 += aliasPixel;
					y2 += aliasPixel;
				} else {
					x1 = x2 = options.position === 'left' ? me.right : me.left;
					x1 += aliasPixel;
					x2 += aliasPixel;
				}

				context.beginPath();
				context.moveTo(x1, y1);
				context.lineTo(x2, y2);
				context.stroke();
			}
		}
	});
};

},{"25":25,"26":26,"34":34,"45":45}],33:[function(require,module,exports){
'use strict';

var defaults = require(25);
var helpers = require(45);

module.exports = function(Chart) {

	Chart.scaleService = {
		// Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
		// use the new chart options to grab the correct scale
		constructors: {},
		// Use a registration function so that we can move to an ES6 map when we no longer need to support
		// old browsers

		// Scale config defaults
		defaults: {},
		registerScaleType: function(type, scaleConstructor, scaleDefaults) {
			this.constructors[type] = scaleConstructor;
			this.defaults[type] = helpers.clone(scaleDefaults);
		},
		getScaleConstructor: function(type) {
			return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
		},
		getScaleDefaults: function(type) {
			// Return the scale defaults merged with the global settings so that we always use the latest ones
			return this.defaults.hasOwnProperty(type) ? helpers.merge({}, [defaults.scale, this.defaults[type]]) : {};
		},
		updateScaleDefaults: function(type, additions) {
			var me = this;
			if (me.defaults.hasOwnProperty(type)) {
				me.defaults[type] = helpers.extend(me.defaults[type], additions);
			}
		},
		addScalesToLayout: function(chart) {
			// Adds each scale to the chart.boxes array to be sized accordingly
			helpers.each(chart.scales, function(scale) {
				// Set ILayoutItem parameters for backwards compatibility
				scale.fullWidth = scale.options.fullWidth;
				scale.position = scale.options.position;
				scale.weight = scale.options.weight;
				Chart.layoutService.addBox(chart, scale);
			});
		}
	};
};

},{"25":25,"45":45}],34:[function(require,module,exports){
'use strict';

var helpers = require(45);

/**
 * Namespace to hold static tick generation functions
 * @namespace Chart.Ticks
 */
module.exports = {
	/**
	 * Namespace to hold generators for different types of ticks
	 * @namespace Chart.Ticks.generators
	 */
	generators: {
		/**
		 * Interface for the options provided to the numeric tick generator
		 * @interface INumericTickGenerationOptions
		 */
		/**
		 * The maximum number of ticks to display
		 * @name INumericTickGenerationOptions#maxTicks
		 * @type Number
		 */
		/**
		 * The distance between each tick.
		 * @name INumericTickGenerationOptions#stepSize
		 * @type Number
		 * @optional
		 */
		/**
		 * Forced minimum for the ticks. If not specified, the minimum of the data range is used to calculate the tick minimum
		 * @name INumericTickGenerationOptions#min
		 * @type Number
		 * @optional
		 */
		/**
		 * The maximum value of the ticks. If not specified, the maximum of the data range is used to calculate the tick maximum
		 * @name INumericTickGenerationOptions#max
		 * @type Number
		 * @optional
		 */

		/**
		 * Generate a set of linear ticks
		 * @method Chart.Ticks.generators.linear
		 * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks
		 * @param dataRange {IRange} the range of the data
		 * @returns {Array<Number>} array of tick values
		 */
		linear: function(generationOptions, dataRange) {
			var ticks = [];
			// To get a "nice" value for the tick spacing, we will use the appropriately named
			// "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
			// for details.

			var spacing;
			if (generationOptions.stepSize && generationOptions.stepSize > 0) {
				spacing = generationOptions.stepSize;
			} else {
				var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false);
				spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true);
			}
			var niceMin = Math.floor(dataRange.min / spacing) * spacing;
			var niceMax = Math.ceil(dataRange.max / spacing) * spacing;

			// If min, max and stepSize is set and they make an evenly spaced scale use it.
			if (generationOptions.min && generationOptions.max && generationOptions.stepSize) {
				// If very close to our whole number, use it.
				if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) {
					niceMin = generationOptions.min;
					niceMax = generationOptions.max;
				}
			}

			var numSpaces = (niceMax - niceMin) / spacing;
			// If very close to our rounded value, use it.
			if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
				numSpaces = Math.round(numSpaces);
			} else {
				numSpaces = Math.ceil(numSpaces);
			}

			// Put the values into the ticks array
			ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin);
			for (var j = 1; j < numSpaces; ++j) {
				ticks.push(niceMin + (j * spacing));
			}
			ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax);

			return ticks;
		},

		/**
		 * Generate a set of logarithmic ticks
		 * @method Chart.Ticks.generators.logarithmic
		 * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks
		 * @param dataRange {IRange} the range of the data
		 * @returns {Array<Number>} array of tick values
		 */
		logarithmic: function(generationOptions, dataRange) {
			var ticks = [];
			var valueOrDefault = helpers.valueOrDefault;

			// Figure out what the max number of ticks we can support it is based on the size of
			// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
			// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
			// the graph
			var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min))));

			var endExp = Math.floor(helpers.log10(dataRange.max));
			var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
			var exp, significand;

			if (tickVal === 0) {
				exp = Math.floor(helpers.log10(dataRange.minNotZero));
				significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp));

				ticks.push(tickVal);
				tickVal = significand * Math.pow(10, exp);
			} else {
				exp = Math.floor(helpers.log10(tickVal));
				significand = Math.floor(tickVal / Math.pow(10, exp));
			}

			do {
				ticks.push(tickVal);

				++significand;
				if (significand === 10) {
					significand = 1;
					++exp;
				}

				tickVal = significand * Math.pow(10, exp);
			} while (exp < endExp || (exp === endExp && significand < endSignificand));

			var lastTick = valueOrDefault(generationOptions.max, tickVal);
			ticks.push(lastTick);

			return ticks;
		}
	},

	/**
	 * Namespace to hold formatters for different types of ticks
	 * @namespace Chart.Ticks.formatters
	 */
	formatters: {
		/**
		 * Formatter for value labels
		 * @method Chart.Ticks.formatters.values
		 * @param value the value to display
		 * @return {String|Array} the label to display
		 */
		values: function(value) {
			return helpers.isArray(value) ? value : '' + value;
		},

		/**
		 * Formatter for linear numeric ticks
		 * @method Chart.Ticks.formatters.linear
		 * @param tickValue {Number} the value to be formatted
		 * @param index {Number} the position of the tickValue parameter in the ticks array
		 * @param ticks {Array<Number>} the list of ticks being converted
		 * @return {String} string representation of the tickValue parameter
		 */
		linear: function(tickValue, index, ticks) {
			// If we have lots of ticks, don't use the ones
			var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];

			// If we have a number like 2.5 as the delta, figure out how many decimal places we need
			if (Math.abs(delta) > 1) {
				if (tickValue !== Math.floor(tickValue)) {
					// not an integer
					delta = tickValue - Math.floor(tickValue);
				}
			}

			var logDelta = helpers.log10(Math.abs(delta));
			var tickString = '';

			if (tickValue !== 0) {
				var numDecimal = -1 * Math.floor(logDelta);
				numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places
				tickString = tickValue.toFixed(numDecimal);
			} else {
				tickString = '0'; // never show decimal places for 0
			}

			return tickString;
		},

		logarithmic: function(tickValue, index, ticks) {
			var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue))));

			if (tickValue === 0) {
				return '0';
			} else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) {
				return tickValue.toExponential();
			}
			return '';
		}
	}
};

},{"45":45}],35:[function(require,module,exports){
'use strict';

var defaults = require(25);
var Element = require(26);
var helpers = require(45);

defaults._set('global', {
	tooltips: {
		enabled: true,
		custom: null,
		mode: 'nearest',
		position: 'average',
		intersect: true,
		backgroundColor: 'rgba(0,0,0,0.8)',
		titleFontStyle: 'bold',
		titleSpacing: 2,
		titleMarginBottom: 6,
		titleFontColor: '#fff',
		titleAlign: 'left',
		bodySpacing: 2,
		bodyFontColor: '#fff',
		bodyAlign: 'left',
		footerFontStyle: 'bold',
		footerSpacing: 2,
		footerMarginTop: 6,
		footerFontColor: '#fff',
		footerAlign: 'left',
		yPadding: 6,
		xPadding: 6,
		caretPadding: 2,
		caretSize: 5,
		cornerRadius: 6,
		multiKeyBackground: '#fff',
		displayColors: true,
		borderColor: 'rgba(0,0,0,0)',
		borderWidth: 0,
		callbacks: {
			// Args are: (tooltipItems, data)
			beforeTitle: helpers.noop,
			title: function(tooltipItems, data) {
				// Pick first xLabel for now
				var title = '';
				var labels = data.labels;
				var labelCount = labels ? labels.length : 0;

				if (tooltipItems.length > 0) {
					var item = tooltipItems[0];

					if (item.xLabel) {
						title = item.xLabel;
					} else if (labelCount > 0 && item.index < labelCount) {
						title = labels[item.index];
					}
				}

				return title;
			},
			afterTitle: helpers.noop,

			// Args are: (tooltipItems, data)
			beforeBody: helpers.noop,

			// Args are: (tooltipItem, data)
			beforeLabel: helpers.noop,
			label: function(tooltipItem, data) {
				var label = data.datasets[tooltipItem.datasetIndex].label || '';

				if (label) {
					label += ': ';
				}
				label += tooltipItem.yLabel;
				return label;
			},
			labelColor: function(tooltipItem, chart) {
				var meta = chart.getDatasetMeta(tooltipItem.datasetIndex);
				var activeElement = meta.data[tooltipItem.index];
				var view = activeElement._view;
				return {
					borderColor: view.borderColor,
					backgroundColor: view.backgroundColor
				};
			},
			labelTextColor: function() {
				return this._options.bodyFontColor;
			},
			afterLabel: helpers.noop,

			// Args are: (tooltipItems, data)
			afterBody: helpers.noop,

			// Args are: (tooltipItems, data)
			beforeFooter: helpers.noop,
			footer: helpers.noop,
			afterFooter: helpers.noop
		}
	}
});

module.exports = function(Chart) {

	/**
 	 * Helper method to merge the opacity into a color
 	 */
	function mergeOpacity(colorString, opacity) {
		var color = helpers.color(colorString);
		return color.alpha(opacity * color.alpha()).rgbaString();
	}

	// Helper to push or concat based on if the 2nd parameter is an array or not
	function pushOrConcat(base, toPush) {
		if (toPush) {
			if (helpers.isArray(toPush)) {
				// base = base.concat(toPush);
				Array.prototype.push.apply(base, toPush);
			} else {
				base.push(toPush);
			}
		}

		return base;
	}

	// Private helper to create a tooltip item model
	// @param element : the chart element (point, arc, bar) to create the tooltip item for
	// @return : new tooltip item
	function createTooltipItem(element) {
		var xScale = element._xScale;
		var yScale = element._yScale || element._scale; // handle radar || polarArea charts
		var index = element._index;
		var datasetIndex = element._datasetIndex;

		return {
			xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '',
			yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '',
			index: index,
			datasetIndex: datasetIndex,
			x: element._model.x,
			y: element._model.y
		};
	}

	/**
	 * Helper to get the reset model for the tooltip
	 * @param tooltipOpts {Object} the tooltip options
	 */
	function getBaseModel(tooltipOpts) {
		var globalDefaults = defaults.global;
		var valueOrDefault = helpers.valueOrDefault;

		return {
			// Positioning
			xPadding: tooltipOpts.xPadding,
			yPadding: tooltipOpts.yPadding,
			xAlign: tooltipOpts.xAlign,
			yAlign: tooltipOpts.yAlign,

			// Body
			bodyFontColor: tooltipOpts.bodyFontColor,
			_bodyFontFamily: valueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),
			_bodyFontStyle: valueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),
			_bodyAlign: tooltipOpts.bodyAlign,
			bodyFontSize: valueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),
			bodySpacing: tooltipOpts.bodySpacing,

			// Title
			titleFontColor: tooltipOpts.titleFontColor,
			_titleFontFamily: valueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),
			_titleFontStyle: valueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),
			titleFontSize: valueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),
			_titleAlign: tooltipOpts.titleAlign,
			titleSpacing: tooltipOpts.titleSpacing,
			titleMarginBottom: tooltipOpts.titleMarginBottom,

			// Footer
			footerFontColor: tooltipOpts.footerFontColor,
			_footerFontFamily: valueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),
			_footerFontStyle: valueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),
			footerFontSize: valueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),
			_footerAlign: tooltipOpts.footerAlign,
			footerSpacing: tooltipOpts.footerSpacing,
			footerMarginTop: tooltipOpts.footerMarginTop,

			// Appearance
			caretSize: tooltipOpts.caretSize,
			cornerRadius: tooltipOpts.cornerRadius,
			backgroundColor: tooltipOpts.backgroundColor,
			opacity: 0,
			legendColorBackground: tooltipOpts.multiKeyBackground,
			displayColors: tooltipOpts.displayColors,
			borderColor: tooltipOpts.borderColor,
			borderWidth: tooltipOpts.borderWidth
		};
	}

	/**
	 * Get the size of the tooltip
	 */
	function getTooltipSize(tooltip, model) {
		var ctx = tooltip._chart.ctx;

		var height = model.yPadding * 2; // Tooltip Padding
		var width = 0;

		// Count of all lines in the body
		var body = model.body;
		var combinedBodyLength = body.reduce(function(count, bodyItem) {
			return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length;
		}, 0);
		combinedBodyLength += model.beforeBody.length + model.afterBody.length;

		var titleLineCount = model.title.length;
		var footerLineCount = model.footer.length;
		var titleFontSize = model.titleFontSize;
		var bodyFontSize = model.bodyFontSize;
		var footerFontSize = model.footerFontSize;

		height += titleLineCount * titleFontSize; // Title Lines
		height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing
		height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin
		height += combinedBodyLength * bodyFontSize; // Body Lines
		height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing
		height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin
		height += footerLineCount * (footerFontSize); // Footer Lines
		height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing

		// Title width
		var widthPadding = 0;
		var maxLineWidth = function(line) {
			width = Math.max(width, ctx.measureText(line).width + widthPadding);
		};

		ctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily);
		helpers.each(model.title, maxLineWidth);

		// Body width
		ctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily);
		helpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth);

		// Body lines may include some extra width due to the color box
		widthPadding = model.displayColors ? (bodyFontSize + 2) : 0;
		helpers.each(body, function(bodyItem) {
			helpers.each(bodyItem.before, maxLineWidth);
			helpers.each(bodyItem.lines, maxLineWidth);
			helpers.each(bodyItem.after, maxLineWidth);
		});

		// Reset back to 0
		widthPadding = 0;

		// Footer width
		ctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily);
		helpers.each(model.footer, maxLineWidth);

		// Add padding
		width += 2 * model.xPadding;

		return {
			width: width,
			height: height
		};
	}

	/**
	 * Helper to get the alignment of a tooltip given the size
	 */
	function determineAlignment(tooltip, size) {
		var model = tooltip._model;
		var chart = tooltip._chart;
		var chartArea = tooltip._chart.chartArea;
		var xAlign = 'center';
		var yAlign = 'center';

		if (model.y < size.height) {
			yAlign = 'top';
		} else if (model.y > (chart.height - size.height)) {
			yAlign = 'bottom';
		}

		var lf, rf; // functions to determine left, right alignment
		var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart
		var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges
		var midX = (chartArea.left + chartArea.right) / 2;
		var midY = (chartArea.top + chartArea.bottom) / 2;

		if (yAlign === 'center') {
			lf = function(x) {
				return x <= midX;
			};
			rf = function(x) {
				return x > midX;
			};
		} else {
			lf = function(x) {
				return x <= (size.width / 2);
			};
			rf = function(x) {
				return x >= (chart.width - (size.width / 2));
			};
		}

		olf = function(x) {
			return x + size.width > chart.width;
		};
		orf = function(x) {
			return x - size.width < 0;
		};
		yf = function(y) {
			return y <= midY ? 'top' : 'bottom';
		};

		if (lf(model.x)) {
			xAlign = 'left';

			// Is tooltip too wide and goes over the right side of the chart.?
			if (olf(model.x)) {
				xAlign = 'center';
				yAlign = yf(model.y);
			}
		} else if (rf(model.x)) {
			xAlign = 'right';

			// Is tooltip too wide and goes outside left edge of canvas?
			if (orf(model.x)) {
				xAlign = 'center';
				yAlign = yf(model.y);
			}
		}

		var opts = tooltip._options;
		return {
			xAlign: opts.xAlign ? opts.xAlign : xAlign,
			yAlign: opts.yAlign ? opts.yAlign : yAlign
		};
	}

	/**
	 * @Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment
	 */
	function getBackgroundPoint(vm, size, alignment) {
		// Background Position
		var x = vm.x;
		var y = vm.y;

		var caretSize = vm.caretSize;
		var caretPadding = vm.caretPadding;
		var cornerRadius = vm.cornerRadius;
		var xAlign = alignment.xAlign;
		var yAlign = alignment.yAlign;
		var paddingAndSize = caretSize + caretPadding;
		var radiusAndPadding = cornerRadius + caretPadding;

		if (xAlign === 'right') {
			x -= size.width;
		} else if (xAlign === 'center') {
			x -= (size.width / 2);
		}

		if (yAlign === 'top') {
			y += paddingAndSize;
		} else if (yAlign === 'bottom') {
			y -= size.height + paddingAndSize;
		} else {
			y -= (size.height / 2);
		}

		if (yAlign === 'center') {
			if (xAlign === 'left') {
				x += paddingAndSize;
			} else if (xAlign === 'right') {
				x -= paddingAndSize;
			}
		} else if (xAlign === 'left') {
			x -= radiusAndPadding;
		} else if (xAlign === 'right') {
			x += radiusAndPadding;
		}

		return {
			x: x,
			y: y
		};
	}

	Chart.Tooltip = Element.extend({
		initialize: function() {
			this._model = getBaseModel(this._options);
		},

		// Get the title
		// Args are: (tooltipItem, data)
		getTitle: function() {
			var me = this;
			var opts = me._options;
			var callbacks = opts.callbacks;

			var beforeTitle = callbacks.beforeTitle.apply(me, arguments);
			var title = callbacks.title.apply(me, arguments);
			var afterTitle = callbacks.afterTitle.apply(me, arguments);

			var lines = [];
			lines = pushOrConcat(lines, beforeTitle);
			lines = pushOrConcat(lines, title);
			lines = pushOrConcat(lines, afterTitle);

			return lines;
		},

		// Args are: (tooltipItem, data)
		getBeforeBody: function() {
			var lines = this._options.callbacks.beforeBody.apply(this, arguments);
			return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
		},

		// Args are: (tooltipItem, data)
		getBody: function(tooltipItems, data) {
			var me = this;
			var callbacks = me._options.callbacks;
			var bodyItems = [];

			helpers.each(tooltipItems, function(tooltipItem) {
				var bodyItem = {
					before: [],
					lines: [],
					after: []
				};
				pushOrConcat(bodyItem.before, callbacks.beforeLabel.call(me, tooltipItem, data));
				pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data));
				pushOrConcat(bodyItem.after, callbacks.afterLabel.call(me, tooltipItem, data));

				bodyItems.push(bodyItem);
			});

			return bodyItems;
		},

		// Args are: (tooltipItem, data)
		getAfterBody: function() {
			var lines = this._options.callbacks.afterBody.apply(this, arguments);
			return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
		},

		// Get the footer and beforeFooter and afterFooter lines
		// Args are: (tooltipItem, data)
		getFooter: function() {
			var me = this;
			var callbacks = me._options.callbacks;

			var beforeFooter = callbacks.beforeFooter.apply(me, arguments);
			var footer = callbacks.footer.apply(me, arguments);
			var afterFooter = callbacks.afterFooter.apply(me, arguments);

			var lines = [];
			lines = pushOrConcat(lines, beforeFooter);
			lines = pushOrConcat(lines, footer);
			lines = pushOrConcat(lines, afterFooter);

			return lines;
		},

		update: function(changed) {
			var me = this;
			var opts = me._options;

			// Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition
			// that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time
			// which breaks any animations.
			var existingModel = me._model;
			var model = me._model = getBaseModel(opts);
			var active = me._active;

			var data = me._data;

			// In the case where active.length === 0 we need to keep these at existing values for good animations
			var alignment = {
				xAlign: existingModel.xAlign,
				yAlign: existingModel.yAlign
			};
			var backgroundPoint = {
				x: existingModel.x,
				y: existingModel.y
			};
			var tooltipSize = {
				width: existingModel.width,
				height: existingModel.height
			};
			var tooltipPosition = {
				x: existingModel.caretX,
				y: existingModel.caretY
			};

			var i, len;

			if (active.length) {
				model.opacity = 1;

				var labelColors = [];
				var labelTextColors = [];
				tooltipPosition = Chart.Tooltip.positioners[opts.position](active, me._eventPosition);

				var tooltipItems = [];
				for (i = 0, len = active.length; i < len; ++i) {
					tooltipItems.push(createTooltipItem(active[i]));
				}

				// If the user provided a filter function, use it to modify the tooltip items
				if (opts.filter) {
					tooltipItems = tooltipItems.filter(function(a) {
						return opts.filter(a, data);
					});
				}

				// If the user provided a sorting function, use it to modify the tooltip items
				if (opts.itemSort) {
					tooltipItems = tooltipItems.sort(function(a, b) {
						return opts.itemSort(a, b, data);
					});
				}

				// Determine colors for boxes
				helpers.each(tooltipItems, function(tooltipItem) {
					labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart));
					labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart));
				});


				// Build the Text Lines
				model.title = me.getTitle(tooltipItems, data);
				model.beforeBody = me.getBeforeBody(tooltipItems, data);
				model.body = me.getBody(tooltipItems, data);
				model.afterBody = me.getAfterBody(tooltipItems, data);
				model.footer = me.getFooter(tooltipItems, data);

				// Initial positioning and colors
				model.x = Math.round(tooltipPosition.x);
				model.y = Math.round(tooltipPosition.y);
				model.caretPadding = opts.caretPadding;
				model.labelColors = labelColors;
				model.labelTextColors = labelTextColors;

				// data points
				model.dataPoints = tooltipItems;

				// We need to determine alignment of the tooltip
				tooltipSize = getTooltipSize(this, model);
				alignment = determineAlignment(this, tooltipSize);
				// Final Size and Position
				backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment);
			} else {
				model.opacity = 0;
			}

			model.xAlign = alignment.xAlign;
			model.yAlign = alignment.yAlign;
			model.x = backgroundPoint.x;
			model.y = backgroundPoint.y;
			model.width = tooltipSize.width;
			model.height = tooltipSize.height;

			// Point where the caret on the tooltip points to
			model.caretX = tooltipPosition.x;
			model.caretY = tooltipPosition.y;

			me._model = model;

			if (changed && opts.custom) {
				opts.custom.call(me, model);
			}

			return me;
		},
		drawCaret: function(tooltipPoint, size) {
			var ctx = this._chart.ctx;
			var vm = this._view;
			var caretPosition = this.getCaretPosition(tooltipPoint, size, vm);

			ctx.lineTo(caretPosition.x1, caretPosition.y1);
			ctx.lineTo(caretPosition.x2, caretPosition.y2);
			ctx.lineTo(caretPosition.x3, caretPosition.y3);
		},
		getCaretPosition: function(tooltipPoint, size, vm) {
			var x1, x2, x3, y1, y2, y3;
			var caretSize = vm.caretSize;
			var cornerRadius = vm.cornerRadius;
			var xAlign = vm.xAlign;
			var yAlign = vm.yAlign;
			var ptX = tooltipPoint.x;
			var ptY = tooltipPoint.y;
			var width = size.width;
			var height = size.height;

			if (yAlign === 'center') {
				y2 = ptY + (height / 2);

				if (xAlign === 'left') {
					x1 = ptX;
					x2 = x1 - caretSize;
					x3 = x1;

					y1 = y2 + caretSize;
					y3 = y2 - caretSize;
				} else {
					x1 = ptX + width;
					x2 = x1 + caretSize;
					x3 = x1;

					y1 = y2 - caretSize;
					y3 = y2 + caretSize;
				}
			} else {
				if (xAlign === 'left') {
					x2 = ptX + cornerRadius + (caretSize);
					x1 = x2 - caretSize;
					x3 = x2 + caretSize;
				} else if (xAlign === 'right') {
					x2 = ptX + width - cornerRadius - caretSize;
					x1 = x2 - caretSize;
					x3 = x2 + caretSize;
				} else {
					x2 = ptX + (width / 2);
					x1 = x2 - caretSize;
					x3 = x2 + caretSize;
				}
				if (yAlign === 'top') {
					y1 = ptY;
					y2 = y1 - caretSize;
					y3 = y1;
				} else {
					y1 = ptY + height;
					y2 = y1 + caretSize;
					y3 = y1;
					// invert drawing order
					var tmp = x3;
					x3 = x1;
					x1 = tmp;
				}
			}
			return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3};
		},
		drawTitle: function(pt, vm, ctx, opacity) {
			var title = vm.title;

			if (title.length) {
				ctx.textAlign = vm._titleAlign;
				ctx.textBaseline = 'top';

				var titleFontSize = vm.titleFontSize;
				var titleSpacing = vm.titleSpacing;

				ctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity);
				ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);

				var i, len;
				for (i = 0, len = title.length; i < len; ++i) {
					ctx.fillText(title[i], pt.x, pt.y);
					pt.y += titleFontSize + titleSpacing; // Line Height and spacing

					if (i + 1 === title.length) {
						pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing
					}
				}
			}
		},
		drawBody: function(pt, vm, ctx, opacity) {
			var bodyFontSize = vm.bodyFontSize;
			var bodySpacing = vm.bodySpacing;
			var body = vm.body;

			ctx.textAlign = vm._bodyAlign;
			ctx.textBaseline = 'top';
			ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);

			// Before Body
			var xLinePadding = 0;
			var fillLineOfText = function(line) {
				ctx.fillText(line, pt.x + xLinePadding, pt.y);
				pt.y += bodyFontSize + bodySpacing;
			};

			// Before body lines
			helpers.each(vm.beforeBody, fillLineOfText);

			var drawColorBoxes = vm.displayColors;
			xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0;

			// Draw body lines now
			helpers.each(body, function(bodyItem, i) {
				helpers.each(bodyItem.before, fillLineOfText);

				helpers.each(bodyItem.lines, function(line) {
					// Draw Legend-like boxes if needed
					if (drawColorBoxes) {
						// Fill a white rect so that colours merge nicely if the opacity is < 1
						ctx.fillStyle = mergeOpacity(vm.legendColorBackground, opacity);
						ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize);

						// Border
						ctx.lineWidth = 1;
						ctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity);
						ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize);

						// Inner square
						ctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity);
						ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
						var textColor = mergeOpacity(vm.labelTextColors[i], opacity);
						ctx.fillStyle = textColor;
					}

					fillLineOfText(line);
				});

				helpers.each(bodyItem.after, fillLineOfText);
			});

			// Reset back to 0 for after body
			xLinePadding = 0;

			// After body lines
			helpers.each(vm.afterBody, fillLineOfText);
			pt.y -= bodySpacing; // Remove last body spacing
		},
		drawFooter: function(pt, vm, ctx, opacity) {
			var footer = vm.footer;

			if (footer.length) {
				pt.y += vm.footerMarginTop;

				ctx.textAlign = vm._footerAlign;
				ctx.textBaseline = 'top';

				ctx.fillStyle = mergeOpacity(vm.footerFontColor, opacity);
				ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);

				helpers.each(footer, function(line) {
					ctx.fillText(line, pt.x, pt.y);
					pt.y += vm.footerFontSize + vm.footerSpacing;
				});
			}
		},
		drawBackground: function(pt, vm, ctx, tooltipSize, opacity) {
			ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity);
			ctx.strokeStyle = mergeOpacity(vm.borderColor, opacity);
			ctx.lineWidth = vm.borderWidth;
			var xAlign = vm.xAlign;
			var yAlign = vm.yAlign;
			var x = pt.x;
			var y = pt.y;
			var width = tooltipSize.width;
			var height = tooltipSize.height;
			var radius = vm.cornerRadius;

			ctx.beginPath();
			ctx.moveTo(x + radius, y);
			if (yAlign === 'top') {
				this.drawCaret(pt, tooltipSize);
			}
			ctx.lineTo(x + width - radius, y);
			ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
			if (yAlign === 'center' && xAlign === 'right') {
				this.drawCaret(pt, tooltipSize);
			}
			ctx.lineTo(x + width, y + height - radius);
			ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
			if (yAlign === 'bottom') {
				this.drawCaret(pt, tooltipSize);
			}
			ctx.lineTo(x + radius, y + height);
			ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
			if (yAlign === 'center' && xAlign === 'left') {
				this.drawCaret(pt, tooltipSize);
			}
			ctx.lineTo(x, y + radius);
			ctx.quadraticCurveTo(x, y, x + radius, y);
			ctx.closePath();

			ctx.fill();

			if (vm.borderWidth > 0) {
				ctx.stroke();
			}
		},
		draw: function() {
			var ctx = this._chart.ctx;
			var vm = this._view;

			if (vm.opacity === 0) {
				return;
			}

			var tooltipSize = {
				width: vm.width,
				height: vm.height
			};
			var pt = {
				x: vm.x,
				y: vm.y
			};

			// IE11/Edge does not like very small opacities, so snap to 0
			var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;

			// Truthy/falsey value for empty tooltip
			var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length;

			if (this._options.enabled && hasTooltipContent) {
				// Draw Background
				this.drawBackground(pt, vm, ctx, tooltipSize, opacity);

				// Draw Title, Body, and Footer
				pt.x += vm.xPadding;
				pt.y += vm.yPadding;

				// Titles
				this.drawTitle(pt, vm, ctx, opacity);

				// Body
				this.drawBody(pt, vm, ctx, opacity);

				// Footer
				this.drawFooter(pt, vm, ctx, opacity);
			}
		},

		/**
		 * Handle an event
		 * @private
		 * @param {IEvent} event - The event to handle
		 * @returns {Boolean} true if the tooltip changed
		 */
		handleEvent: function(e) {
			var me = this;
			var options = me._options;
			var changed = false;

			me._lastActive = me._lastActive || [];

			// Find Active Elements for tooltips
			if (e.type === 'mouseout') {
				me._active = [];
			} else {
				me._active = me._chart.getElementsAtEventForMode(e, options.mode, options);
			}

			// Remember Last Actives
			changed = !helpers.arrayEquals(me._active, me._lastActive);

			// If tooltip didn't change, do not handle the target event
			if (!changed) {
				return false;
			}

			me._lastActive = me._active;

			if (options.enabled || options.custom) {
				me._eventPosition = {
					x: e.x,
					y: e.y
				};

				var model = me._model;
				me.update(true);
				me.pivot();

				// See if our tooltip position changed
				changed |= (model.x !== me._model.x) || (model.y !== me._model.y);
			}

			return changed;
		}
	});

	/**
	 * @namespace Chart.Tooltip.positioners
	 */
	Chart.Tooltip.positioners = {
		/**
		 * Average mode places the tooltip at the average position of the elements shown
		 * @function Chart.Tooltip.positioners.average
		 * @param elements {ChartElement[]} the elements being displayed in the tooltip
		 * @returns {Point} tooltip position
		 */
		average: function(elements) {
			if (!elements.length) {
				return false;
			}

			var i, len;
			var x = 0;
			var y = 0;
			var count = 0;

			for (i = 0, len = elements.length; i < len; ++i) {
				var el = elements[i];
				if (el && el.hasValue()) {
					var pos = el.tooltipPosition();
					x += pos.x;
					y += pos.y;
					++count;
				}
			}

			return {
				x: Math.round(x / count),
				y: Math.round(y / count)
			};
		},

		/**
		 * Gets the tooltip position nearest of the item nearest to the event position
		 * @function Chart.Tooltip.positioners.nearest
		 * @param elements {Chart.Element[]} the tooltip elements
		 * @param eventPosition {Point} the position of the event in canvas coordinates
		 * @returns {Point} the tooltip position
		 */
		nearest: function(elements, eventPosition) {
			var x = eventPosition.x;
			var y = eventPosition.y;
			var minDistance = Number.POSITIVE_INFINITY;
			var i, len, nearestElement;

			for (i = 0, len = elements.length; i < len; ++i) {
				var el = elements[i];
				if (el && el.hasValue()) {
					var center = el.getCenterPoint();
					var d = helpers.distanceBetweenPoints(eventPosition, center);

					if (d < minDistance) {
						minDistance = d;
						nearestElement = el;
					}
				}
			}

			if (nearestElement) {
				var tp = nearestElement.tooltipPosition();
				x = tp.x;
				y = tp.y;
			}

			return {
				x: x,
				y: y
			};
		}
	};
};

},{"25":25,"26":26,"45":45}],36:[function(require,module,exports){
'use strict';

var defaults = require(25);
var Element = require(26);
var helpers = require(45);

defaults._set('global', {
	elements: {
		arc: {
			backgroundColor: defaults.global.defaultColor,
			borderColor: '#fff',
			borderWidth: 2
		}
	}
});

module.exports = Element.extend({
	inLabelRange: function(mouseX) {
		var vm = this._view;

		if (vm) {
			return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
		}
		return false;
	},

	inRange: function(chartX, chartY) {
		var vm = this._view;

		if (vm) {
			var pointRelativePosition = helpers.getAngleFromPoint(vm, {x: chartX, y: chartY});
			var	angle = pointRelativePosition.angle;
			var distance = pointRelativePosition.distance;

			// Sanitise angle range
			var startAngle = vm.startAngle;
			var endAngle = vm.endAngle;
			while (endAngle < startAngle) {
				endAngle += 2.0 * Math.PI;
			}
			while (angle > endAngle) {
				angle -= 2.0 * Math.PI;
			}
			while (angle < startAngle) {
				angle += 2.0 * Math.PI;
			}

			// Check if within the range of the open/close angle
			var betweenAngles = (angle >= startAngle && angle <= endAngle);
			var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);

			return (betweenAngles && withinRadius);
		}
		return false;
	},

	getCenterPoint: function() {
		var vm = this._view;
		var halfAngle = (vm.startAngle + vm.endAngle) / 2;
		var halfRadius = (vm.innerRadius + vm.outerRadius) / 2;
		return {
			x: vm.x + Math.cos(halfAngle) * halfRadius,
			y: vm.y + Math.sin(halfAngle) * halfRadius
		};
	},

	getArea: function() {
		var vm = this._view;
		return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2));
	},

	tooltipPosition: function() {
		var vm = this._view;
		var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2);
		var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;

		return {
			x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
			y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
		};
	},

	draw: function() {
		var ctx = this._chart.ctx;
		var vm = this._view;
		var sA = vm.startAngle;
		var eA = vm.endAngle;

		ctx.beginPath();

		ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);
		ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);

		ctx.closePath();
		ctx.strokeStyle = vm.borderColor;
		ctx.lineWidth = vm.borderWidth;

		ctx.fillStyle = vm.backgroundColor;

		ctx.fill();
		ctx.lineJoin = 'bevel';

		if (vm.borderWidth) {
			ctx.stroke();
		}
	}
});

},{"25":25,"26":26,"45":45}],37:[function(require,module,exports){
'use strict';

var defaults = require(25);
var Element = require(26);
var helpers = require(45);

var globalDefaults = defaults.global;

defaults._set('global', {
	elements: {
		line: {
			tension: 0.4,
			backgroundColor: globalDefaults.defaultColor,
			borderWidth: 3,
			borderColor: globalDefaults.defaultColor,
			borderCapStyle: 'butt',
			borderDash: [],
			borderDashOffset: 0.0,
			borderJoinStyle: 'miter',
			capBezierPoints: true,
			fill: true, // do we fill in the area between the line and its base axis
		}
	}
});

module.exports = Element.extend({
	draw: function() {
		var me = this;
		var vm = me._view;
		var ctx = me._chart.ctx;
		var spanGaps = vm.spanGaps;
		var points = me._children.slice(); // clone array
		var globalOptionLineElements = globalDefaults.elements.line;
		var lastDrawnIndex = -1;
		var index, current, previous, currentVM;

		// If we are looping, adding the first point again
		if (me._loop && points.length) {
			points.push(points[0]);
		}

		ctx.save();

		// Stroke Line Options
		ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle;

		// IE 9 and 10 do not support line dash
		if (ctx.setLineDash) {
			ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);
		}

		ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset;
		ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;
		ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth;
		ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor;

		// Stroke Line
		ctx.beginPath();
		lastDrawnIndex = -1;

		for (index = 0; index < points.length; ++index) {
			current = points[index];
			previous = helpers.previousItem(points, index);
			currentVM = current._view;

			// First point moves to it's starting position no matter what
			if (index === 0) {
				if (!currentVM.skip) {
					ctx.moveTo(currentVM.x, currentVM.y);
					lastDrawnIndex = index;
				}
			} else {
				previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex];

				if (!currentVM.skip) {
					if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) {
						// There was a gap and this is the first point after the gap
						ctx.moveTo(currentVM.x, currentVM.y);
					} else {
						// Line to next point
						helpers.canvas.lineTo(ctx, previous._view, current._view);
					}
					lastDrawnIndex = index;
				}
			}
		}

		ctx.stroke();
		ctx.restore();
	}
});

},{"25":25,"26":26,"45":45}],38:[function(require,module,exports){
'use strict';

var defaults = require(25);
var Element = require(26);
var helpers = require(45);

var defaultColor = defaults.global.defaultColor;

defaults._set('global', {
	elements: {
		point: {
			radius: 3,
			pointStyle: 'circle',
			backgroundColor: defaultColor,
			borderColor: defaultColor,
			borderWidth: 1,
			// Hover
			hitRadius: 1,
			hoverRadius: 4,
			hoverBorderWidth: 1
		}
	}
});

function xRange(mouseX) {
	var vm = this._view;
	return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
}

function yRange(mouseY) {
	var vm = this._view;
	return vm ? (Math.pow(mouseY - vm.y, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
}

module.exports = Element.extend({
	inRange: function(mouseX, mouseY) {
		var vm = this._view;
		return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;
	},

	inLabelRange: xRange,
	inXRange: xRange,
	inYRange: yRange,

	getCenterPoint: function() {
		var vm = this._view;
		return {
			x: vm.x,
			y: vm.y
		};
	},

	getArea: function() {
		return Math.PI * Math.pow(this._view.radius, 2);
	},

	tooltipPosition: function() {
		var vm = this._view;
		return {
			x: vm.x,
			y: vm.y,
			padding: vm.radius + vm.borderWidth
		};
	},

	draw: function(chartArea) {
		var vm = this._view;
		var model = this._model;
		var ctx = this._chart.ctx;
		var pointStyle = vm.pointStyle;
		var radius = vm.radius;
		var x = vm.x;
		var y = vm.y;
		var color = helpers.color;
		var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.)
		var ratio = 0;

		if (vm.skip) {
			return;
		}

		ctx.strokeStyle = vm.borderColor || defaultColor;
		ctx.lineWidth = helpers.valueOrDefault(vm.borderWidth, defaults.global.elements.point.borderWidth);
		ctx.fillStyle = vm.backgroundColor || defaultColor;

		// Cliping for Points.
		// going out from inner charArea?
		if ((chartArea !== undefined) && ((model.x < chartArea.left) || (chartArea.right * errMargin < model.x) || (model.y < chartArea.top) || (chartArea.bottom * errMargin < model.y))) {
			// Point fade out
			if (model.x < chartArea.left) {
				ratio = (x - model.x) / (chartArea.left - model.x);
			} else if (chartArea.right * errMargin < model.x) {
				ratio = (model.x - x) / (model.x - chartArea.right);
			} else if (model.y < chartArea.top) {
				ratio = (y - model.y) / (chartArea.top - model.y);
			} else if (chartArea.bottom * errMargin < model.y) {
				ratio = (model.y - y) / (model.y - chartArea.bottom);
			}
			ratio = Math.round(ratio * 100) / 100;
			ctx.strokeStyle = color(ctx.strokeStyle).alpha(ratio).rgbString();
			ctx.fillStyle = color(ctx.fillStyle).alpha(ratio).rgbString();
		}

		helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y);
	}
});

},{"25":25,"26":26,"45":45}],39:[function(require,module,exports){
'use strict';

var defaults = require(25);
var Element = require(26);

defaults._set('global', {
	elements: {
		rectangle: {
			backgroundColor: defaults.global.defaultColor,
			borderColor: defaults.global.defaultColor,
			borderSkipped: 'bottom',
			borderWidth: 0
		}
	}
});

function isVertical(bar) {
	return bar._view.width !== undefined;
}

/**
 * Helper function to get the bounds of the bar regardless of the orientation
 * @param bar {Chart.Element.Rectangle} the bar
 * @return {Bounds} bounds of the bar
 * @private
 */
function getBarBounds(bar) {
	var vm = bar._view;
	var x1, x2, y1, y2;

	if (isVertical(bar)) {
		// vertical
		var halfWidth = vm.width / 2;
		x1 = vm.x - halfWidth;
		x2 = vm.x + halfWidth;
		y1 = Math.min(vm.y, vm.base);
		y2 = Math.max(vm.y, vm.base);
	} else {
		// horizontal bar
		var halfHeight = vm.height / 2;
		x1 = Math.min(vm.x, vm.base);
		x2 = Math.max(vm.x, vm.base);
		y1 = vm.y - halfHeight;
		y2 = vm.y + halfHeight;
	}

	return {
		left: x1,
		top: y1,
		right: x2,
		bottom: y2
	};
}

module.exports = Element.extend({
	draw: function() {
		var ctx = this._chart.ctx;
		var vm = this._view;
		var left, right, top, bottom, signX, signY, borderSkipped;
		var borderWidth = vm.borderWidth;

		if (!vm.horizontal) {
			// bar
			left = vm.x - vm.width / 2;
			right = vm.x + vm.width / 2;
			top = vm.y;
			bottom = vm.base;
			signX = 1;
			signY = bottom > top ? 1 : -1;
			borderSkipped = vm.borderSkipped || 'bottom';
		} else {
			// horizontal bar
			left = vm.base;
			right = vm.x;
			top = vm.y - vm.height / 2;
			bottom = vm.y + vm.height / 2;
			signX = right > left ? 1 : -1;
			signY = 1;
			borderSkipped = vm.borderSkipped || 'left';
		}

		// Canvas doesn't allow us to stroke inside the width so we can
		// adjust the sizes to fit if we're setting a stroke on the line
		if (borderWidth) {
			// borderWidth shold be less than bar width and bar height.
			var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));
			borderWidth = borderWidth > barSize ? barSize : borderWidth;
			var halfStroke = borderWidth / 2;
			// Adjust borderWidth when bar top position is near vm.base(zero).
			var borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0);
			var borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0);
			var borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0);
			var borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0);
			// not become a vertical line?
			if (borderLeft !== borderRight) {
				top = borderTop;
				bottom = borderBottom;
			}
			// not become a horizontal line?
			if (borderTop !== borderBottom) {
				left = borderLeft;
				right = borderRight;
			}
		}

		ctx.beginPath();
		ctx.fillStyle = vm.backgroundColor;
		ctx.strokeStyle = vm.borderColor;
		ctx.lineWidth = borderWidth;

		// Corner points, from bottom-left to bottom-right clockwise
		// | 1 2 |
		// | 0 3 |
		var corners = [
			[left, bottom],
			[left, top],
			[right, top],
			[right, bottom]
		];

		// Find first (starting) corner with fallback to 'bottom'
		var borders = ['bottom', 'left', 'top', 'right'];
		var startCorner = borders.indexOf(borderSkipped, 0);
		if (startCorner === -1) {
			startCorner = 0;
		}

		function cornerAt(index) {
			return corners[(startCorner + index) % 4];
		}

		// Draw rectangle from 'startCorner'
		var corner = cornerAt(0);
		ctx.moveTo(corner[0], corner[1]);

		for (var i = 1; i < 4; i++) {
			corner = cornerAt(i);
			ctx.lineTo(corner[0], corner[1]);
		}

		ctx.fill();
		if (borderWidth) {
			ctx.stroke();
		}
	},

	height: function() {
		var vm = this._view;
		return vm.base - vm.y;
	},

	inRange: function(mouseX, mouseY) {
		var inRange = false;

		if (this._view) {
			var bounds = getBarBounds(this);
			inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom;
		}

		return inRange;
	},

	inLabelRange: function(mouseX, mouseY) {
		var me = this;
		if (!me._view) {
			return false;
		}

		var inRange = false;
		var bounds = getBarBounds(me);

		if (isVertical(me)) {
			inRange = mouseX >= bounds.left && mouseX <= bounds.right;
		} else {
			inRange = mouseY >= bounds.top && mouseY <= bounds.bottom;
		}

		return inRange;
	},

	inXRange: function(mouseX) {
		var bounds = getBarBounds(this);
		return mouseX >= bounds.left && mouseX <= bounds.right;
	},

	inYRange: function(mouseY) {
		var bounds = getBarBounds(this);
		return mouseY >= bounds.top && mouseY <= bounds.bottom;
	},

	getCenterPoint: function() {
		var vm = this._view;
		var x, y;
		if (isVertical(this)) {
			x = vm.x;
			y = (vm.y + vm.base) / 2;
		} else {
			x = (vm.x + vm.base) / 2;
			y = vm.y;
		}

		return {x: x, y: y};
	},

	getArea: function() {
		var vm = this._view;
		return vm.width * Math.abs(vm.y - vm.base);
	},

	tooltipPosition: function() {
		var vm = this._view;
		return {
			x: vm.x,
			y: vm.y
		};
	}
});

},{"25":25,"26":26}],40:[function(require,module,exports){
'use strict';

module.exports = {};
module.exports.Arc = require(36);
module.exports.Line = require(37);
module.exports.Point = require(38);
module.exports.Rectangle = require(39);

},{"36":36,"37":37,"38":38,"39":39}],41:[function(require,module,exports){
'use strict';

var helpers = require(42);

/**
 * @namespace Chart.helpers.canvas
 */
var exports = module.exports = {
	/**
	 * Clears the entire canvas associated to the given `chart`.
	 * @param {Chart} chart - The chart for which to clear the canvas.
	 */
	clear: function(chart) {
		chart.ctx.clearRect(0, 0, chart.width, chart.height);
	},

	/**
	 * Creates a "path" for a rectangle with rounded corners at position (x, y) with a
	 * given size (width, height) and the same `radius` for all corners.
	 * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context.
	 * @param {Number} x - The x axis of the coordinate for the rectangle starting point.
	 * @param {Number} y - The y axis of the coordinate for the rectangle starting point.
	 * @param {Number} width - The rectangle's width.
	 * @param {Number} height - The rectangle's height.
	 * @param {Number} radius - The rounded amount (in pixels) for the four corners.
	 * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object?
	 */
	roundedRect: function(ctx, x, y, width, height, radius) {
		if (radius) {
			var rx = Math.min(radius, width / 2);
			var ry = Math.min(radius, height / 2);

			ctx.moveTo(x + rx, y);
			ctx.lineTo(x + width - rx, y);
			ctx.quadraticCurveTo(x + width, y, x + width, y + ry);
			ctx.lineTo(x + width, y + height - ry);
			ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height);
			ctx.lineTo(x + rx, y + height);
			ctx.quadraticCurveTo(x, y + height, x, y + height - ry);
			ctx.lineTo(x, y + ry);
			ctx.quadraticCurveTo(x, y, x + rx, y);
		} else {
			ctx.rect(x, y, width, height);
		}
	},

	drawPoint: function(ctx, style, radius, x, y) {
		var type, edgeLength, xOffset, yOffset, height, size;

		if (typeof style === 'object') {
			type = style.toString();
			if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
				ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height);
				return;
			}
		}

		if (isNaN(radius) || radius <= 0) {
			return;
		}

		switch (style) {
		// Default includes circle
		default:
			ctx.beginPath();
			ctx.arc(x, y, radius, 0, Math.PI * 2);
			ctx.closePath();
			ctx.fill();
			break;
		case 'triangle':
			ctx.beginPath();
			edgeLength = 3 * radius / Math.sqrt(3);
			height = edgeLength * Math.sqrt(3) / 2;
			ctx.moveTo(x - edgeLength / 2, y + height / 3);
			ctx.lineTo(x + edgeLength / 2, y + height / 3);
			ctx.lineTo(x, y - 2 * height / 3);
			ctx.closePath();
			ctx.fill();
			break;
		case 'rect':
			size = 1 / Math.SQRT2 * radius;
			ctx.beginPath();
			ctx.fillRect(x - size, y - size, 2 * size, 2 * size);
			ctx.strokeRect(x - size, y - size, 2 * size, 2 * size);
			break;
		case 'rectRounded':
			var offset = radius / Math.SQRT2;
			var leftX = x - offset;
			var topY = y - offset;
			var sideSize = Math.SQRT2 * radius;
			ctx.beginPath();
			this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius / 2);
			ctx.closePath();
			ctx.fill();
			break;
		case 'rectRot':
			size = 1 / Math.SQRT2 * radius;
			ctx.beginPath();
			ctx.moveTo(x - size, y);
			ctx.lineTo(x, y + size);
			ctx.lineTo(x + size, y);
			ctx.lineTo(x, y - size);
			ctx.closePath();
			ctx.fill();
			break;
		case 'cross':
			ctx.beginPath();
			ctx.moveTo(x, y + radius);
			ctx.lineTo(x, y - radius);
			ctx.moveTo(x - radius, y);
			ctx.lineTo(x + radius, y);
			ctx.closePath();
			break;
		case 'crossRot':
			ctx.beginPath();
			xOffset = Math.cos(Math.PI / 4) * radius;
			yOffset = Math.sin(Math.PI / 4) * radius;
			ctx.moveTo(x - xOffset, y - yOffset);
			ctx.lineTo(x + xOffset, y + yOffset);
			ctx.moveTo(x - xOffset, y + yOffset);
			ctx.lineTo(x + xOffset, y - yOffset);
			ctx.closePath();
			break;
		case 'star':
			ctx.beginPath();
			ctx.moveTo(x, y + radius);
			ctx.lineTo(x, y - radius);
			ctx.moveTo(x - radius, y);
			ctx.lineTo(x + radius, y);
			xOffset = Math.cos(Math.PI / 4) * radius;
			yOffset = Math.sin(Math.PI / 4) * radius;
			ctx.moveTo(x - xOffset, y - yOffset);
			ctx.lineTo(x + xOffset, y + yOffset);
			ctx.moveTo(x - xOffset, y + yOffset);
			ctx.lineTo(x + xOffset, y - yOffset);
			ctx.closePath();
			break;
		case 'line':
			ctx.beginPath();
			ctx.moveTo(x - radius, y);
			ctx.lineTo(x + radius, y);
			ctx.closePath();
			break;
		case 'dash':
			ctx.beginPath();
			ctx.moveTo(x, y);
			ctx.lineTo(x + radius, y);
			ctx.closePath();
			break;
		}

		ctx.stroke();
	},

	clipArea: function(ctx, area) {
		ctx.save();
		ctx.beginPath();
		ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
		ctx.clip();
	},

	unclipArea: function(ctx) {
		ctx.restore();
	},

	lineTo: function(ctx, previous, target, flip) {
		if (target.steppedLine) {
			if ((target.steppedLine === 'after' && !flip) || (target.steppedLine !== 'after' && flip)) {
				ctx.lineTo(previous.x, target.y);
			} else {
				ctx.lineTo(target.x, previous.y);
			}
			ctx.lineTo(target.x, target.y);
			return;
		}

		if (!target.tension) {
			ctx.lineTo(target.x, target.y);
			return;
		}

		ctx.bezierCurveTo(
			flip ? previous.controlPointPreviousX : previous.controlPointNextX,
			flip ? previous.controlPointPreviousY : previous.controlPointNextY,
			flip ? target.controlPointNextX : target.controlPointPreviousX,
			flip ? target.controlPointNextY : target.controlPointPreviousY,
			target.x,
			target.y);
	}
};

// DEPRECATIONS

/**
 * Provided for backward compatibility, use Chart.helpers.canvas.clear instead.
 * @namespace Chart.helpers.clear
 * @deprecated since version 2.7.0
 * @todo remove at version 3
 * @private
 */
helpers.clear = exports.clear;

/**
 * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead.
 * @namespace Chart.helpers.drawRoundedRectangle
 * @deprecated since version 2.7.0
 * @todo remove at version 3
 * @private
 */
helpers.drawRoundedRectangle = function(ctx) {
	ctx.beginPath();
	exports.roundedRect.apply(exports, arguments);
	ctx.closePath();
};

},{"42":42}],42:[function(require,module,exports){
'use strict';

/**
 * @namespace Chart.helpers
 */
var helpers = {
	/**
	 * An empty function that can be used, for example, for optional callback.
	 */
	noop: function() {},

	/**
	 * Returns a unique id, sequentially generated from a global variable.
	 * @returns {Number}
	 * @function
	 */
	uid: (function() {
		var id = 0;
		return function() {
			return id++;
		};
	}()),

	/**
	 * Returns true if `value` is neither null nor undefined, else returns false.
	 * @param {*} value - The value to test.
	 * @returns {Boolean}
	 * @since 2.7.0
	 */
	isNullOrUndef: function(value) {
		return value === null || typeof value === 'undefined';
	},

	/**
	 * Returns true if `value` is an array, else returns false.
	 * @param {*} value - The value to test.
	 * @returns {Boolean}
	 * @function
	 */
	isArray: Array.isArray ? Array.isArray : function(value) {
		return Object.prototype.toString.call(value) === '[object Array]';
	},

	/**
	 * Returns true if `value` is an object (excluding null), else returns false.
	 * @param {*} value - The value to test.
	 * @returns {Boolean}
	 * @since 2.7.0
	 */
	isObject: function(value) {
		return value !== null && Object.prototype.toString.call(value) === '[object Object]';
	},

	/**
	 * Returns `value` if defined, else returns `defaultValue`.
	 * @param {*} value - The value to return if defined.
	 * @param {*} defaultValue - The value to return if `value` is undefined.
	 * @returns {*}
	 */
	valueOrDefault: function(value, defaultValue) {
		return typeof value === 'undefined' ? defaultValue : value;
	},

	/**
	 * Returns value at the given `index` in array if defined, else returns `defaultValue`.
	 * @param {Array} value - The array to lookup for value at `index`.
	 * @param {Number} index - The index in `value` to lookup for value.
	 * @param {*} defaultValue - The value to return if `value[index]` is undefined.
	 * @returns {*}
	 */
	valueAtIndexOrDefault: function(value, index, defaultValue) {
		return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue);
	},

	/**
	 * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the
	 * value returned by `fn`. If `fn` is not a function, this method returns undefined.
	 * @param {Function} fn - The function to call.
	 * @param {Array|undefined|null} args - The arguments with which `fn` should be called.
	 * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.
	 * @returns {*}
	 */
	callback: function(fn, args, thisArg) {
		if (fn && typeof fn.call === 'function') {
			return fn.apply(thisArg, args);
		}
	},

	/**
	 * Note(SB) for performance sake, this method should only be used when loopable type
	 * is unknown or in none intensive code (not called often and small loopable). Else
	 * it's preferable to use a regular for() loop and save extra function calls.
	 * @param {Object|Array} loopable - The object or array to be iterated.
	 * @param {Function} fn - The function to call for each item.
	 * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.
	 * @param {Boolean} [reverse] - If true, iterates backward on the loopable.
	 */
	each: function(loopable, fn, thisArg, reverse) {
		var i, len, keys;
		if (helpers.isArray(loopable)) {
			len = loopable.length;
			if (reverse) {
				for (i = len - 1; i >= 0; i--) {
					fn.call(thisArg, loopable[i], i);
				}
			} else {
				for (i = 0; i < len; i++) {
					fn.call(thisArg, loopable[i], i);
				}
			}
		} else if (helpers.isObject(loopable)) {
			keys = Object.keys(loopable);
			len = keys.length;
			for (i = 0; i < len; i++) {
				fn.call(thisArg, loopable[keys[i]], keys[i]);
			}
		}
	},

	/**
	 * Returns true if the `a0` and `a1` arrays have the same content, else returns false.
	 * @see http://stackoverflow.com/a/14853974
	 * @param {Array} a0 - The array to compare
	 * @param {Array} a1 - The array to compare
	 * @returns {Boolean}
	 */
	arrayEquals: function(a0, a1) {
		var i, ilen, v0, v1;

		if (!a0 || !a1 || a0.length !== a1.length) {
			return false;
		}

		for (i = 0, ilen = a0.length; i < ilen; ++i) {
			v0 = a0[i];
			v1 = a1[i];

			if (v0 instanceof Array && v1 instanceof Array) {
				if (!helpers.arrayEquals(v0, v1)) {
					return false;
				}
			} else if (v0 !== v1) {
				// NOTE: two different object instances will never be equal: {x:20} != {x:20}
				return false;
			}
		}

		return true;
	},

	/**
	 * Returns a deep copy of `source` without keeping references on objects and arrays.
	 * @param {*} source - The value to clone.
	 * @returns {*}
	 */
	clone: function(source) {
		if (helpers.isArray(source)) {
			return source.map(helpers.clone);
		}

		if (helpers.isObject(source)) {
			var target = {};
			var keys = Object.keys(source);
			var klen = keys.length;
			var k = 0;

			for (; k < klen; ++k) {
				target[keys[k]] = helpers.clone(source[keys[k]]);
			}

			return target;
		}

		return source;
	},

	/**
	 * The default merger when Chart.helpers.merge is called without merger option.
	 * Note(SB): this method is also used by configMerge and scaleMerge as fallback.
	 * @private
	 */
	_merger: function(key, target, source, options) {
		var tval = target[key];
		var sval = source[key];

		if (helpers.isObject(tval) && helpers.isObject(sval)) {
			helpers.merge(tval, sval, options);
		} else {
			target[key] = helpers.clone(sval);
		}
	},

	/**
	 * Merges source[key] in target[key] only if target[key] is undefined.
	 * @private
	 */
	_mergerIf: function(key, target, source) {
		var tval = target[key];
		var sval = source[key];

		if (helpers.isObject(tval) && helpers.isObject(sval)) {
			helpers.mergeIf(tval, sval);
		} else if (!target.hasOwnProperty(key)) {
			target[key] = helpers.clone(sval);
		}
	},

	/**
	 * Recursively deep copies `source` properties into `target` with the given `options`.
	 * IMPORTANT: `target` is not cloned and will be updated with `source` properties.
	 * @param {Object} target - The target object in which all sources are merged into.
	 * @param {Object|Array(Object)} source - Object(s) to merge into `target`.
	 * @param {Object} [options] - Merging options:
	 * @param {Function} [options.merger] - The merge method (key, target, source, options)
	 * @returns {Object} The `target` object.
	 */
	merge: function(target, source, options) {
		var sources = helpers.isArray(source) ? source : [source];
		var ilen = sources.length;
		var merge, i, keys, klen, k;

		if (!helpers.isObject(target)) {
			return target;
		}

		options = options || {};
		merge = options.merger || helpers._merger;

		for (i = 0; i < ilen; ++i) {
			source = sources[i];
			if (!helpers.isObject(source)) {
				continue;
			}

			keys = Object.keys(source);
			for (k = 0, klen = keys.length; k < klen; ++k) {
				merge(keys[k], target, source, options);
			}
		}

		return target;
	},

	/**
	 * Recursively deep copies `source` properties into `target` *only* if not defined in target.
	 * IMPORTANT: `target` is not cloned and will be updated with `source` properties.
	 * @param {Object} target - The target object in which all sources are merged into.
	 * @param {Object|Array(Object)} source - Object(s) to merge into `target`.
	 * @returns {Object} The `target` object.
	 */
	mergeIf: function(target, source) {
		return helpers.merge(target, source, {merger: helpers._mergerIf});
	}
};

module.exports = helpers;

// DEPRECATIONS

/**
 * Provided for backward compatibility, use Chart.helpers.callback instead.
 * @function Chart.helpers.callCallback
 * @deprecated since version 2.6.0
 * @todo remove at version 3
 * @private
 */
helpers.callCallback = helpers.callback;

/**
 * Provided for backward compatibility, use Array.prototype.indexOf instead.
 * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+
 * @function Chart.helpers.indexOf
 * @deprecated since version 2.7.0
 * @todo remove at version 3
 * @private
 */
helpers.indexOf = function(array, item, fromIndex) {
	return Array.prototype.indexOf.call(array, item, fromIndex);
};

/**
 * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead.
 * @function Chart.helpers.getValueOrDefault
 * @deprecated since version 2.7.0
 * @todo remove at version 3
 * @private
 */
helpers.getValueOrDefault = helpers.valueOrDefault;

/**
 * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead.
 * @function Chart.helpers.getValueAtIndexOrDefault
 * @deprecated since version 2.7.0
 * @todo remove at version 3
 * @private
 */
helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault;

},{}],43:[function(require,module,exports){
'use strict';

var helpers = require(42);

/**
 * Easing functions adapted from Robert Penner's easing equations.
 * @namespace Chart.helpers.easingEffects
 * @see http://www.robertpenner.com/easing/
 */
var effects = {
	linear: function(t) {
		return t;
	},

	easeInQuad: function(t) {
		return t * t;
	},

	easeOutQuad: function(t) {
		return -t * (t - 2);
	},

	easeInOutQuad: function(t) {
		if ((t /= 0.5) < 1) {
			return 0.5 * t * t;
		}
		return -0.5 * ((--t) * (t - 2) - 1);
	},

	easeInCubic: function(t) {
		return t * t * t;
	},

	easeOutCubic: function(t) {
		return (t = t - 1) * t * t + 1;
	},

	easeInOutCubic: function(t) {
		if ((t /= 0.5) < 1) {
			return 0.5 * t * t * t;
		}
		return 0.5 * ((t -= 2) * t * t + 2);
	},

	easeInQuart: function(t) {
		return t * t * t * t;
	},

	easeOutQuart: function(t) {
		return -((t = t - 1) * t * t * t - 1);
	},

	easeInOutQuart: function(t) {
		if ((t /= 0.5) < 1) {
			return 0.5 * t * t * t * t;
		}
		return -0.5 * ((t -= 2) * t * t * t - 2);
	},

	easeInQuint: function(t) {
		return t * t * t * t * t;
	},

	easeOutQuint: function(t) {
		return (t = t - 1) * t * t * t * t + 1;
	},

	easeInOutQuint: function(t) {
		if ((t /= 0.5) < 1) {
			return 0.5 * t * t * t * t * t;
		}
		return 0.5 * ((t -= 2) * t * t * t * t + 2);
	},

	easeInSine: function(t) {
		return -Math.cos(t * (Math.PI / 2)) + 1;
	},

	easeOutSine: function(t) {
		return Math.sin(t * (Math.PI / 2));
	},

	easeInOutSine: function(t) {
		return -0.5 * (Math.cos(Math.PI * t) - 1);
	},

	easeInExpo: function(t) {
		return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));
	},

	easeOutExpo: function(t) {
		return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1;
	},

	easeInOutExpo: function(t) {
		if (t === 0) {
			return 0;
		}
		if (t === 1) {
			return 1;
		}
		if ((t /= 0.5) < 1) {
			return 0.5 * Math.pow(2, 10 * (t - 1));
		}
		return 0.5 * (-Math.pow(2, -10 * --t) + 2);
	},

	easeInCirc: function(t) {
		if (t >= 1) {
			return t;
		}
		return -(Math.sqrt(1 - t * t) - 1);
	},

	easeOutCirc: function(t) {
		return Math.sqrt(1 - (t = t - 1) * t);
	},

	easeInOutCirc: function(t) {
		if ((t /= 0.5) < 1) {
			return -0.5 * (Math.sqrt(1 - t * t) - 1);
		}
		return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);
	},

	easeInElastic: function(t) {
		var s = 1.70158;
		var p = 0;
		var a = 1;
		if (t === 0) {
			return 0;
		}
		if (t === 1) {
			return 1;
		}
		if (!p) {
			p = 0.3;
		}
		if (a < 1) {
			a = 1;
			s = p / 4;
		} else {
			s = p / (2 * Math.PI) * Math.asin(1 / a);
		}
		return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
	},

	easeOutElastic: function(t) {
		var s = 1.70158;
		var p = 0;
		var a = 1;
		if (t === 0) {
			return 0;
		}
		if (t === 1) {
			return 1;
		}
		if (!p) {
			p = 0.3;
		}
		if (a < 1) {
			a = 1;
			s = p / 4;
		} else {
			s = p / (2 * Math.PI) * Math.asin(1 / a);
		}
		return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1;
	},

	easeInOutElastic: function(t) {
		var s = 1.70158;
		var p = 0;
		var a = 1;
		if (t === 0) {
			return 0;
		}
		if ((t /= 0.5) === 2) {
			return 1;
		}
		if (!p) {
			p = 0.45;
		}
		if (a < 1) {
			a = 1;
			s = p / 4;
		} else {
			s = p / (2 * Math.PI) * Math.asin(1 / a);
		}
		if (t < 1) {
			return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
		}
		return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1;
	},
	easeInBack: function(t) {
		var s = 1.70158;
		return t * t * ((s + 1) * t - s);
	},

	easeOutBack: function(t) {
		var s = 1.70158;
		return (t = t - 1) * t * ((s + 1) * t + s) + 1;
	},

	easeInOutBack: function(t) {
		var s = 1.70158;
		if ((t /= 0.5) < 1) {
			return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s));
		}
		return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
	},

	easeInBounce: function(t) {
		return 1 - effects.easeOutBounce(1 - t);
	},

	easeOutBounce: function(t) {
		if (t < (1 / 2.75)) {
			return 7.5625 * t * t;
		}
		if (t < (2 / 2.75)) {
			return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75;
		}
		if (t < (2.5 / 2.75)) {
			return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375;
		}
		return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375;
	},

	easeInOutBounce: function(t) {
		if (t < 0.5) {
			return effects.easeInBounce(t * 2) * 0.5;
		}
		return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5;
	}
};

module.exports = {
	effects: effects
};

// DEPRECATIONS

/**
 * Provided for backward compatibility, use Chart.helpers.easing.effects instead.
 * @function Chart.helpers.easingEffects
 * @deprecated since version 2.7.0
 * @todo remove at version 3
 * @private
 */
helpers.easingEffects = effects;

},{"42":42}],44:[function(require,module,exports){
'use strict';

var helpers = require(42);

/**
 * @alias Chart.helpers.options
 * @namespace
 */
module.exports = {
	/**
	 * Converts the given line height `value` in pixels for a specific font `size`.
	 * @param {Number|String} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
	 * @param {Number} size - The font size (in pixels) used to resolve relative `value`.
	 * @returns {Number} The effective line height in pixels (size * 1.2 if value is invalid).
	 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
	 * @since 2.7.0
	 */
	toLineHeight: function(value, size) {
		var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);
		if (!matches || matches[1] === 'normal') {
			return size * 1.2;
		}

		value = +matches[2];

		switch (matches[3]) {
		case 'px':
			return value;
		case '%':
			value /= 100;
			break;
		default:
			break;
		}

		return size * value;
	},

	/**
	 * Converts the given value into a padding object with pre-computed width/height.
	 * @param {Number|Object} value - If a number, set the value to all TRBL component,
	 *  else, if and object, use defined properties and sets undefined ones to 0.
	 * @returns {Object} The padding values (top, right, bottom, left, width, height)
	 * @since 2.7.0
	 */
	toPadding: function(value) {
		var t, r, b, l;

		if (helpers.isObject(value)) {
			t = +value.top || 0;
			r = +value.right || 0;
			b = +value.bottom || 0;
			l = +value.left || 0;
		} else {
			t = r = b = l = +value || 0;
		}

		return {
			top: t,
			right: r,
			bottom: b,
			left: l,
			height: t + b,
			width: l + r
		};
	},

	/**
	 * Evaluates the given `inputs` sequentially and returns the first defined value.
	 * @param {Array[]} inputs - An array of values, falling back to the last value.
	 * @param {Object} [context] - If defined and the current value is a function, the value
	 * is called with `context` as first argument and the result becomes the new input.
	 * @param {Number} [index] - If defined and the current value is an array, the value
	 * at `index` become the new input.
	 * @since 2.7.0
	 */
	resolve: function(inputs, context, index) {
		var i, ilen, value;

		for (i = 0, ilen = inputs.length; i < ilen; ++i) {
			value = inputs[i];
			if (value === undefined) {
				continue;
			}
			if (context !== undefined && typeof value === 'function') {
				value = value(context);
			}
			if (index !== undefined && helpers.isArray(value)) {
				value = value[index];
			}
			if (value !== undefined) {
				return value;
			}
		}
	}
};

},{"42":42}],45:[function(require,module,exports){
'use strict';

module.exports = require(42);
module.exports.easing = require(43);
module.exports.canvas = require(41);
module.exports.options = require(44);

},{"41":41,"42":42,"43":43,"44":44}],46:[function(require,module,exports){
/**
 * Platform fallback implementation (minimal).
 * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939
 */

module.exports = {
	acquireContext: function(item) {
		if (item && item.canvas) {
			// Support for any object associated to a canvas (including a context2d)
			item = item.canvas;
		}

		return item && item.getContext('2d') || null;
	}
};

},{}],47:[function(require,module,exports){
/**
 * Chart.Platform implementation for targeting a web browser
 */

'use strict';

var helpers = require(45);

var EXPANDO_KEY = '$chartjs';
var CSS_PREFIX = 'chartjs-';
var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor';
var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation';
var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart'];

/**
 * DOM event types -> Chart.js event types.
 * Note: only events with different types are mapped.
 * @see https://developer.mozilla.org/en-US/docs/Web/Events
 */
var EVENT_TYPES = {
	touchstart: 'mousedown',
	touchmove: 'mousemove',
	touchend: 'mouseup',
	pointerenter: 'mouseenter',
	pointerdown: 'mousedown',
	pointermove: 'mousemove',
	pointerup: 'mouseup',
	pointerleave: 'mouseout',
	pointerout: 'mouseout'
};

/**
 * The "used" size is the final value of a dimension property after all calculations have
 * been performed. This method uses the computed style of `element` but returns undefined
 * if the computed style is not expressed in pixels. That can happen in some cases where
 * `element` has a size relative to its parent and this last one is not yet displayed,
 * for example because of `display: none` on a parent node.
 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
 * @returns {Number} Size in pixels or undefined if unknown.
 */
function readUsedSize(element, property) {
	var value = helpers.getStyle(element, property);
	var matches = value && value.match(/^(\d+)(\.\d+)?px$/);
	return matches ? Number(matches[1]) : undefined;
}

/**
 * Initializes the canvas style and render size without modifying the canvas display size,
 * since responsiveness is handled by the controller.resize() method. The config is used
 * to determine the aspect ratio to apply in case no explicit height has been specified.
 */
function initCanvas(canvas, config) {
	var style = canvas.style;

	// NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it
	// returns null or '' if no explicit value has been set to the canvas attribute.
	var renderHeight = canvas.getAttribute('height');
	var renderWidth = canvas.getAttribute('width');

	// Chart.js modifies some canvas values that we want to restore on destroy
	canvas[EXPANDO_KEY] = {
		initial: {
			height: renderHeight,
			width: renderWidth,
			style: {
				display: style.display,
				height: style.height,
				width: style.width
			}
		}
	};

	// Force canvas to display as block to avoid extra space caused by inline
	// elements, which would interfere with the responsive resize process.
	// https://github.com/chartjs/Chart.js/issues/2538
	style.display = style.display || 'block';

	if (renderWidth === null || renderWidth === '') {
		var displayWidth = readUsedSize(canvas, 'width');
		if (displayWidth !== undefined) {
			canvas.width = displayWidth;
		}
	}

	if (renderHeight === null || renderHeight === '') {
		if (canvas.style.height === '') {
			// If no explicit render height and style height, let's apply the aspect ratio,
			// which one can be specified by the user but also by charts as default option
			// (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.
			canvas.height = canvas.width / (config.options.aspectRatio || 2);
		} else {
			var displayHeight = readUsedSize(canvas, 'height');
			if (displayWidth !== undefined) {
				canvas.height = displayHeight;
			}
		}
	}

	return canvas;
}

/**
 * Detects support for options object argument in addEventListener.
 * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
 * @private
 */
var supportsEventListenerOptions = (function() {
	var supports = false;
	try {
		var options = Object.defineProperty({}, 'passive', {
			get: function() {
				supports = true;
			}
		});
		window.addEventListener('e', null, options);
	} catch (e) {
		// continue regardless of error
	}
	return supports;
}());

// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events.
// https://github.com/chartjs/Chart.js/issues/4287
var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false;

function addEventListener(node, type, listener) {
	node.addEventListener(type, listener, eventListenerOptions);
}

function removeEventListener(node, type, listener) {
	node.removeEventListener(type, listener, eventListenerOptions);
}

function createEvent(type, chart, x, y, nativeEvent) {
	return {
		type: type,
		chart: chart,
		native: nativeEvent || null,
		x: x !== undefined ? x : null,
		y: y !== undefined ? y : null,
	};
}

function fromNativeEvent(event, chart) {
	var type = EVENT_TYPES[event.type] || event.type;
	var pos = helpers.getRelativePosition(event, chart);
	return createEvent(type, chart, pos.x, pos.y, event);
}

function throttled(fn, thisArg) {
	var ticking = false;
	var args = [];

	return function() {
		args = Array.prototype.slice.call(arguments);
		thisArg = thisArg || this;

		if (!ticking) {
			ticking = true;
			helpers.requestAnimFrame.call(window, function() {
				ticking = false;
				fn.apply(thisArg, args);
			});
		}
	};
}

// Implementation based on https://github.com/marcj/css-element-queries
function createResizer(handler) {
	var resizer = document.createElement('div');
	var cls = CSS_PREFIX + 'size-monitor';
	var maxSize = 1000000;
	var style =
		'position:absolute;' +
		'left:0;' +
		'top:0;' +
		'right:0;' +
		'bottom:0;' +
		'overflow:hidden;' +
		'pointer-events:none;' +
		'visibility:hidden;' +
		'z-index:-1;';

	resizer.style.cssText = style;
	resizer.className = cls;
	resizer.innerHTML =
		'<div class="' + cls + '-expand" style="' + style + '">' +
			'<div style="' +
				'position:absolute;' +
				'width:' + maxSize + 'px;' +
				'height:' + maxSize + 'px;' +
				'left:0;' +
				'top:0">' +
			'</div>' +
		'</div>' +
		'<div class="' + cls + '-shrink" style="' + style + '">' +
			'<div style="' +
				'position:absolute;' +
				'width:200%;' +
				'height:200%;' +
				'left:0; ' +
				'top:0">' +
			'</div>' +
		'</div>';

	var expand = resizer.childNodes[0];
	var shrink = resizer.childNodes[1];

	resizer._reset = function() {
		expand.scrollLeft = maxSize;
		expand.scrollTop = maxSize;
		shrink.scrollLeft = maxSize;
		shrink.scrollTop = maxSize;
	};
	var onScroll = function() {
		resizer._reset();
		handler();
	};

	addEventListener(expand, 'scroll', onScroll.bind(expand, 'expand'));
	addEventListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink'));

	return resizer;
}

// https://davidwalsh.name/detect-node-insertion
function watchForRender(node, handler) {
	var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
	var proxy = expando.renderProxy = function(e) {
		if (e.animationName === CSS_RENDER_ANIMATION) {
			handler();
		}
	};

	helpers.each(ANIMATION_START_EVENTS, function(type) {
		addEventListener(node, type, proxy);
	});

	node.classList.add(CSS_RENDER_MONITOR);
}

function unwatchForRender(node) {
	var expando = node[EXPANDO_KEY] || {};
	var proxy = expando.renderProxy;

	if (proxy) {
		helpers.each(ANIMATION_START_EVENTS, function(type) {
			removeEventListener(node, type, proxy);
		});

		delete expando.renderProxy;
	}

	node.classList.remove(CSS_RENDER_MONITOR);
}

function addResizeListener(node, listener, chart) {
	var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});

	// Let's keep track of this added resizer and thus avoid DOM query when removing it.
	var resizer = expando.resizer = createResizer(throttled(function() {
		if (expando.resizer) {
			return listener(createEvent('resize', chart));
		}
	}));

	// The resizer needs to be attached to the node parent, so we first need to be
	// sure that `node` is attached to the DOM before injecting the resizer element.
	watchForRender(node, function() {
		if (expando.resizer) {
			var container = node.parentNode;
			if (container && container !== resizer.parentNode) {
				container.insertBefore(resizer, container.firstChild);
			}

			// The container size might have changed, let's reset the resizer state.
			resizer._reset();
		}
	});
}

function removeResizeListener(node) {
	var expando = node[EXPANDO_KEY] || {};
	var resizer = expando.resizer;

	delete expando.resizer;
	unwatchForRender(node);

	if (resizer && resizer.parentNode) {
		resizer.parentNode.removeChild(resizer);
	}
}

function injectCSS(platform, css) {
	// http://stackoverflow.com/q/3922139
	var style = platform._style || document.createElement('style');
	if (!platform._style) {
		platform._style = style;
		css = '/* Chart.js */\n' + css;
		style.setAttribute('type', 'text/css');
		document.getElementsByTagName('head')[0].appendChild(style);
	}

	style.appendChild(document.createTextNode(css));
}

module.exports = {
	/**
	 * This property holds whether this platform is enabled for the current environment.
	 * Currently used by platform.js to select the proper implementation.
	 * @private
	 */
	_enabled: typeof window !== 'undefined' && typeof document !== 'undefined',

	initialize: function() {
		var keyframes = 'from{opacity:0.99}to{opacity:1}';

		injectCSS(this,
			// DOM rendering detection
			// https://davidwalsh.name/detect-node-insertion
			'@-webkit-keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' +
			'@keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' +
			'.' + CSS_RENDER_MONITOR + '{' +
				'-webkit-animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' +
				'animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' +
			'}'
		);
	},

	acquireContext: function(item, config) {
		if (typeof item === 'string') {
			item = document.getElementById(item);
		} else if (item.length) {
			// Support for array based queries (such as jQuery)
			item = item[0];
		}

		if (item && item.canvas) {
			// Support for any object associated to a canvas (including a context2d)
			item = item.canvas;
		}

		// To prevent canvas fingerprinting, some add-ons undefine the getContext
		// method, for example: https://github.com/kkapsner/CanvasBlocker
		// https://github.com/chartjs/Chart.js/issues/2807
		var context = item && item.getContext && item.getContext('2d');

		// `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is
		// inside an iframe or when running in a protected environment. We could guess the
		// types from their toString() value but let's keep things flexible and assume it's
		// a sufficient condition if the item has a context2D which has item as `canvas`.
		// https://github.com/chartjs/Chart.js/issues/3887
		// https://github.com/chartjs/Chart.js/issues/4102
		// https://github.com/chartjs/Chart.js/issues/4152
		if (context && context.canvas === item) {
			initCanvas(item, config);
			return context;
		}

		return null;
	},

	releaseContext: function(context) {
		var canvas = context.canvas;
		if (!canvas[EXPANDO_KEY]) {
			return;
		}

		var initial = canvas[EXPANDO_KEY].initial;
		['height', 'width'].forEach(function(prop) {
			var value = initial[prop];
			if (helpers.isNullOrUndef(value)) {
				canvas.removeAttribute(prop);
			} else {
				canvas.setAttribute(prop, value);
			}
		});

		helpers.each(initial.style || {}, function(value, key) {
			canvas.style[key] = value;
		});

		// The canvas render size might have been changed (and thus the state stack discarded),
		// we can't use save() and restore() to restore the initial state. So make sure that at
		// least the canvas context is reset to the default state by setting the canvas width.
		// https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html
		canvas.width = canvas.width;

		delete canvas[EXPANDO_KEY];
	},

	addEventListener: function(chart, type, listener) {
		var canvas = chart.canvas;
		if (type === 'resize') {
			// Note: the resize event is not supported on all browsers.
			addResizeListener(canvas, listener, chart);
			return;
		}

		var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {});
		var proxies = expando.proxies || (expando.proxies = {});
		var proxy = proxies[chart.id + '_' + type] = function(event) {
			listener(fromNativeEvent(event, chart));
		};

		addEventListener(canvas, type, proxy);
	},

	removeEventListener: function(chart, type, listener) {
		var canvas = chart.canvas;
		if (type === 'resize') {
			// Note: the resize event is not supported on all browsers.
			removeResizeListener(canvas, listener);
			return;
		}

		var expando = listener[EXPANDO_KEY] || {};
		var proxies = expando.proxies || {};
		var proxy = proxies[chart.id + '_' + type];
		if (!proxy) {
			return;
		}

		removeEventListener(canvas, type, proxy);
	}
};

// DEPRECATIONS

/**
 * Provided for backward compatibility, use EventTarget.addEventListener instead.
 * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
 * @function Chart.helpers.addEvent
 * @deprecated since version 2.7.0
 * @todo remove at version 3
 * @private
 */
helpers.addEvent = addEventListener;

/**
 * Provided for backward compatibility, use EventTarget.removeEventListener instead.
 * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
 * @function Chart.helpers.removeEvent
 * @deprecated since version 2.7.0
 * @todo remove at version 3
 * @private
 */
helpers.removeEvent = removeEventListener;

},{"45":45}],48:[function(require,module,exports){
'use strict';

var helpers = require(45);
var basic = require(46);
var dom = require(47);

// @TODO Make possible to select another platform at build time.
var implementation = dom._enabled ? dom : basic;

/**
 * @namespace Chart.platform
 * @see https://chartjs.gitbooks.io/proposals/content/Platform.html
 * @since 2.4.0
 */
module.exports = helpers.extend({
	/**
	 * @since 2.7.0
	 */
	initialize: function() {},

	/**
	 * Called at chart construction time, returns a context2d instance implementing
	 * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.
	 * @param {*} item - The native item from which to acquire context (platform specific)
	 * @param {Object} options - The chart options
	 * @returns {CanvasRenderingContext2D} context2d instance
	 */
	acquireContext: function() {},

	/**
	 * Called at chart destruction time, releases any resources associated to the context
	 * previously returned by the acquireContext() method.
	 * @param {CanvasRenderingContext2D} context - The context2d instance
	 * @returns {Boolean} true if the method succeeded, else false
	 */
	releaseContext: function() {},

	/**
	 * Registers the specified listener on the given chart.
	 * @param {Chart} chart - Chart from which to listen for event
	 * @param {String} type - The ({@link IEvent}) type to listen for
	 * @param {Function} listener - Receives a notification (an object that implements
	 * the {@link IEvent} interface) when an event of the specified type occurs.
	 */
	addEventListener: function() {},

	/**
	 * Removes the specified listener previously registered with addEventListener.
	 * @param {Chart} chart -Chart from which to remove the listener
	 * @param {String} type - The ({@link IEvent}) type to remove
	 * @param {Function} listener - The listener function to remove from the event target.
	 */
	removeEventListener: function() {}

}, implementation);

/**
 * @interface IPlatform
 * Allows abstracting platform dependencies away from the chart
 * @borrows Chart.platform.acquireContext as acquireContext
 * @borrows Chart.platform.releaseContext as releaseContext
 * @borrows Chart.platform.addEventListener as addEventListener
 * @borrows Chart.platform.removeEventListener as removeEventListener
 */

/**
 * @interface IEvent
 * @prop {String} type - The event type name, possible values are:
 * 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout',
 * 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize'
 * @prop {*} native - The original native event (null for emulated events, e.g. 'resize')
 * @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events)
 * @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events)
 */

},{"45":45,"46":46,"47":47}],49:[function(require,module,exports){
/**
 * Plugin based on discussion from the following Chart.js issues:
 * @see https://github.com/chartjs/Chart.js/issues/2380#issuecomment-279961569
 * @see https://github.com/chartjs/Chart.js/issues/2440#issuecomment-256461897
 */

'use strict';

var defaults = require(25);
var elements = require(40);
var helpers = require(45);

defaults._set('global', {
	plugins: {
		filler: {
			propagate: true
		}
	}
});

module.exports = function() {

	var mappers = {
		dataset: function(source) {
			var index = source.fill;
			var chart = source.chart;
			var meta = chart.getDatasetMeta(index);
			var visible = meta && chart.isDatasetVisible(index);
			var points = (visible && meta.dataset._children) || [];
			var length = points.length || 0;

			return !length ? null : function(point, i) {
				return (i < length && points[i]._view) || null;
			};
		},

		boundary: function(source) {
			var boundary = source.boundary;
			var x = boundary ? boundary.x : null;
			var y = boundary ? boundary.y : null;

			return function(point) {
				return {
					x: x === null ? point.x : x,
					y: y === null ? point.y : y,
				};
			};
		}
	};

	// @todo if (fill[0] === '#')
	function decodeFill(el, index, count) {
		var model = el._model || {};
		var fill = model.fill;
		var target;

		if (fill === undefined) {
			fill = !!model.backgroundColor;
		}

		if (fill === false || fill === null) {
			return false;
		}

		if (fill === true) {
			return 'origin';
		}

		target = parseFloat(fill, 10);
		if (isFinite(target) && Math.floor(target) === target) {
			if (fill[0] === '-' || fill[0] === '+') {
				target = index + target;
			}

			if (target === index || target < 0 || target >= count) {
				return false;
			}

			return target;
		}

		switch (fill) {
		// compatibility
		case 'bottom':
			return 'start';
		case 'top':
			return 'end';
		case 'zero':
			return 'origin';
		// supported boundaries
		case 'origin':
		case 'start':
		case 'end':
			return fill;
		// invalid fill values
		default:
			return false;
		}
	}

	function computeBoundary(source) {
		var model = source.el._model || {};
		var scale = source.el._scale || {};
		var fill = source.fill;
		var target = null;
		var horizontal;

		if (isFinite(fill)) {
			return null;
		}

		// Backward compatibility: until v3, we still need to support boundary values set on
		// the model (scaleTop, scaleBottom and scaleZero) because some external plugins and
		// controllers might still use it (e.g. the Smith chart).

		if (fill === 'start') {
			target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom;
		} else if (fill === 'end') {
			target = model.scaleTop === undefined ? scale.top : model.scaleTop;
		} else if (model.scaleZero !== undefined) {
			target = model.scaleZero;
		} else if (scale.getBasePosition) {
			target = scale.getBasePosition();
		} else if (scale.getBasePixel) {
			target = scale.getBasePixel();
		}

		if (target !== undefined && target !== null) {
			if (target.x !== undefined && target.y !== undefined) {
				return target;
			}

			if (typeof target === 'number' && isFinite(target)) {
				horizontal = scale.isHorizontal();
				return {
					x: horizontal ? target : null,
					y: horizontal ? null : target
				};
			}
		}

		return null;
	}

	function resolveTarget(sources, index, propagate) {
		var source = sources[index];
		var fill = source.fill;
		var visited = [index];
		var target;

		if (!propagate) {
			return fill;
		}

		while (fill !== false && visited.indexOf(fill) === -1) {
			if (!isFinite(fill)) {
				return fill;
			}

			target = sources[fill];
			if (!target) {
				return false;
			}

			if (target.visible) {
				return fill;
			}

			visited.push(fill);
			fill = target.fill;
		}

		return false;
	}

	function createMapper(source) {
		var fill = source.fill;
		var type = 'dataset';

		if (fill === false) {
			return null;
		}

		if (!isFinite(fill)) {
			type = 'boundary';
		}

		return mappers[type](source);
	}

	function isDrawable(point) {
		return point && !point.skip;
	}

	function drawArea(ctx, curve0, curve1, len0, len1) {
		var i;

		if (!len0 || !len1) {
			return;
		}

		// building first area curve (normal)
		ctx.moveTo(curve0[0].x, curve0[0].y);
		for (i = 1; i < len0; ++i) {
			helpers.canvas.lineTo(ctx, curve0[i - 1], curve0[i]);
		}

		// joining the two area curves
		ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y);

		// building opposite area curve (reverse)
		for (i = len1 - 1; i > 0; --i) {
			helpers.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true);
		}
	}

	function doFill(ctx, points, mapper, view, color, loop) {
		var count = points.length;
		var span = view.spanGaps;
		var curve0 = [];
		var curve1 = [];
		var len0 = 0;
		var len1 = 0;
		var i, ilen, index, p0, p1, d0, d1;

		ctx.beginPath();

		for (i = 0, ilen = (count + !!loop); i < ilen; ++i) {
			index = i % count;
			p0 = points[index]._view;
			p1 = mapper(p0, index, view);
			d0 = isDrawable(p0);
			d1 = isDrawable(p1);

			if (d0 && d1) {
				len0 = curve0.push(p0);
				len1 = curve1.push(p1);
			} else if (len0 && len1) {
				if (!span) {
					drawArea(ctx, curve0, curve1, len0, len1);
					len0 = len1 = 0;
					curve0 = [];
					curve1 = [];
				} else {
					if (d0) {
						curve0.push(p0);
					}
					if (d1) {
						curve1.push(p1);
					}
				}
			}
		}

		drawArea(ctx, curve0, curve1, len0, len1);

		ctx.closePath();
		ctx.fillStyle = color;
		ctx.fill();
	}

	return {
		id: 'filler',

		afterDatasetsUpdate: function(chart, options) {
			var count = (chart.data.datasets || []).length;
			var propagate = options.propagate;
			var sources = [];
			var meta, i, el, source;

			for (i = 0; i < count; ++i) {
				meta = chart.getDatasetMeta(i);
				el = meta.dataset;
				source = null;

				if (el && el._model && el instanceof elements.Line) {
					source = {
						visible: chart.isDatasetVisible(i),
						fill: decodeFill(el, i, count),
						chart: chart,
						el: el
					};
				}

				meta.$filler = source;
				sources.push(source);
			}

			for (i = 0; i < count; ++i) {
				source = sources[i];
				if (!source) {
					continue;
				}

				source.fill = resolveTarget(sources, i, propagate);
				source.boundary = computeBoundary(source);
				source.mapper = createMapper(source);
			}
		},

		beforeDatasetDraw: function(chart, args) {
			var meta = args.meta.$filler;
			if (!meta) {
				return;
			}

			var ctx = chart.ctx;
			var el = meta.el;
			var view = el._view;
			var points = el._children || [];
			var mapper = meta.mapper;
			var color = view.backgroundColor || defaults.global.defaultColor;

			if (mapper && color && points.length) {
				helpers.canvas.clipArea(ctx, chart.chartArea);
				doFill(ctx, points, mapper, view, color, el._loop);
				helpers.canvas.unclipArea(ctx);
			}
		}
	};
};

},{"25":25,"40":40,"45":45}],50:[function(require,module,exports){
'use strict';

var defaults = require(25);
var Element = require(26);
var helpers = require(45);

defaults._set('global', {
	legend: {
		display: true,
		position: 'top',
		fullWidth: true,
		reverse: false,
		weight: 1000,

		// a callback that will handle
		onClick: function(e, legendItem) {
			var index = legendItem.datasetIndex;
			var ci = this.chart;
			var meta = ci.getDatasetMeta(index);

			// See controller.isDatasetVisible comment
			meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null;

			// We hid a dataset ... rerender the chart
			ci.update();
		},

		onHover: null,

		labels: {
			boxWidth: 40,
			padding: 10,
			// Generates labels shown in the legend
			// Valid properties to return:
			// text : text to display
			// fillStyle : fill of coloured box
			// strokeStyle: stroke of coloured box
			// hidden : if this legend item refers to a hidden item
			// lineCap : cap style for line
			// lineDash
			// lineDashOffset :
			// lineJoin :
			// lineWidth :
			generateLabels: function(chart) {
				var data = chart.data;
				return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) {
					return {
						text: dataset.label,
						fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]),
						hidden: !chart.isDatasetVisible(i),
						lineCap: dataset.borderCapStyle,
						lineDash: dataset.borderDash,
						lineDashOffset: dataset.borderDashOffset,
						lineJoin: dataset.borderJoinStyle,
						lineWidth: dataset.borderWidth,
						strokeStyle: dataset.borderColor,
						pointStyle: dataset.pointStyle,

						// Below is extra data used for toggling the datasets
						datasetIndex: i
					};
				}, this) : [];
			}
		}
	},

	legendCallback: function(chart) {
		var text = [];
		text.push('<ul class="' + chart.id + '-legend">');
		for (var i = 0; i < chart.data.datasets.length; i++) {
			text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>');
			if (chart.data.datasets[i].label) {
				text.push(chart.data.datasets[i].label);
			}
			text.push('</li>');
		}
		text.push('</ul>');
		return text.join('');
	}
});

module.exports = function(Chart) {

	var layout = Chart.layoutService;
	var noop = helpers.noop;

	/**
	 * Helper function to get the box width based on the usePointStyle option
	 * @param labelopts {Object} the label options on the legend
	 * @param fontSize {Number} the label font size
	 * @return {Number} width of the color box area
	 */
	function getBoxWidth(labelOpts, fontSize) {
		return labelOpts.usePointStyle ?
			fontSize * Math.SQRT2 :
			labelOpts.boxWidth;
	}

	Chart.Legend = Element.extend({

		initialize: function(config) {
			helpers.extend(this, config);

			// Contains hit boxes for each dataset (in dataset order)
			this.legendHitBoxes = [];

			// Are we in doughnut mode which has a different data type
			this.doughnutMode = false;
		},

		// These methods are ordered by lifecycle. Utilities then follow.
		// Any function defined here is inherited by all legend types.
		// Any function can be extended by the legend type

		beforeUpdate: noop,
		update: function(maxWidth, maxHeight, margins) {
			var me = this;

			// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
			me.beforeUpdate();

			// Absorb the master measurements
			me.maxWidth = maxWidth;
			me.maxHeight = maxHeight;
			me.margins = margins;

			// Dimensions
			me.beforeSetDimensions();
			me.setDimensions();
			me.afterSetDimensions();
			// Labels
			me.beforeBuildLabels();
			me.buildLabels();
			me.afterBuildLabels();

			// Fit
			me.beforeFit();
			me.fit();
			me.afterFit();
			//
			me.afterUpdate();

			return me.minSize;
		},
		afterUpdate: noop,

		//

		beforeSetDimensions: noop,
		setDimensions: function() {
			var me = this;
			// Set the unconstrained dimension before label rotation
			if (me.isHorizontal()) {
				// Reset position before calculating rotation
				me.width = me.maxWidth;
				me.left = 0;
				me.right = me.width;
			} else {
				me.height = me.maxHeight;

				// Reset position before calculating rotation
				me.top = 0;
				me.bottom = me.height;
			}

			// Reset padding
			me.paddingLeft = 0;
			me.paddingTop = 0;
			me.paddingRight = 0;
			me.paddingBottom = 0;

			// Reset minSize
			me.minSize = {
				width: 0,
				height: 0
			};
		},
		afterSetDimensions: noop,

		//

		beforeBuildLabels: noop,
		buildLabels: function() {
			var me = this;
			var labelOpts = me.options.labels || {};
			var legendItems = helpers.callback(labelOpts.generateLabels, [me.chart], me) || [];

			if (labelOpts.filter) {
				legendItems = legendItems.filter(function(item) {
					return labelOpts.filter(item, me.chart.data);
				});
			}

			if (me.options.reverse) {
				legendItems.reverse();
			}

			me.legendItems = legendItems;
		},
		afterBuildLabels: noop,

		//

		beforeFit: noop,
		fit: function() {
			var me = this;
			var opts = me.options;
			var labelOpts = opts.labels;
			var display = opts.display;

			var ctx = me.ctx;

			var globalDefault = defaults.global;
			var valueOrDefault = helpers.valueOrDefault;
			var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);
			var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);
			var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);
			var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);

			// Reset hit boxes
			var hitboxes = me.legendHitBoxes = [];

			var minSize = me.minSize;
			var isHorizontal = me.isHorizontal();

			if (isHorizontal) {
				minSize.width = me.maxWidth; // fill all the width
				minSize.height = display ? 10 : 0;
			} else {
				minSize.width = display ? 10 : 0;
				minSize.height = me.maxHeight; // fill all the height
			}

			// Increase sizes here
			if (display) {
				ctx.font = labelFont;

				if (isHorizontal) {
					// Labels

					// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
					var lineWidths = me.lineWidths = [0];
					var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0;

					ctx.textAlign = 'left';
					ctx.textBaseline = 'top';

					helpers.each(me.legendItems, function(legendItem, i) {
						var boxWidth = getBoxWidth(labelOpts, fontSize);
						var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;

						if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) {
							totalHeight += fontSize + (labelOpts.padding);
							lineWidths[lineWidths.length] = me.left;
						}

						// Store the hitbox width and height here. Final position will be updated in `draw`
						hitboxes[i] = {
							left: 0,
							top: 0,
							width: width,
							height: fontSize
						};

						lineWidths[lineWidths.length - 1] += width + labelOpts.padding;
					});

					minSize.height += totalHeight;

				} else {
					var vPadding = labelOpts.padding;
					var columnWidths = me.columnWidths = [];
					var totalWidth = labelOpts.padding;
					var currentColWidth = 0;
					var currentColHeight = 0;
					var itemHeight = fontSize + vPadding;

					helpers.each(me.legendItems, function(legendItem, i) {
						var boxWidth = getBoxWidth(labelOpts, fontSize);
						var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;

						// If too tall, go to new column
						if (currentColHeight + itemHeight > minSize.height) {
							totalWidth += currentColWidth + labelOpts.padding;
							columnWidths.push(currentColWidth); // previous column width

							currentColWidth = 0;
							currentColHeight = 0;
						}

						// Get max width
						currentColWidth = Math.max(currentColWidth, itemWidth);
						currentColHeight += itemHeight;

						// Store the hitbox width and height here. Final position will be updated in `draw`
						hitboxes[i] = {
							left: 0,
							top: 0,
							width: itemWidth,
							height: fontSize
						};
					});

					totalWidth += currentColWidth;
					columnWidths.push(currentColWidth);
					minSize.width += totalWidth;
				}
			}

			me.width = minSize.width;
			me.height = minSize.height;
		},
		afterFit: noop,

		// Shared Methods
		isHorizontal: function() {
			return this.options.position === 'top' || this.options.position === 'bottom';
		},

		// Actually draw the legend on the canvas
		draw: function() {
			var me = this;
			var opts = me.options;
			var labelOpts = opts.labels;
			var globalDefault = defaults.global;
			var lineDefault = globalDefault.elements.line;
			var legendWidth = me.width;
			var lineWidths = me.lineWidths;

			if (opts.display) {
				var ctx = me.ctx;
				var valueOrDefault = helpers.valueOrDefault;
				var fontColor = valueOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor);
				var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);
				var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);
				var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);
				var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
				var cursor;

				// Canvas setup
				ctx.textAlign = 'left';
				ctx.textBaseline = 'middle';
				ctx.lineWidth = 0.5;
				ctx.strokeStyle = fontColor; // for strikethrough effect
				ctx.fillStyle = fontColor; // render in correct colour
				ctx.font = labelFont;

				var boxWidth = getBoxWidth(labelOpts, fontSize);
				var hitboxes = me.legendHitBoxes;

				// current position
				var drawLegendBox = function(x, y, legendItem) {
					if (isNaN(boxWidth) || boxWidth <= 0) {
						return;
					}

					// Set the ctx for the box
					ctx.save();

					ctx.fillStyle = valueOrDefault(legendItem.fillStyle, globalDefault.defaultColor);
					ctx.lineCap = valueOrDefault(legendItem.lineCap, lineDefault.borderCapStyle);
					ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset);
					ctx.lineJoin = valueOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle);
					ctx.lineWidth = valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth);
					ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, globalDefault.defaultColor);
					var isLineWidthZero = (valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0);

					if (ctx.setLineDash) {
						// IE 9 and 10 do not support line dash
						ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash));
					}

					if (opts.labels && opts.labels.usePointStyle) {
						// Recalculate x and y for drawPoint() because its expecting
						// x and y to be center of figure (instead of top left)
						var radius = fontSize * Math.SQRT2 / 2;
						var offSet = radius / Math.SQRT2;
						var centerX = x + offSet;
						var centerY = y + offSet;

						// Draw pointStyle as legend symbol
						helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
					} else {
						// Draw box as legend symbol
						if (!isLineWidthZero) {
							ctx.strokeRect(x, y, boxWidth, fontSize);
						}
						ctx.fillRect(x, y, boxWidth, fontSize);
					}

					ctx.restore();
				};
				var fillText = function(x, y, legendItem, textWidth) {
					var halfFontSize = fontSize / 2;
					var xLeft = boxWidth + halfFontSize + x;
					var yMiddle = y + halfFontSize;

					ctx.fillText(legendItem.text, xLeft, yMiddle);

					if (legendItem.hidden) {
						// Strikethrough the text if hidden
						ctx.beginPath();
						ctx.lineWidth = 2;
						ctx.moveTo(xLeft, yMiddle);
						ctx.lineTo(xLeft + textWidth, yMiddle);
						ctx.stroke();
					}
				};

				// Horizontal
				var isHorizontal = me.isHorizontal();
				if (isHorizontal) {
					cursor = {
						x: me.left + ((legendWidth - lineWidths[0]) / 2),
						y: me.top + labelOpts.padding,
						line: 0
					};
				} else {
					cursor = {
						x: me.left + labelOpts.padding,
						y: me.top + labelOpts.padding,
						line: 0
					};
				}

				var itemHeight = fontSize + labelOpts.padding;
				helpers.each(me.legendItems, function(legendItem, i) {
					var textWidth = ctx.measureText(legendItem.text).width;
					var width = boxWidth + (fontSize / 2) + textWidth;
					var x = cursor.x;
					var y = cursor.y;

					if (isHorizontal) {
						if (x + width >= legendWidth) {
							y = cursor.y += itemHeight;
							cursor.line++;
							x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2);
						}
					} else if (y + itemHeight > me.bottom) {
						x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding;
						y = cursor.y = me.top + labelOpts.padding;
						cursor.line++;
					}

					drawLegendBox(x, y, legendItem);

					hitboxes[i].left = x;
					hitboxes[i].top = y;

					// Fill the actual label
					fillText(x, y, legendItem, textWidth);

					if (isHorizontal) {
						cursor.x += width + (labelOpts.padding);
					} else {
						cursor.y += itemHeight;
					}

				});
			}
		},

		/**
		 * Handle an event
		 * @private
		 * @param {IEvent} event - The event to handle
		 * @return {Boolean} true if a change occured
		 */
		handleEvent: function(e) {
			var me = this;
			var opts = me.options;
			var type = e.type === 'mouseup' ? 'click' : e.type;
			var changed = false;

			if (type === 'mousemove') {
				if (!opts.onHover) {
					return;
				}
			} else if (type === 'click') {
				if (!opts.onClick) {
					return;
				}
			} else {
				return;
			}

			// Chart event already has relative position in it
			var x = e.x;
			var y = e.y;

			if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
				// See if we are touching one of the dataset boxes
				var lh = me.legendHitBoxes;
				for (var i = 0; i < lh.length; ++i) {
					var hitBox = lh[i];

					if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
						// Touching an element
						if (type === 'click') {
							// use e.native for backwards compatibility
							opts.onClick.call(me, e.native, me.legendItems[i]);
							changed = true;
							break;
						} else if (type === 'mousemove') {
							// use e.native for backwards compatibility
							opts.onHover.call(me, e.native, me.legendItems[i]);
							changed = true;
							break;
						}
					}
				}
			}

			return changed;
		}
	});

	function createNewLegendAndAttach(chart, legendOpts) {
		var legend = new Chart.Legend({
			ctx: chart.ctx,
			options: legendOpts,
			chart: chart
		});

		layout.configure(chart, legend, legendOpts);
		layout.addBox(chart, legend);
		chart.legend = legend;
	}

	return {
		id: 'legend',

		beforeInit: function(chart) {
			var legendOpts = chart.options.legend;

			if (legendOpts) {
				createNewLegendAndAttach(chart, legendOpts);
			}
		},

		beforeUpdate: function(chart) {
			var legendOpts = chart.options.legend;
			var legend = chart.legend;

			if (legendOpts) {
				helpers.mergeIf(legendOpts, defaults.global.legend);

				if (legend) {
					layout.configure(chart, legend, legendOpts);
					legend.options = legendOpts;
				} else {
					createNewLegendAndAttach(chart, legendOpts);
				}
			} else if (legend) {
				layout.removeBox(chart, legend);
				delete chart.legend;
			}
		},

		afterEvent: function(chart, e) {
			var legend = chart.legend;
			if (legend) {
				legend.handleEvent(e);
			}
		}
	};
};

},{"25":25,"26":26,"45":45}],51:[function(require,module,exports){
'use strict';

var defaults = require(25);
var Element = require(26);
var helpers = require(45);

defaults._set('global', {
	title: {
		display: false,
		fontStyle: 'bold',
		fullWidth: true,
		lineHeight: 1.2,
		padding: 10,
		position: 'top',
		text: '',
		weight: 2000         // by default greater than legend (1000) to be above
	}
});

module.exports = function(Chart) {

	var layout = Chart.layoutService;
	var noop = helpers.noop;

	Chart.Title = Element.extend({
		initialize: function(config) {
			var me = this;
			helpers.extend(me, config);

			// Contains hit boxes for each dataset (in dataset order)
			me.legendHitBoxes = [];
		},

		// These methods are ordered by lifecycle. Utilities then follow.

		beforeUpdate: noop,
		update: function(maxWidth, maxHeight, margins) {
			var me = this;

			// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
			me.beforeUpdate();

			// Absorb the master measurements
			me.maxWidth = maxWidth;
			me.maxHeight = maxHeight;
			me.margins = margins;

			// Dimensions
			me.beforeSetDimensions();
			me.setDimensions();
			me.afterSetDimensions();
			// Labels
			me.beforeBuildLabels();
			me.buildLabels();
			me.afterBuildLabels();

			// Fit
			me.beforeFit();
			me.fit();
			me.afterFit();
			//
			me.afterUpdate();

			return me.minSize;

		},
		afterUpdate: noop,

		//

		beforeSetDimensions: noop,
		setDimensions: function() {
			var me = this;
			// Set the unconstrained dimension before label rotation
			if (me.isHorizontal()) {
				// Reset position before calculating rotation
				me.width = me.maxWidth;
				me.left = 0;
				me.right = me.width;
			} else {
				me.height = me.maxHeight;

				// Reset position before calculating rotation
				me.top = 0;
				me.bottom = me.height;
			}

			// Reset padding
			me.paddingLeft = 0;
			me.paddingTop = 0;
			me.paddingRight = 0;
			me.paddingBottom = 0;

			// Reset minSize
			me.minSize = {
				width: 0,
				height: 0
			};
		},
		afterSetDimensions: noop,

		//

		beforeBuildLabels: noop,
		buildLabels: noop,
		afterBuildLabels: noop,

		//

		beforeFit: noop,
		fit: function() {
			var me = this;
			var valueOrDefault = helpers.valueOrDefault;
			var opts = me.options;
			var display = opts.display;
			var fontSize = valueOrDefault(opts.fontSize, defaults.global.defaultFontSize);
			var minSize = me.minSize;
			var lineCount = helpers.isArray(opts.text) ? opts.text.length : 1;
			var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);
			var textSize = display ? (lineCount * lineHeight) + (opts.padding * 2) : 0;

			if (me.isHorizontal()) {
				minSize.width = me.maxWidth; // fill all the width
				minSize.height = textSize;
			} else {
				minSize.width = textSize;
				minSize.height = me.maxHeight; // fill all the height
			}

			me.width = minSize.width;
			me.height = minSize.height;

		},
		afterFit: noop,

		// Shared Methods
		isHorizontal: function() {
			var pos = this.options.position;
			return pos === 'top' || pos === 'bottom';
		},

		// Actually draw the title block on the canvas
		draw: function() {
			var me = this;
			var ctx = me.ctx;
			var valueOrDefault = helpers.valueOrDefault;
			var opts = me.options;
			var globalDefaults = defaults.global;

			if (opts.display) {
				var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize);
				var fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle);
				var fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily);
				var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily);
				var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);
				var offset = lineHeight / 2 + opts.padding;
				var rotation = 0;
				var top = me.top;
				var left = me.left;
				var bottom = me.bottom;
				var right = me.right;
				var maxWidth, titleX, titleY;

				ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour
				ctx.font = titleFont;

				// Horizontal
				if (me.isHorizontal()) {
					titleX = left + ((right - left) / 2); // midpoint of the width
					titleY = top + offset;
					maxWidth = right - left;
				} else {
					titleX = opts.position === 'left' ? left + offset : right - offset;
					titleY = top + ((bottom - top) / 2);
					maxWidth = bottom - top;
					rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
				}

				ctx.save();
				ctx.translate(titleX, titleY);
				ctx.rotate(rotation);
				ctx.textAlign = 'center';
				ctx.textBaseline = 'middle';

				var text = opts.text;
				if (helpers.isArray(text)) {
					var y = 0;
					for (var i = 0; i < text.length; ++i) {
						ctx.fillText(text[i], 0, y, maxWidth);
						y += lineHeight;
					}
				} else {
					ctx.fillText(text, 0, 0, maxWidth);
				}

				ctx.restore();
			}
		}
	});

	function createNewTitleBlockAndAttach(chart, titleOpts) {
		var title = new Chart.Title({
			ctx: chart.ctx,
			options: titleOpts,
			chart: chart
		});

		layout.configure(chart, title, titleOpts);
		layout.addBox(chart, title);
		chart.titleBlock = title;
	}

	return {
		id: 'title',

		beforeInit: function(chart) {
			var titleOpts = chart.options.title;

			if (titleOpts) {
				createNewTitleBlockAndAttach(chart, titleOpts);
			}
		},

		beforeUpdate: function(chart) {
			var titleOpts = chart.options.title;
			var titleBlock = chart.titleBlock;

			if (titleOpts) {
				helpers.mergeIf(titleOpts, defaults.global.title);

				if (titleBlock) {
					layout.configure(chart, titleBlock, titleOpts);
					titleBlock.options = titleOpts;
				} else {
					createNewTitleBlockAndAttach(chart, titleOpts);
				}
			} else if (titleBlock) {
				Chart.layoutService.removeBox(chart, titleBlock);
				delete chart.titleBlock;
			}
		}
	};
};

},{"25":25,"26":26,"45":45}],52:[function(require,module,exports){
'use strict';

module.exports = function(Chart) {

	// Default config for a category scale
	var defaultConfig = {
		position: 'bottom'
	};

	var DatasetScale = Chart.Scale.extend({
		/**
		* Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those
		* else fall back to data.labels
		* @private
		*/
		getLabels: function() {
			var data = this.chart.data;
			return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels;
		},

		determineDataLimits: function() {
			var me = this;
			var labels = me.getLabels();
			me.minIndex = 0;
			me.maxIndex = labels.length - 1;
			var findIndex;

			if (me.options.ticks.min !== undefined) {
				// user specified min value
				findIndex = labels.indexOf(me.options.ticks.min);
				me.minIndex = findIndex !== -1 ? findIndex : me.minIndex;
			}

			if (me.options.ticks.max !== undefined) {
				// user specified max value
				findIndex = labels.indexOf(me.options.ticks.max);
				me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex;
			}

			me.min = labels[me.minIndex];
			me.max = labels[me.maxIndex];
		},

		buildTicks: function() {
			var me = this;
			var labels = me.getLabels();
			// If we are viewing some subset of labels, slice the original array
			me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1);
		},

		getLabelForIndex: function(index, datasetIndex) {
			var me = this;
			var data = me.chart.data;
			var isHorizontal = me.isHorizontal();

			if (data.yLabels && !isHorizontal) {
				return me.getRightValue(data.datasets[datasetIndex].data[index]);
			}
			return me.ticks[index - me.minIndex];
		},

		// Used to get data value locations.  Value can either be an index or a numerical value
		getPixelForValue: function(value, index) {
			var me = this;
			var offset = me.options.offset;
			// 1 is added because we need the length but we have the indexes
			var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1);

			// If value is a data object, then index is the index in the data array,
			// not the index of the scale. We need to change that.
			var valueCategory;
			if (value !== undefined && value !== null) {
				valueCategory = me.isHorizontal() ? value.x : value.y;
			}
			if (valueCategory !== undefined || (value !== undefined && isNaN(index))) {
				var labels = me.getLabels();
				value = valueCategory || value;
				var idx = labels.indexOf(value);
				index = idx !== -1 ? idx : index;
			}

			if (me.isHorizontal()) {
				var valueWidth = me.width / offsetAmt;
				var widthOffset = (valueWidth * (index - me.minIndex));

				if (offset) {
					widthOffset += (valueWidth / 2);
				}

				return me.left + Math.round(widthOffset);
			}
			var valueHeight = me.height / offsetAmt;
			var heightOffset = (valueHeight * (index - me.minIndex));

			if (offset) {
				heightOffset += (valueHeight / 2);
			}

			return me.top + Math.round(heightOffset);
		},
		getPixelForTick: function(index) {
			return this.getPixelForValue(this.ticks[index], index + this.minIndex, null);
		},
		getValueForPixel: function(pixel) {
			var me = this;
			var offset = me.options.offset;
			var value;
			var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
			var horz = me.isHorizontal();
			var valueDimension = (horz ? me.width : me.height) / offsetAmt;

			pixel -= horz ? me.left : me.top;

			if (offset) {
				pixel -= (valueDimension / 2);
			}

			if (pixel <= 0) {
				value = 0;
			} else {
				value = Math.round(pixel / valueDimension);
			}

			return value + me.minIndex;
		},
		getBasePixel: function() {
			return this.bottom;
		}
	});

	Chart.scaleService.registerScaleType('category', DatasetScale, defaultConfig);

};

},{}],53:[function(require,module,exports){
'use strict';

var defaults = require(25);
var helpers = require(45);
var Ticks = require(34);

module.exports = function(Chart) {

	var defaultConfig = {
		position: 'left',
		ticks: {
			callback: Ticks.formatters.linear
		}
	};

	var LinearScale = Chart.LinearScaleBase.extend({

		determineDataLimits: function() {
			var me = this;
			var opts = me.options;
			var chart = me.chart;
			var data = chart.data;
			var datasets = data.datasets;
			var isHorizontal = me.isHorizontal();
			var DEFAULT_MIN = 0;
			var DEFAULT_MAX = 1;

			function IDMatches(meta) {
				return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
			}

			// First Calculate the range
			me.min = null;
			me.max = null;

			var hasStacks = opts.stacked;
			if (hasStacks === undefined) {
				helpers.each(datasets, function(dataset, datasetIndex) {
					if (hasStacks) {
						return;
					}

					var meta = chart.getDatasetMeta(datasetIndex);
					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
						meta.stack !== undefined) {
						hasStacks = true;
					}
				});
			}

			if (opts.stacked || hasStacks) {
				var valuesPerStack = {};

				helpers.each(datasets, function(dataset, datasetIndex) {
					var meta = chart.getDatasetMeta(datasetIndex);
					var key = [
						meta.type,
						// we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
						((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
						meta.stack
					].join('.');

					if (valuesPerStack[key] === undefined) {
						valuesPerStack[key] = {
							positiveValues: [],
							negativeValues: []
						};
					}

					// Store these per type
					var positiveValues = valuesPerStack[key].positiveValues;
					var negativeValues = valuesPerStack[key].negativeValues;

					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
						helpers.each(dataset.data, function(rawValue, index) {
							var value = +me.getRightValue(rawValue);
							if (isNaN(value) || meta.data[index].hidden) {
								return;
							}

							positiveValues[index] = positiveValues[index] || 0;
							negativeValues[index] = negativeValues[index] || 0;

							if (opts.relativePoints) {
								positiveValues[index] = 100;
							} else if (value < 0) {
								negativeValues[index] += value;
							} else {
								positiveValues[index] += value;
							}
						});
					}
				});

				helpers.each(valuesPerStack, function(valuesForType) {
					var values = valuesForType.positiveValues.concat(valuesForType.negativeValues);
					var minVal = helpers.min(values);
					var maxVal = helpers.max(values);
					me.min = me.min === null ? minVal : Math.min(me.min, minVal);
					me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
				});

			} else {
				helpers.each(datasets, function(dataset, datasetIndex) {
					var meta = chart.getDatasetMeta(datasetIndex);
					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
						helpers.each(dataset.data, function(rawValue, index) {
							var value = +me.getRightValue(rawValue);
							if (isNaN(value) || meta.data[index].hidden) {
								return;
							}

							if (me.min === null) {
								me.min = value;
							} else if (value < me.min) {
								me.min = value;
							}

							if (me.max === null) {
								me.max = value;
							} else if (value > me.max) {
								me.max = value;
							}
						});
					}
				});
			}

			me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN;
			me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX;

			// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
			this.handleTickRangeOptions();
		},
		getTickLimit: function() {
			var maxTicks;
			var me = this;
			var tickOpts = me.options.ticks;

			if (me.isHorizontal()) {
				maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50));
			} else {
				// The factor of 2 used to scale the font size has been experimentally determined.
				var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize);
				maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize)));
			}

			return maxTicks;
		},
		// Called after the ticks are built. We need
		handleDirectionalChanges: function() {
			if (!this.isHorizontal()) {
				// We are in a vertical orientation. The top value is the highest. So reverse the array
				this.ticks.reverse();
			}
		},
		getLabelForIndex: function(index, datasetIndex) {
			return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
		},
		// Utils
		getPixelForValue: function(value) {
			// This must be called after fit has been run so that
			// this.left, this.top, this.right, and this.bottom have been defined
			var me = this;
			var start = me.start;

			var rightValue = +me.getRightValue(value);
			var pixel;
			var range = me.end - start;

			if (me.isHorizontal()) {
				pixel = me.left + (me.width / range * (rightValue - start));
				return Math.round(pixel);
			}

			pixel = me.bottom - (me.height / range * (rightValue - start));
			return Math.round(pixel);
		},
		getValueForPixel: function(pixel) {
			var me = this;
			var isHorizontal = me.isHorizontal();
			var innerDimension = isHorizontal ? me.width : me.height;
			var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension;
			return me.start + ((me.end - me.start) * offset);
		},
		getPixelForTick: function(index) {
			return this.getPixelForValue(this.ticksAsNumbers[index]);
		}
	});
	Chart.scaleService.registerScaleType('linear', LinearScale, defaultConfig);

};

},{"25":25,"34":34,"45":45}],54:[function(require,module,exports){
'use strict';

var helpers = require(45);
var Ticks = require(34);

module.exports = function(Chart) {

	var noop = helpers.noop;

	Chart.LinearScaleBase = Chart.Scale.extend({
		getRightValue: function(value) {
			if (typeof value === 'string') {
				return +value;
			}
			return Chart.Scale.prototype.getRightValue.call(this, value);
		},

		handleTickRangeOptions: function() {
			var me = this;
			var opts = me.options;
			var tickOpts = opts.ticks;

			// If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
			// do nothing since that would make the chart weird. If the user really wants a weird chart
			// axis, they can manually override it
			if (tickOpts.beginAtZero) {
				var minSign = helpers.sign(me.min);
				var maxSign = helpers.sign(me.max);

				if (minSign < 0 && maxSign < 0) {
					// move the top up to 0
					me.max = 0;
				} else if (minSign > 0 && maxSign > 0) {
					// move the bottom down to 0
					me.min = 0;
				}
			}

			var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined;
			var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined;

			if (tickOpts.min !== undefined) {
				me.min = tickOpts.min;
			} else if (tickOpts.suggestedMin !== undefined) {
				if (me.min === null) {
					me.min = tickOpts.suggestedMin;
				} else {
					me.min = Math.min(me.min, tickOpts.suggestedMin);
				}
			}

			if (tickOpts.max !== undefined) {
				me.max = tickOpts.max;
			} else if (tickOpts.suggestedMax !== undefined) {
				if (me.max === null) {
					me.max = tickOpts.suggestedMax;
				} else {
					me.max = Math.max(me.max, tickOpts.suggestedMax);
				}
			}

			if (setMin !== setMax) {
				// We set the min or the max but not both.
				// So ensure that our range is good
				// Inverted or 0 length range can happen when
				// ticks.min is set, and no datasets are visible
				if (me.min >= me.max) {
					if (setMin) {
						me.max = me.min + 1;
					} else {
						me.min = me.max - 1;
					}
				}
			}

			if (me.min === me.max) {
				me.max++;

				if (!tickOpts.beginAtZero) {
					me.min--;
				}
			}
		},
		getTickLimit: noop,
		handleDirectionalChanges: noop,

		buildTicks: function() {
			var me = this;
			var opts = me.options;
			var tickOpts = opts.ticks;

			// Figure out what the max number of ticks we can support it is based on the size of
			// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
			// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
			// the graph. Make sure we always have at least 2 ticks
			var maxTicks = me.getTickLimit();
			maxTicks = Math.max(2, maxTicks);

			var numericGeneratorOptions = {
				maxTicks: maxTicks,
				min: tickOpts.min,
				max: tickOpts.max,
				stepSize: helpers.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize)
			};
			var ticks = me.ticks = Ticks.generators.linear(numericGeneratorOptions, me);

			me.handleDirectionalChanges();

			// At this point, we need to update our max and min given the tick values since we have expanded the
			// range of the scale
			me.max = helpers.max(ticks);
			me.min = helpers.min(ticks);

			if (tickOpts.reverse) {
				ticks.reverse();

				me.start = me.max;
				me.end = me.min;
			} else {
				me.start = me.min;
				me.end = me.max;
			}
		},
		convertTicksToLabels: function() {
			var me = this;
			me.ticksAsNumbers = me.ticks.slice();
			me.zeroLineIndex = me.ticks.indexOf(0);

			Chart.Scale.prototype.convertTicksToLabels.call(me);
		}
	});
};

},{"34":34,"45":45}],55:[function(require,module,exports){
'use strict';

var helpers = require(45);
var Ticks = require(34);

module.exports = function(Chart) {

	var defaultConfig = {
		position: 'left',

		// label settings
		ticks: {
			callback: Ticks.formatters.logarithmic
		}
	};

	var LogarithmicScale = Chart.Scale.extend({
		determineDataLimits: function() {
			var me = this;
			var opts = me.options;
			var tickOpts = opts.ticks;
			var chart = me.chart;
			var data = chart.data;
			var datasets = data.datasets;
			var valueOrDefault = helpers.valueOrDefault;
			var isHorizontal = me.isHorizontal();
			function IDMatches(meta) {
				return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
			}

			// Calculate Range
			me.min = null;
			me.max = null;
			me.minNotZero = null;

			var hasStacks = opts.stacked;
			if (hasStacks === undefined) {
				helpers.each(datasets, function(dataset, datasetIndex) {
					if (hasStacks) {
						return;
					}

					var meta = chart.getDatasetMeta(datasetIndex);
					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
						meta.stack !== undefined) {
						hasStacks = true;
					}
				});
			}

			if (opts.stacked || hasStacks) {
				var valuesPerStack = {};

				helpers.each(datasets, function(dataset, datasetIndex) {
					var meta = chart.getDatasetMeta(datasetIndex);
					var key = [
						meta.type,
						// we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
						((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
						meta.stack
					].join('.');

					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
						if (valuesPerStack[key] === undefined) {
							valuesPerStack[key] = [];
						}

						helpers.each(dataset.data, function(rawValue, index) {
							var values = valuesPerStack[key];
							var value = +me.getRightValue(rawValue);
							if (isNaN(value) || meta.data[index].hidden) {
								return;
							}

							values[index] = values[index] || 0;

							if (opts.relativePoints) {
								values[index] = 100;
							} else {
								// Don't need to split positive and negative since the log scale can't handle a 0 crossing
								values[index] += value;
							}
						});
					}
				});

				helpers.each(valuesPerStack, function(valuesForType) {
					var minVal = helpers.min(valuesForType);
					var maxVal = helpers.max(valuesForType);
					me.min = me.min === null ? minVal : Math.min(me.min, minVal);
					me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
				});

			} else {
				helpers.each(datasets, function(dataset, datasetIndex) {
					var meta = chart.getDatasetMeta(datasetIndex);
					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
						helpers.each(dataset.data, function(rawValue, index) {
							var value = +me.getRightValue(rawValue);
							if (isNaN(value) || meta.data[index].hidden) {
								return;
							}

							if (me.min === null) {
								me.min = value;
							} else if (value < me.min) {
								me.min = value;
							}

							if (me.max === null) {
								me.max = value;
							} else if (value > me.max) {
								me.max = value;
							}

							if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {
								me.minNotZero = value;
							}
						});
					}
				});
			}

			me.min = valueOrDefault(tickOpts.min, me.min);
			me.max = valueOrDefault(tickOpts.max, me.max);

			if (me.min === me.max) {
				if (me.min !== 0 && me.min !== null) {
					me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1);
					me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1);
				} else {
					me.min = 1;
					me.max = 10;
				}
			}
		},
		buildTicks: function() {
			var me = this;
			var opts = me.options;
			var tickOpts = opts.ticks;

			var generationOptions = {
				min: tickOpts.min,
				max: tickOpts.max
			};
			var ticks = me.ticks = Ticks.generators.logarithmic(generationOptions, me);

			if (!me.isHorizontal()) {
				// We are in a vertical orientation. The top value is the highest. So reverse the array
				ticks.reverse();
			}

			// At this point, we need to update our max and min given the tick values since we have expanded the
			// range of the scale
			me.max = helpers.max(ticks);
			me.min = helpers.min(ticks);

			if (tickOpts.reverse) {
				ticks.reverse();

				me.start = me.max;
				me.end = me.min;
			} else {
				me.start = me.min;
				me.end = me.max;
			}
		},
		convertTicksToLabels: function() {
			this.tickValues = this.ticks.slice();

			Chart.Scale.prototype.convertTicksToLabels.call(this);
		},
		// Get the correct tooltip label
		getLabelForIndex: function(index, datasetIndex) {
			return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
		},
		getPixelForTick: function(index) {
			return this.getPixelForValue(this.tickValues[index]);
		},
		getPixelForValue: function(value) {
			var me = this;
			var start = me.start;
			var newVal = +me.getRightValue(value);
			var opts = me.options;
			var tickOpts = opts.ticks;
			var innerDimension, pixel, range;

			if (me.isHorizontal()) {
				range = helpers.log10(me.end) - helpers.log10(start); // todo: if start === 0
				if (newVal === 0) {
					pixel = me.left;
				} else {
					innerDimension = me.width;
					pixel = me.left + (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
				}
			} else {
				// Bottom - top since pixels increase downward on a screen
				innerDimension = me.height;
				if (start === 0 && !tickOpts.reverse) {
					range = helpers.log10(me.end) - helpers.log10(me.minNotZero);
					if (newVal === start) {
						pixel = me.bottom;
					} else if (newVal === me.minNotZero) {
						pixel = me.bottom - innerDimension * 0.02;
					} else {
						pixel = me.bottom - innerDimension * 0.02 - (innerDimension * 0.98 / range * (helpers.log10(newVal) - helpers.log10(me.minNotZero)));
					}
				} else if (me.end === 0 && tickOpts.reverse) {
					range = helpers.log10(me.start) - helpers.log10(me.minNotZero);
					if (newVal === me.end) {
						pixel = me.top;
					} else if (newVal === me.minNotZero) {
						pixel = me.top + innerDimension * 0.02;
					} else {
						pixel = me.top + innerDimension * 0.02 + (innerDimension * 0.98 / range * (helpers.log10(newVal) - helpers.log10(me.minNotZero)));
					}
				} else if (newVal === 0) {
					pixel = tickOpts.reverse ? me.top : me.bottom;
				} else {
					range = helpers.log10(me.end) - helpers.log10(start);
					innerDimension = me.height;
					pixel = me.bottom - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
				}
			}
			return pixel;
		},
		getValueForPixel: function(pixel) {
			var me = this;
			var range = helpers.log10(me.end) - helpers.log10(me.start);
			var value, innerDimension;

			if (me.isHorizontal()) {
				innerDimension = me.width;
				value = me.start * Math.pow(10, (pixel - me.left) * range / innerDimension);
			} else { // todo: if start === 0
				innerDimension = me.height;
				value = Math.pow(10, (me.bottom - pixel) * range / innerDimension) / me.start;
			}
			return value;
		}
	});
	Chart.scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig);

};

},{"34":34,"45":45}],56:[function(require,module,exports){
'use strict';

var defaults = require(25);
var helpers = require(45);
var Ticks = require(34);

module.exports = function(Chart) {

	var globalDefaults = defaults.global;

	var defaultConfig = {
		display: true,

		// Boolean - Whether to animate scaling the chart from the centre
		animate: true,
		position: 'chartArea',

		angleLines: {
			display: true,
			color: 'rgba(0, 0, 0, 0.1)',
			lineWidth: 1
		},

		gridLines: {
			circular: false
		},

		// label settings
		ticks: {
			// Boolean - Show a backdrop to the scale label
			showLabelBackdrop: true,

			// String - The colour of the label backdrop
			backdropColor: 'rgba(255,255,255,0.75)',

			// Number - The backdrop padding above & below the label in pixels
			backdropPaddingY: 2,

			// Number - The backdrop padding to the side of the label in pixels
			backdropPaddingX: 2,

			callback: Ticks.formatters.linear
		},

		pointLabels: {
			// Boolean - if true, show point labels
			display: true,

			// Number - Point label font size in pixels
			fontSize: 10,

			// Function - Used to convert point labels
			callback: function(label) {
				return label;
			}
		}
	};

	function getValueCount(scale) {
		var opts = scale.options;
		return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0;
	}

	function getPointLabelFontOptions(scale) {
		var pointLabelOptions = scale.options.pointLabels;
		var fontSize = helpers.valueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize);
		var fontStyle = helpers.valueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle);
		var fontFamily = helpers.valueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily);
		var font = helpers.fontString(fontSize, fontStyle, fontFamily);

		return {
			size: fontSize,
			style: fontStyle,
			family: fontFamily,
			font: font
		};
	}

	function measureLabelSize(ctx, fontSize, label) {
		if (helpers.isArray(label)) {
			return {
				w: helpers.longestText(ctx, ctx.font, label),
				h: (label.length * fontSize) + ((label.length - 1) * 1.5 * fontSize)
			};
		}

		return {
			w: ctx.measureText(label).width,
			h: fontSize
		};
	}

	function determineLimits(angle, pos, size, min, max) {
		if (angle === min || angle === max) {
			return {
				start: pos - (size / 2),
				end: pos + (size / 2)
			};
		} else if (angle < min || angle > max) {
			return {
				start: pos - size - 5,
				end: pos
			};
		}

		return {
			start: pos,
			end: pos + size + 5
		};
	}

	/**
	 * Helper function to fit a radial linear scale with point labels
	 */
	function fitWithPointLabels(scale) {
		/*
		 * Right, this is really confusing and there is a lot of maths going on here
		 * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
		 *
		 * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
		 *
		 * Solution:
		 *
		 * We assume the radius of the polygon is half the size of the canvas at first
		 * at each index we check if the text overlaps.
		 *
		 * Where it does, we store that angle and that index.
		 *
		 * After finding the largest index and angle we calculate how much we need to remove
		 * from the shape radius to move the point inwards by that x.
		 *
		 * We average the left and right distances to get the maximum shape radius that can fit in the box
		 * along with labels.
		 *
		 * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
		 * on each side, removing that from the size, halving it and adding the left x protrusion width.
		 *
		 * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
		 * and position it in the most space efficient manner
		 *
		 * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
		 */

		var plFont = getPointLabelFontOptions(scale);

		// Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
		// Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
		var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);
		var furthestLimits = {
			r: scale.width,
			l: 0,
			t: scale.height,
			b: 0
		};
		var furthestAngles = {};
		var i, textSize, pointPosition;

		scale.ctx.font = plFont.font;
		scale._pointLabelSizes = [];

		var valueCount = getValueCount(scale);
		for (i = 0; i < valueCount; i++) {
			pointPosition = scale.getPointPosition(i, largestPossibleRadius);
			textSize = measureLabelSize(scale.ctx, plFont.size, scale.pointLabels[i] || '');
			scale._pointLabelSizes[i] = textSize;

			// Add quarter circle to make degree 0 mean top of circle
			var angleRadians = scale.getIndexAngle(i);
			var angle = helpers.toDegrees(angleRadians) % 360;
			var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
			var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);

			if (hLimits.start < furthestLimits.l) {
				furthestLimits.l = hLimits.start;
				furthestAngles.l = angleRadians;
			}

			if (hLimits.end > furthestLimits.r) {
				furthestLimits.r = hLimits.end;
				furthestAngles.r = angleRadians;
			}

			if (vLimits.start < furthestLimits.t) {
				furthestLimits.t = vLimits.start;
				furthestAngles.t = angleRadians;
			}

			if (vLimits.end > furthestLimits.b) {
				furthestLimits.b = vLimits.end;
				furthestAngles.b = angleRadians;
			}
		}

		scale.setReductions(largestPossibleRadius, furthestLimits, furthestAngles);
	}

	/**
	 * Helper function to fit a radial linear scale with no point labels
	 */
	function fit(scale) {
		var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);
		scale.drawingArea = Math.round(largestPossibleRadius);
		scale.setCenterPoint(0, 0, 0, 0);
	}

	function getTextAlignForAngle(angle) {
		if (angle === 0 || angle === 180) {
			return 'center';
		} else if (angle < 180) {
			return 'left';
		}

		return 'right';
	}

	function fillText(ctx, text, position, fontSize) {
		if (helpers.isArray(text)) {
			var y = position.y;
			var spacing = 1.5 * fontSize;

			for (var i = 0; i < text.length; ++i) {
				ctx.fillText(text[i], position.x, y);
				y += spacing;
			}
		} else {
			ctx.fillText(text, position.x, position.y);
		}
	}

	function adjustPointPositionForLabelHeight(angle, textSize, position) {
		if (angle === 90 || angle === 270) {
			position.y -= (textSize.h / 2);
		} else if (angle > 270 || angle < 90) {
			position.y -= textSize.h;
		}
	}

	function drawPointLabels(scale) {
		var ctx = scale.ctx;
		var valueOrDefault = helpers.valueOrDefault;
		var opts = scale.options;
		var angleLineOpts = opts.angleLines;
		var pointLabelOpts = opts.pointLabels;

		ctx.lineWidth = angleLineOpts.lineWidth;
		ctx.strokeStyle = angleLineOpts.color;

		var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);

		// Point Label Font
		var plFont = getPointLabelFontOptions(scale);

		ctx.textBaseline = 'top';

		for (var i = getValueCount(scale) - 1; i >= 0; i--) {
			if (angleLineOpts.display) {
				var outerPosition = scale.getPointPosition(i, outerDistance);
				ctx.beginPath();
				ctx.moveTo(scale.xCenter, scale.yCenter);
				ctx.lineTo(outerPosition.x, outerPosition.y);
				ctx.stroke();
				ctx.closePath();
			}

			if (pointLabelOpts.display) {
				// Extra 3px out for some label spacing
				var pointLabelPosition = scale.getPointPosition(i, outerDistance + 5);

				// Keep this in loop since we may support array properties here
				var pointLabelFontColor = valueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor);
				ctx.font = plFont.font;
				ctx.fillStyle = pointLabelFontColor;

				var angleRadians = scale.getIndexAngle(i);
				var angle = helpers.toDegrees(angleRadians);
				ctx.textAlign = getTextAlignForAngle(angle);
				adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);
				fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.size);
			}
		}
	}

	function drawRadiusLine(scale, gridLineOpts, radius, index) {
		var ctx = scale.ctx;
		ctx.strokeStyle = helpers.valueAtIndexOrDefault(gridLineOpts.color, index - 1);
		ctx.lineWidth = helpers.valueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1);

		if (scale.options.gridLines.circular) {
			// Draw circular arcs between the points
			ctx.beginPath();
			ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2);
			ctx.closePath();
			ctx.stroke();
		} else {
			// Draw straight lines connecting each index
			var valueCount = getValueCount(scale);

			if (valueCount === 0) {
				return;
			}

			ctx.beginPath();
			var pointPosition = scale.getPointPosition(0, radius);
			ctx.moveTo(pointPosition.x, pointPosition.y);

			for (var i = 1; i < valueCount; i++) {
				pointPosition = scale.getPointPosition(i, radius);
				ctx.lineTo(pointPosition.x, pointPosition.y);
			}

			ctx.closePath();
			ctx.stroke();
		}
	}

	function numberOrZero(param) {
		return helpers.isNumber(param) ? param : 0;
	}

	var LinearRadialScale = Chart.LinearScaleBase.extend({
		setDimensions: function() {
			var me = this;
			var opts = me.options;
			var tickOpts = opts.ticks;
			// Set the unconstrained dimension before label rotation
			me.width = me.maxWidth;
			me.height = me.maxHeight;
			me.xCenter = Math.round(me.width / 2);
			me.yCenter = Math.round(me.height / 2);

			var minSize = helpers.min([me.height, me.width]);
			var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
			me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2);
		},
		determineDataLimits: function() {
			var me = this;
			var chart = me.chart;
			var min = Number.POSITIVE_INFINITY;
			var max = Number.NEGATIVE_INFINITY;

			helpers.each(chart.data.datasets, function(dataset, datasetIndex) {
				if (chart.isDatasetVisible(datasetIndex)) {
					var meta = chart.getDatasetMeta(datasetIndex);

					helpers.each(dataset.data, function(rawValue, index) {
						var value = +me.getRightValue(rawValue);
						if (isNaN(value) || meta.data[index].hidden) {
							return;
						}

						min = Math.min(value, min);
						max = Math.max(value, max);
					});
				}
			});

			me.min = (min === Number.POSITIVE_INFINITY ? 0 : min);
			me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max);

			// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
			me.handleTickRangeOptions();
		},
		getTickLimit: function() {
			var tickOpts = this.options.ticks;
			var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
			return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize)));
		},
		convertTicksToLabels: function() {
			var me = this;

			Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me);

			// Point labels
			me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me);
		},
		getLabelForIndex: function(index, datasetIndex) {
			return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
		},
		fit: function() {
			if (this.options.pointLabels.display) {
				fitWithPointLabels(this);
			} else {
				fit(this);
			}
		},
		/**
		 * Set radius reductions and determine new radius and center point
		 * @private
		 */
		setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) {
			var me = this;
			var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);
			var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r);
			var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);
			var radiusReductionBottom = -Math.max(furthestLimits.b - me.height, 0) / Math.cos(furthestAngles.b);

			radiusReductionLeft = numberOrZero(radiusReductionLeft);
			radiusReductionRight = numberOrZero(radiusReductionRight);
			radiusReductionTop = numberOrZero(radiusReductionTop);
			radiusReductionBottom = numberOrZero(radiusReductionBottom);

			me.drawingArea = Math.min(
				Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),
				Math.round(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2));
			me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
		},
		setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) {
			var me = this;
			var maxRight = me.width - rightMovement - me.drawingArea;
			var maxLeft = leftMovement + me.drawingArea;
			var maxTop = topMovement + me.drawingArea;
			var maxBottom = me.height - bottomMovement - me.drawingArea;

			me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left);
			me.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top);
		},

		getIndexAngle: function(index) {
			var angleMultiplier = (Math.PI * 2) / getValueCount(this);
			var startAngle = this.chart.options && this.chart.options.startAngle ?
				this.chart.options.startAngle :
				0;

			var startAngleRadians = startAngle * Math.PI * 2 / 360;

			// Start from the top instead of right, so remove a quarter of the circle
			return index * angleMultiplier + startAngleRadians;
		},
		getDistanceFromCenterForValue: function(value) {
			var me = this;

			if (value === null) {
				return 0; // null always in center
			}

			// Take into account half font size + the yPadding of the top value
			var scalingFactor = me.drawingArea / (me.max - me.min);
			if (me.options.ticks.reverse) {
				return (me.max - value) * scalingFactor;
			}
			return (value - me.min) * scalingFactor;
		},
		getPointPosition: function(index, distanceFromCenter) {
			var me = this;
			var thisAngle = me.getIndexAngle(index) - (Math.PI / 2);
			return {
				x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter,
				y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter
			};
		},
		getPointPositionForValue: function(index, value) {
			return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
		},

		getBasePosition: function() {
			var me = this;
			var min = me.min;
			var max = me.max;

			return me.getPointPositionForValue(0,
				me.beginAtZero ? 0 :
				min < 0 && max < 0 ? max :
				min > 0 && max > 0 ? min :
				0);
		},

		draw: function() {
			var me = this;
			var opts = me.options;
			var gridLineOpts = opts.gridLines;
			var tickOpts = opts.ticks;
			var valueOrDefault = helpers.valueOrDefault;

			if (opts.display) {
				var ctx = me.ctx;
				var startAngle = this.getIndexAngle(0);

				// Tick Font
				var tickFontSize = valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
				var tickFontStyle = valueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle);
				var tickFontFamily = valueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily);
				var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);

				helpers.each(me.ticks, function(label, index) {
					// Don't draw a centre value (if it is minimum)
					if (index > 0 || tickOpts.reverse) {
						var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);

						// Draw circular lines around the scale
						if (gridLineOpts.display && index !== 0) {
							drawRadiusLine(me, gridLineOpts, yCenterOffset, index);
						}

						if (tickOpts.display) {
							var tickFontColor = valueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor);
							ctx.font = tickLabelFont;

							ctx.save();
							ctx.translate(me.xCenter, me.yCenter);
							ctx.rotate(startAngle);

							if (tickOpts.showLabelBackdrop) {
								var labelWidth = ctx.measureText(label).width;
								ctx.fillStyle = tickOpts.backdropColor;
								ctx.fillRect(
									-labelWidth / 2 - tickOpts.backdropPaddingX,
									-yCenterOffset - tickFontSize / 2 - tickOpts.backdropPaddingY,
									labelWidth + tickOpts.backdropPaddingX * 2,
									tickFontSize + tickOpts.backdropPaddingY * 2
								);
							}

							ctx.textAlign = 'center';
							ctx.textBaseline = 'middle';
							ctx.fillStyle = tickFontColor;
							ctx.fillText(label, 0, -yCenterOffset);
							ctx.restore();
						}
					}
				});

				if (opts.angleLines.display || opts.pointLabels.display) {
					drawPointLabels(me);
				}
			}
		}
	});
	Chart.scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig);

};

},{"25":25,"34":34,"45":45}],57:[function(require,module,exports){
/* global window: false */
'use strict';

var moment = require(1);
moment = typeof moment === 'function' ? moment : window.moment;

var defaults = require(25);
var helpers = require(45);

// Integer constants are from the ES6 spec.
var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991;
var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;

var INTERVALS = {
	millisecond: {
		major: true,
		size: 1,
		steps: [1, 2, 5, 10, 20, 50, 100, 250, 500]
	},
	second: {
		major: true,
		size: 1000,
		steps: [1, 2, 5, 10, 30]
	},
	minute: {
		major: true,
		size: 60000,
		steps: [1, 2, 5, 10, 30]
	},
	hour: {
		major: true,
		size: 3600000,
		steps: [1, 2, 3, 6, 12]
	},
	day: {
		major: true,
		size: 86400000,
		steps: [1, 2, 5]
	},
	week: {
		major: false,
		size: 604800000,
		steps: [1, 2, 3, 4]
	},
	month: {
		major: true,
		size: 2.628e9,
		steps: [1, 2, 3]
	},
	quarter: {
		major: false,
		size: 7.884e9,
		steps: [1, 2, 3, 4]
	},
	year: {
		major: true,
		size: 3.154e10
	}
};

var UNITS = Object.keys(INTERVALS);

function sorter(a, b) {
	return a - b;
}

function arrayUnique(items) {
	var hash = {};
	var out = [];
	var i, ilen, item;

	for (i = 0, ilen = items.length; i < ilen; ++i) {
		item = items[i];
		if (!hash[item]) {
			hash[item] = true;
			out.push(item);
		}
	}

	return out;
}

/**
 * Returns an array of {time, pos} objects used to interpolate a specific `time` or position
 * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is
 * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other
 * extremity (left + width or top + height). Note that it would be more optimized to directly
 * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need
 * to create the lookup table. The table ALWAYS contains at least two items: min and max.
 *
 * @param {Number[]} timestamps - timestamps sorted from lowest to highest.
 * @param {String} distribution - If 'linear', timestamps will be spread linearly along the min
 * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}.
 * If 'series', timestamps will be positioned at the same distance from each other. In this
 * case, only timestamps that break the time linearity are registered, meaning that in the
 * best case, all timestamps are linear, the table contains only min and max.
 */
function buildLookupTable(timestamps, min, max, distribution) {
	if (distribution === 'linear' || !timestamps.length) {
		return [
			{time: min, pos: 0},
			{time: max, pos: 1}
		];
	}

	var table = [];
	var items = [min];
	var i, ilen, prev, curr, next;

	for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
		curr = timestamps[i];
		if (curr > min && curr < max) {
			items.push(curr);
		}
	}

	items.push(max);

	for (i = 0, ilen = items.length; i < ilen; ++i) {
		next = items[i + 1];
		prev = items[i - 1];
		curr = items[i];

		// only add points that breaks the scale linearity
		if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) {
			table.push({time: curr, pos: i / (ilen - 1)});
		}
	}

	return table;
}

// @see adapted from http://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/
function lookup(table, key, value) {
	var lo = 0;
	var hi = table.length - 1;
	var mid, i0, i1;

	while (lo >= 0 && lo <= hi) {
		mid = (lo + hi) >> 1;
		i0 = table[mid - 1] || null;
		i1 = table[mid];

		if (!i0) {
			// given value is outside table (before first item)
			return {lo: null, hi: i1};
		} else if (i1[key] < value) {
			lo = mid + 1;
		} else if (i0[key] > value) {
			hi = mid - 1;
		} else {
			return {lo: i0, hi: i1};
		}
	}

	// given value is outside table (after last item)
	return {lo: i1, hi: null};
}

/**
 * Linearly interpolates the given source `value` using the table items `skey` values and
 * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos')
 * returns the position for a timestamp equal to 42. If value is out of bounds, values at
 * index [0, 1] or [n - 1, n] are used for the interpolation.
 */
function interpolate(table, skey, sval, tkey) {
	var range = lookup(table, skey, sval);

	// Note: the lookup table ALWAYS contains at least 2 items (min and max)
	var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo;
	var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi;

	var span = next[skey] - prev[skey];
	var ratio = span ? (sval - prev[skey]) / span : 0;
	var offset = (next[tkey] - prev[tkey]) * ratio;

	return prev[tkey] + offset;
}

/**
 * Convert the given value to a moment object using the given time options.
 * @see http://momentjs.com/docs/#/parsing/
 */
function momentify(value, options) {
	var parser = options.parser;
	var format = options.parser || options.format;

	if (typeof parser === 'function') {
		return parser(value);
	}

	if (typeof value === 'string' && typeof format === 'string') {
		return moment(value, format);
	}

	if (!(value instanceof moment)) {
		value = moment(value);
	}

	if (value.isValid()) {
		return value;
	}

	// Labels are in an incompatible moment format and no `parser` has been provided.
	// The user might still use the deprecated `format` option to convert his inputs.
	if (typeof format === 'function') {
		return format(value);
	}

	return value;
}

function parse(input, scale) {
	if (helpers.isNullOrUndef(input)) {
		return null;
	}

	var options = scale.options.time;
	var value = momentify(scale.getRightValue(input), options);
	if (!value.isValid()) {
		return null;
	}

	if (options.round) {
		value.startOf(options.round);
	}

	return value.valueOf();
}

/**
 * Returns the number of unit to skip to be able to display up to `capacity` number of ticks
 * in `unit` for the given `min` / `max` range and respecting the interval steps constraints.
 */
function determineStepSize(min, max, unit, capacity) {
	var range = max - min;
	var interval = INTERVALS[unit];
	var milliseconds = interval.size;
	var steps = interval.steps;
	var i, ilen, factor;

	if (!steps) {
		return Math.ceil(range / ((capacity || 1) * milliseconds));
	}

	for (i = 0, ilen = steps.length; i < ilen; ++i) {
		factor = steps[i];
		if (Math.ceil(range / (milliseconds * factor)) <= capacity) {
			break;
		}
	}

	return factor;
}

function determineUnit(minUnit, min, max, capacity) {
	var ilen = UNITS.length;
	var i, interval, factor;

	for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) {
		interval = INTERVALS[UNITS[i]];
		factor = interval.steps ? interval.steps[interval.steps.length - 1] : MAX_INTEGER;

		if (Math.ceil((max - min) / (factor * interval.size)) <= capacity) {
			return UNITS[i];
		}
	}

	return UNITS[ilen - 1];
}

function determineMajorUnit(unit) {
	for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) {
		if (INTERVALS[UNITS[i]].major) {
			return UNITS[i];
		}
	}
}

/**
 * Generates a maximum of `capacity` timestamps between min and max, rounded to the
 * `minor` unit, aligned on the `major` unit and using the given scale time `options`.
 * Important: this method can return ticks outside the min and max range, it's the
 * responsibility of the calling code to clamp values if needed.
 */
function generate(min, max, minor, major, capacity, options) {
	var timeOpts = options.time;
	var stepSize = helpers.valueOrDefault(timeOpts.stepSize, timeOpts.unitStepSize);
	var weekday = minor === 'week' ? timeOpts.isoWeekday : false;
	var majorTicksEnabled = options.ticks.major.enabled;
	var interval = INTERVALS[minor];
	var first = moment(min);
	var last = moment(max);
	var ticks = [];
	var time;

	if (!stepSize) {
		stepSize = determineStepSize(min, max, minor, capacity);
	}

	// For 'week' unit, handle the first day of week option
	if (weekday) {
		first = first.isoWeekday(weekday);
		last = last.isoWeekday(weekday);
	}

	// Align first/last ticks on unit
	first = first.startOf(weekday ? 'day' : minor);
	last = last.startOf(weekday ? 'day' : minor);

	// Make sure that the last tick include max
	if (last < max) {
		last.add(1, minor);
	}

	time = moment(first);

	if (majorTicksEnabled && major && !weekday && !timeOpts.round) {
		// Align the first tick on the previous `minor` unit aligned on the `major` unit:
		// we first aligned time on the previous `major` unit then add the number of full
		// stepSize there is between first and the previous major time.
		time.startOf(major);
		time.add(~~((first - time) / (interval.size * stepSize)) * stepSize, minor);
	}

	for (; time < last; time.add(stepSize, minor)) {
		ticks.push(+time);
	}

	ticks.push(+time);

	return ticks;
}

/**
 * Returns the right and left offsets from edges in the form of {left, right}.
 * Offsets are added when the `offset` option is true.
 */
function computeOffsets(table, ticks, min, max, options) {
	var left = 0;
	var right = 0;
	var upper, lower;

	if (options.offset && ticks.length) {
		if (!options.time.min) {
			upper = ticks.length > 1 ? ticks[1] : max;
			lower = ticks[0];
			left = (
				interpolate(table, 'time', upper, 'pos') -
				interpolate(table, 'time', lower, 'pos')
			) / 2;
		}
		if (!options.time.max) {
			upper = ticks[ticks.length - 1];
			lower = ticks.length > 1 ? ticks[ticks.length - 2] : min;
			right = (
				interpolate(table, 'time', upper, 'pos') -
				interpolate(table, 'time', lower, 'pos')
			) / 2;
		}
	}

	return {left: left, right: right};
}

function ticksFromTimestamps(values, majorUnit) {
	var ticks = [];
	var i, ilen, value, major;

	for (i = 0, ilen = values.length; i < ilen; ++i) {
		value = values[i];
		major = majorUnit ? value === +moment(value).startOf(majorUnit) : false;

		ticks.push({
			value: value,
			major: major
		});
	}

	return ticks;
}

module.exports = function(Chart) {

	var defaultConfig = {
		position: 'bottom',

		/**
		 * Data distribution along the scale:
		 * - 'linear': data are spread according to their time (distances can vary),
		 * - 'series': data are spread at the same distance from each other.
		 * @see https://github.com/chartjs/Chart.js/pull/4507
		 * @since 2.7.0
		 */
		distribution: 'linear',

		/**
		 * Scale boundary strategy (bypassed by min/max time options)
		 * - `data`: make sure data are fully visible, ticks outside are removed
		 * - `ticks`: make sure ticks are fully visible, data outside are truncated
		 * @see https://github.com/chartjs/Chart.js/pull/4556
		 * @since 2.7.0
		 */
		bounds: 'data',

		time: {
			parser: false, // false == a pattern string from http://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment
			format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/
			unit: false, // false == automatic or override with week, month, year, etc.
			round: false, // none, or override with week, month, year, etc.
			displayFormat: false, // DEPRECATED
			isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/
			minUnit: 'millisecond',

			// defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/
			displayFormats: {
				millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM,
				second: 'h:mm:ss a', // 11:20:01 AM
				minute: 'h:mm a', // 11:20 AM
				hour: 'hA', // 5PM
				day: 'MMM D', // Sep 4
				week: 'll', // Week 46, or maybe "[W]WW - YYYY" ?
				month: 'MMM YYYY', // Sept 2015
				quarter: '[Q]Q - YYYY', // Q3
				year: 'YYYY' // 2015
			},
		},
		ticks: {
			autoSkip: false,

			/**
			 * Ticks generation input values:
			 * - 'auto': generates "optimal" ticks based on scale size and time options.
			 * - 'data': generates ticks from data (including labels from data {t|x|y} objects).
			 * - 'labels': generates ticks from user given `data.labels` values ONLY.
			 * @see https://github.com/chartjs/Chart.js/pull/4507
			 * @since 2.7.0
			 */
			source: 'auto',

			major: {
				enabled: false
			}
		}
	};

	var TimeScale = Chart.Scale.extend({
		initialize: function() {
			if (!moment) {
				throw new Error('Chart.js - Moment.js could not be found! You must include it before Chart.js to use the time scale. Download at https://momentjs.com');
			}

			this.mergeTicksOptions();

			Chart.Scale.prototype.initialize.call(this);
		},

		update: function() {
			var me = this;
			var options = me.options;

			// DEPRECATIONS: output a message only one time per update
			if (options.time && options.time.format) {
				console.warn('options.time.format is deprecated and replaced by options.time.parser.');
			}

			return Chart.Scale.prototype.update.apply(me, arguments);
		},

		/**
		 * Allows data to be referenced via 't' attribute
		 */
		getRightValue: function(rawValue) {
			if (rawValue && rawValue.t !== undefined) {
				rawValue = rawValue.t;
			}
			return Chart.Scale.prototype.getRightValue.call(this, rawValue);
		},

		determineDataLimits: function() {
			var me = this;
			var chart = me.chart;
			var timeOpts = me.options.time;
			var min = parse(timeOpts.min, me) || MAX_INTEGER;
			var max = parse(timeOpts.max, me) || MIN_INTEGER;
			var timestamps = [];
			var datasets = [];
			var labels = [];
			var i, j, ilen, jlen, data, timestamp;

			// Convert labels to timestamps
			for (i = 0, ilen = chart.data.labels.length; i < ilen; ++i) {
				labels.push(parse(chart.data.labels[i], me));
			}

			// Convert data to timestamps
			for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
				if (chart.isDatasetVisible(i)) {
					data = chart.data.datasets[i].data;

					// Let's consider that all data have the same format.
					if (helpers.isObject(data[0])) {
						datasets[i] = [];

						for (j = 0, jlen = data.length; j < jlen; ++j) {
							timestamp = parse(data[j], me);
							timestamps.push(timestamp);
							datasets[i][j] = timestamp;
						}
					} else {
						timestamps.push.apply(timestamps, labels);
						datasets[i] = labels.slice(0);
					}
				} else {
					datasets[i] = [];
				}
			}

			if (labels.length) {
				// Sort labels **after** data have been converted
				labels = arrayUnique(labels).sort(sorter);
				min = Math.min(min, labels[0]);
				max = Math.max(max, labels[labels.length - 1]);
			}

			if (timestamps.length) {
				timestamps = arrayUnique(timestamps).sort(sorter);
				min = Math.min(min, timestamps[0]);
				max = Math.max(max, timestamps[timestamps.length - 1]);
			}

			// In case there is no valid min/max, let's use today limits
			min = min === MAX_INTEGER ? +moment().startOf('day') : min;
			max = max === MIN_INTEGER ? +moment().endOf('day') + 1 : max;

			// Make sure that max is strictly higher than min (required by the lookup table)
			me.min = Math.min(min, max);
			me.max = Math.max(min + 1, max);

			// PRIVATE
			me._horizontal = me.isHorizontal();
			me._table = [];
			me._timestamps = {
				data: timestamps,
				datasets: datasets,
				labels: labels
			};
		},

		buildTicks: function() {
			var me = this;
			var min = me.min;
			var max = me.max;
			var options = me.options;
			var timeOpts = options.time;
			var formats = timeOpts.displayFormats;
			var capacity = me.getLabelCapacity(min);
			var unit = timeOpts.unit || determineUnit(timeOpts.minUnit, min, max, capacity);
			var majorUnit = determineMajorUnit(unit);
			var timestamps = [];
			var ticks = [];
			var i, ilen, timestamp;

			switch (options.ticks.source) {
			case 'data':
				timestamps = me._timestamps.data;
				break;
			case 'labels':
				timestamps = me._timestamps.labels;
				break;
			case 'auto':
			default:
				timestamps = generate(min, max, unit, majorUnit, capacity, options);
			}

			if (options.bounds === 'ticks' && timestamps.length) {
				min = timestamps[0];
				max = timestamps[timestamps.length - 1];
			}

			// Enforce limits with user min/max options
			min = parse(timeOpts.min, me) || min;
			max = parse(timeOpts.max, me) || max;

			// Remove ticks outside the min/max range
			for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
				timestamp = timestamps[i];
				if (timestamp >= min && timestamp <= max) {
					ticks.push(timestamp);
				}
			}

			me.min = min;
			me.max = max;

			// PRIVATE
			me._unit = unit;
			me._majorUnit = majorUnit;
			me._minorFormat = formats[unit];
			me._majorFormat = formats[majorUnit];
			me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution);
			me._offsets = computeOffsets(me._table, ticks, min, max, options);

			return ticksFromTimestamps(ticks, majorUnit);
		},

		getLabelForIndex: function(index, datasetIndex) {
			var me = this;
			var data = me.chart.data;
			var timeOpts = me.options.time;
			var label = data.labels && index < data.labels.length ? data.labels[index] : '';
			var value = data.datasets[datasetIndex].data[index];

			if (helpers.isObject(value)) {
				label = me.getRightValue(value);
			}
			if (timeOpts.tooltipFormat) {
				label = momentify(label, timeOpts).format(timeOpts.tooltipFormat);
			}

			return label;
		},

		/**
		 * Function to format an individual tick mark
		 * @private
		 */
		tickFormatFunction: function(tick, index, ticks) {
			var me = this;
			var options = me.options;
			var time = tick.valueOf();
			var majorUnit = me._majorUnit;
			var majorFormat = me._majorFormat;
			var majorTime = tick.clone().startOf(me._majorUnit).valueOf();
			var majorTickOpts = options.ticks.major;
			var major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime;
			var label = tick.format(major ? majorFormat : me._minorFormat);
			var tickOpts = major ? majorTickOpts : options.ticks.minor;
			var formatter = helpers.valueOrDefault(tickOpts.callback, tickOpts.userCallback);

			return formatter ? formatter(label, index, ticks) : label;
		},

		convertTicksToLabels: function(ticks) {
			var labels = [];
			var i, ilen;

			for (i = 0, ilen = ticks.length; i < ilen; ++i) {
				labels.push(this.tickFormatFunction(moment(ticks[i].value), i, ticks));
			}

			return labels;
		},

		/**
		 * @private
		 */
		getPixelForOffset: function(time) {
			var me = this;
			var size = me._horizontal ? me.width : me.height;
			var start = me._horizontal ? me.left : me.top;
			var pos = interpolate(me._table, 'time', time, 'pos');

			return start + size * (me._offsets.left + pos) / (me._offsets.left + 1 + me._offsets.right);
		},

		getPixelForValue: function(value, index, datasetIndex) {
			var me = this;
			var time = null;

			if (index !== undefined && datasetIndex !== undefined) {
				time = me._timestamps.datasets[datasetIndex][index];
			}

			if (time === null) {
				time = parse(value, me);
			}

			if (time !== null) {
				return me.getPixelForOffset(time);
			}
		},

		getPixelForTick: function(index) {
			var ticks = this.getTicks();
			return index >= 0 && index < ticks.length ?
				this.getPixelForOffset(ticks[index].value) :
				null;
		},

		getValueForPixel: function(pixel) {
			var me = this;
			var size = me._horizontal ? me.width : me.height;
			var start = me._horizontal ? me.left : me.top;
			var pos = (size ? (pixel - start) / size : 0) * (me._offsets.left + 1 + me._offsets.left) - me._offsets.right;
			var time = interpolate(me._table, 'pos', pos, 'time');

			return moment(time);
		},

		/**
		 * Crude approximation of what the label width might be
		 * @private
		 */
		getLabelWidth: function(label) {
			var me = this;
			var ticksOpts = me.options.ticks;
			var tickLabelWidth = me.ctx.measureText(label).width;
			var angle = helpers.toRadians(ticksOpts.maxRotation);
			var cosRotation = Math.cos(angle);
			var sinRotation = Math.sin(angle);
			var tickFontSize = helpers.valueOrDefault(ticksOpts.fontSize, defaults.global.defaultFontSize);

			return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation);
		},

		/**
		 * @private
		 */
		getLabelCapacity: function(exampleTime) {
			var me = this;

			me._minorFormat = me.options.time.displayFormats.millisecond;	// Pick the longest format for guestimation

			var exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, []);
			var tickLabelWidth = me.getLabelWidth(exampleLabel);
			var innerWidth = me.isHorizontal() ? me.width : me.height;

			return Math.floor(innerWidth / tickLabelWidth);
		}
	});

	Chart.scaleService.registerScaleType('time', TimeScale, defaultConfig);
};

},{"1":1,"25":25,"45":45}]},{},[7])(7)
});;(function () {
  function PercentageLabel() {
    this.drawDataset = this.drawDataset.bind(this);
  }

  PercentageLabel.prototype.beforeDatasetsUpdate = function (chartInstance) {
    if (this.parseOptions(chartInstance) && this.position === 'outside') {
      var padding = this.fontSize * 1.5 + 2;
      chartInstance.chartArea.top += padding;
      chartInstance.chartArea.bottom -= padding;
    }
  };

  PercentageLabel.prototype.afterDatasetsDraw = function (chartInstance) {
    if (!this.parseOptions(chartInstance)) {
      return;
    }
    this.labelBounds = [];
    chartInstance.config.data.datasets.forEach(this.drawDataset);
  };

  PercentageLabel.prototype.drawDataset = function (dataset) {
    var ctx = this.ctx;
    var chartInstance = this.chartInstance;
    var meta = dataset._meta[Object.keys(dataset._meta)[0]];
    var totalPercentage = 0;
    for (var i = 0; i < meta.data.length; i++) {
      var element = meta.data[i],
        view = element._view, text;

      //Skips label creation if value is zero and showZero is set
      if (view.circumference === 0 && !this.showZero) {
        continue;
      }
      switch (this.render) {
        case 'value':
          var value = dataset.data[i];
          if (this.format) {
            value = this.format(value);
          }
          text = value.toString();
          break;
        case 'label':
          text = chartInstance.config.data.labels[i];
          break;
        case 'image':
          text = this.images[i] ? this.loadImage(this.images[i]) : '';
          break;
        case 'percentage':
        default:
          var percentage = view.circumference / this.options.circumference * 100;
          percentage = parseFloat(percentage.toFixed(this.precision));
          totalPercentage += percentage;
          if (totalPercentage > 100) {
            percentage -= totalPercentage - 100;
            // After adjusting the percentage, need to trim the numbers after decimal points again, otherwise it may not show
            // on chart due to very long number after decimal point.
            percentage = parseFloat(percentage.toFixed(this.precision));
          }
          text = percentage + '%';
          break;
      }
      if (typeof this.render === 'function') {
        text = this.render({
          label: chartInstance.config.data.labels[i],
          value: dataset.data[i],
          percentage: percentage
        });

        if (typeof text === 'object') {
          text = this.loadImage(text);
        }
      }
      if (!text) {
        return;
      }
      ctx.save();
      ctx.beginPath();
      ctx.font = Chart.helpers.fontString(this.fontSize, this.fontStyle, this.fontFamily);
      var position, innerRadius, arcOffset;
      if (this.position === 'outside' ||
        this.position === 'border' && chartInstance.config.type === 'pie') {
        innerRadius = view.outerRadius / 2;
        var rangeFromCentre, offset = this.fontSize + 2,
          centreAngle = view.startAngle + ((view.endAngle - view.startAngle) / 2);
        if (this.position === 'border') {
          rangeFromCentre = (view.outerRadius - innerRadius) / 2 + innerRadius;
        } else if (this.position === 'outside') {
          rangeFromCentre = (view.outerRadius - innerRadius) + innerRadius + offset;
        }
        position = {
          x: view.x + (Math.cos(centreAngle) * rangeFromCentre),
          y: view.y + (Math.sin(centreAngle) * rangeFromCentre)
        };
        if (this.position === 'outside') {
          if (position.x < view.x) {
            position.x -= offset;
          } else {
            position.x += offset;
          }
          arcOffset = view.outerRadius + offset;
        }
      } else {
        innerRadius = view.innerRadius;
        position = element.tooltipPosition();
      }

      var fontColor = this.fontColor;
      if (typeof fontColor === 'function') {
        fontColor = fontColor({
          label: chartInstance.config.data.labels[i],
          value: dataset.data[i],
          percentage: percentage,
          text: text,
          backgroundColor: dataset.backgroundColor[i],
          dataset: dataset,
          index: i
        });
      } else if (typeof fontColor !== 'string') {
        fontColor = fontColor[i] || this.options.defaultFontColor;
      }
      if (this.arc) {
        if (!arcOffset) {
          arcOffset = (innerRadius + view.outerRadius) / 2;
        }
        ctx.fillStyle = fontColor;
        ctx.textBaseline = 'middle';
        this.drawArcText(text, arcOffset, view, this.overlap);
      } else {
        var drawable, mertrics = this.measureText(text),
          left = position.x - mertrics.width / 2,
          right = position.x + mertrics.width / 2,
          top = position.y - this.fontSize / 2,
          bottom = position.y + this.fontSize / 2;
        if (this.overlap) {
          drawable = true;
        } else if (this.position === 'outside') {
          drawable = this.checkTextBound(left, right, top, bottom);
        } else {
          drawable = element.inRange(left, top) && element.inRange(left, bottom) &&
            element.inRange(right, top) && element.inRange(right, bottom);
        }
        if (drawable) {
          this.fillText(text, position, fontColor);
        }
      }
      ctx.restore();
    }
  };

  PercentageLabel.prototype.parseOptions = function (chartInstance) {
    var percentageLabel = chartInstance.options.percentageLabel;
    if (percentageLabel) {
      this.chartInstance = chartInstance;
      this.ctx = chartInstance.chart.ctx;
      this.options = chartInstance.config.options;
      this.render = percentageLabel.render || percentageLabel.mode;
      this.position = percentageLabel.position || 'default';
      this.arc = percentageLabel.arc;
      this.format = percentageLabel.format;
      this.precision = percentageLabel.precision || 0;
      this.fontSize = percentageLabel.fontSize || this.options.defaultFontSize;
      this.fontColor = percentageLabel.fontColor || this.options.defaultFontColor;
      this.fontStyle = percentageLabel.fontStyle || this.options.defaultFontStyle;
      this.fontFamily = percentageLabel.fontFamily || this.options.defaultFontFamily;
      this.hasTooltip = chartInstance.tooltip._active && chartInstance.tooltip._active.length;
      this.showZero = percentageLabel.showZero;
      this.overlap = percentageLabel.overlap;
      this.images = percentageLabel.images || [];
      return true;
    } else {
      return false;
    }
  };

  PercentageLabel.prototype.checkTextBound = function (left, right, top, bottom) {
    var labelBounds = this.labelBounds;
    for (var i = 0;i < labelBounds.length;++i) {
      var bound = labelBounds[i];
      var potins = [
        [left, top],
        [left, bottom],
        [right, top],
        [right, bottom]
      ];
      for (var j = 0;j < potins.length;++j) {
        var x = potins[j][0];
        var y = potins[j][1];
        if (x >= bound.left && x <= bound.right && y >= bound.top && y <= bound.bottom) {
          return false;
        }
      }
      potins = [
        [bound.left, bound.top],
        [bound.left, bound.bottom],
        [bound.right, bound.top],
        [bound.right, bound.bottom]
      ];
      for (var j = 0;j < potins.length;++j) {
        var x = potins[j][0];
        var y = potins[j][1];
        if (x >= left && x <= right && y >= top && y <= bottom) {
          return false;
        }
      }
    }
    labelBounds.push({
      left: left,
      right: right,
      top: top,
      bottom: bottom
    });
    return true;
  };

  PercentageLabel.prototype.measureText = function (text) {
    if (typeof text === 'string') {
      return this.ctx.measureText(text);
    } else {
      return { width: text.width, height: text.height };
    }
  };

  PercentageLabel.prototype.fillText = function (text, position, fontColor) {
    var ctx = this.ctx;
    if (typeof text === 'string') {
      ctx.fillStyle = fontColor;
      ctx.textBaseline = 'top';
      ctx.textAlign = 'center';
      ctx.fillText(text, position.x, position.y - this.fontSize / 2);
    } else {
      ctx.drawImage(text, position.x - text.width / 2, position.y - text.height / 2, text.width, text.height);
    }
  };

  PercentageLabel.prototype.loadImage = function (obj) {
    var image = new Image();
    image.src = obj.src;
    image.width = obj.width;
    image.height = obj.height;
    return image;
  };

  PercentageLabel.prototype.drawArcText = function (str, radius, view, overlap) {
    var ctx = this.ctx,
      centerX = view.x,
      centerY = view.y,
      startAngle = view.startAngle,
      endAngle = view.endAngle;

    ctx.save();
    ctx.translate(centerX, centerY);
    var angleSize = endAngle - startAngle;
    startAngle += Math.PI / 2;
    endAngle += Math.PI / 2;
    var origStartAngle = startAngle;
    var mertrics = this.measureText(str);
    startAngle += (endAngle - (mertrics.width / radius + startAngle)) / 2;
    if (!overlap && endAngle - startAngle > angleSize) {
      ctx.restore();
      return;
    }

    if (typeof str === 'string') {
      ctx.rotate(startAngle);
      for (var i = 0; i < str.length; i++) {
        var char = str.charAt(i);
        mertrics = ctx.measureText(char);
        ctx.save();
        ctx.translate(0, -1 * radius);
        ctx.fillText(char, 0, 0);
        ctx.restore();
        ctx.rotate(mertrics.width / radius);
      }
    } else {
      ctx.rotate((origStartAngle + endAngle) / 2);
      ctx.translate(0, -1 * radius);
      this.fillText(str, { x: 0, y: 0 });
    }
    ctx.restore();
  };

  Chart.pluginService.register({
    beforeInit: function(chartInstance) {
      chartInstance.percentageLabel = new PercentageLabel();
    },
    beforeDatasetsUpdate: function (chartInstance) {
      chartInstance.percentageLabel.beforeDatasetsUpdate(chartInstance);
    },
    afterDatasetsDraw: function (chartInstance) {
      chartInstance.percentageLabel.afterDatasetsDraw(chartInstance);
    }
  });
})();;