Click here to Skip to main content
15,896,501 members
Articles / Web Development / ASP.NET

JavascriptHelper:Managing JS files for ASP.NET MVC (With Bundling)

Rate me:
Please Sign up or sign in to vote.
4.43/5 (5 votes)
30 Jun 2012CPOL17 min read 49.1K   499   20  
JavascriptHelper is a MVC component which allows you to specify that a JavaScript file is needed, wherever you need it and the helper will collection them all up, plus all their dependencies and insert all the (along with their associated CSS files) tags in one spots in the layout.
/*
 * jQuery UI wizard plug-in 0.9.5
 *
 *
 * Copyright (c) 2009 Jan Sundman (jan.sundman[at]aland.net)
 * Copyright (c) 2009 James M. Curran (jamescurran[at]mvps.org)
 
 * Licensed under the MIT licens:
 *   http://www.opensource.org/licenses/mit-license.php
 *   
 *
 * Changelog:
 * version 0.9.5a
 *  Fork of Jan's original by James.   Principal changes:
 *    - Changed API to confirm to jQuery UI standards.  Notably, it's
 *      now called wizard() instead of formwizard(). (a formwizard()
 *      function remains which adapts the old-style calls to the new API)
 *   - Added jQuery UI-ish CSS classes for styling.
 *   - Refactored some code.
 *   - added three new options:
 *    o   validationOptions:  A set of key/value pairs to set as configuration 
 *                properties for the validation plugin. (formerly a separate 
 *                parameter in formwizard)
 *    o   formOptions: A set of key/value pairs to set as configuration 
 *                properties for the form plugin. (formerly a separate parameter 
 *                in formwizard)
 *    o  autoDisableNext:  When set to true, the "Next" button is automatically 
 *                disabled, requiring it be manually enable once the user has 
 *                completed the step. 
 *   - added five new methods
 *    o enableNext() - 
 *    o disableNext() - 
 *    o enableBack() - 
 *    o disableBack() - 
 *    o gotoStep(step) - navigates to the given step.  step can be either a 0-based index, or a jQuery selector.
 *  - added one new callback
 *     o show() - called each time a new panel is displayed.          
 *         function show(evnt, args)
 *              event - standard jQuery UI event object.
 *              args - object with fields:  
 *                 step - jQuery wrapped object of the DIV being displayed.
 *                 stepInx - 0-based index of the DIV.
 *                 backwards - true if reached by clicking "back", false otherwise.
 *      (callback afterNext & afterBack remain, but are depreciated)
 * 
 * version 0.9.5
 * -------------
 * - Fix for enabling optional validation
 *
 * version 0.9.4
 * -------------
 * - Performance fixes for validation of the steps
 * - Performance fixes for rendering of the steps
 * - Introduces a need for input fields in the wizard to be disabled in the html
 *
 * version 0.9.3
 * ------------- 
 * - Fixed the continueToNextStep and backButton.click callback to handle navigation correctly when the 
 * history plugin is not used
 * 
 * version 0.9.2 
 * - A check was added to see if there are multiple links on one step. In the
 * case there are we assume they are radio buttons or checkboxes. Only the
 * one that is checked is considered a valid link. This fixes a bug where links
 * in the form of radio buttons do not work. Credits to adnanshareef for 
 * reporting the bug.  
 * 
 * - Added initial functionality for doing server-side validation 
 * 
 * version 0.9.1 
 * -------------
 * - Addition of afterNext and afterBack callbacks, can be used to do stuff after
 * the rendering of a step has been completed
 * 
 * version 0.9.0 
 * -------------
 * - Initial release
 *
 */

(function ($) {

	$.widget("ui.wizard",
	{
		options:
		{
			animated: 'fadeIn',
			historyEnabled: undefined,
			validationEnabled: undefined,
			formPluginEnabled: undefined,
			linkClass: ".link",
			submitStepClass: ".submit_step",
			back: ".wizard_back",
			next: ".wizard_next",
			help: ".wizard_help",
			textSubmit: 'Submit',
			textNext: 'Next',
			textBack: 'Back',
			afterNext: undefined,
			afterBack: undefined,
			show: undefined,
			autoNextDisabled: true,
			serverSideValidationUrls: undefined,
			formOptions: undefined,
			validationOptions: {}
		},

		_init: function () {
			// if historyEnabled not explicit defined, set it based on presence/absence of jquery.history.js plugin
			if (this.options.historyEnabled == undefined)
				this.options.historyEnabled = ($.historyInit != undefined);

			// if formPluginEnabled not explicitly set, set it based on the presents/absence of formOptions
			if (typeof (this.options.formPluginEnabled) == "undefined")
				this.options.formPluginEnabled = (this.options.formOptions == null);

			if (this.options.formPluginEnabled) {
				this.options.formOptions = $.extend({ reset: true, success: function (data) { alert("success"); } }, this.options.formOptions);
				var formOptionsSuccess = this.options.formOptions.success;
				var formSettings = $.extend(this.options.formOptions, { success: function (data) {
					if (formOptions.resetForm) {
						_navigate(0);
						if (this.options.historyEnabled) {
							$.historyLoad(0);
						}
						else {
							_renderStep();
						}
					}
					formOptionsSuccess(data);
				}
				});
			}
			else {
				if (typeof (this.options.formOptions) == "undefined")
					this.options.formOptions = { success: function () { } };
				else
					this.options.formOptions.success = function () { };
			}
			/**
			* Initialization
			*/

			this.element.addClass("ui-wizard ui-widget ui-helper-reset");

			this.steps = this.element.find(".step");
			this.steps.addClass("ui-wizard-content ui-helper-reset ui-widget-content ui-corner-all")

			this.currentStep = 0;
			this.previousStep = undefined;
			this.backButton = this.element.find(this.options.back);
			this.nextButton = this.element.find(this.options.next);
			this.helpButton = this.element.find(this.options.help);

			this.backButton.addClass("ui-wizard-content ui-helper-reset ui-state-default ui-state-hover  ui-state-active");
			this.nextButton.addClass("ui-wizard-content ui-helper-reset ui-state-default ui-state-hover  ui-state-active");
			this.helpButton.addClass("ui-wizard-content ui-helper-reset ui-state-default  ui-button ui-icon ui-icon-help")
				.hide();

			jQuery(".help").hide();

			this.activatedSteps = new Array();
			this.isLastStep = false;
			var wizard = this;         // for closures in the next lines.
			this.helpButton.click(function () {
				jQuery(".help", wizard.steps[wizard.currentStep])
				.clone()
				.dialog({
					resizable: false,
					autoOpen: true,
					height: 250,
					width: 500,
					modal: true,
					overlay:
									{
										backgroundColor: '#000',
										opacity: 0.5
									}
				});
			});

			if (this.options.historyEnabled) {
				if ($.historyInit == undefined) {
					this.options.historyEnabled = false;
					alert("the history plugin needs to be included");
				}
				else {
					location.hash = "";
					$.historyInit(function (hash) { wizard._handleHistory(hash); });
				}
			}
			else {
				this._handleHistory(0);
			}

			if (this.options.validationEnabled == undefined)
				this.options.validationEnabled = (this.options.validationSettings == {});

			if (jQuery().validate == undefined) {
				if (this.options.validationEnabled) {
					this.options.validationEnabled = false;
					alert("the validation plugin needs to be included");
				}
			}
			else if (this.options.validationEnabled) {
				this.element.validate(this.options.validationSettings);
			}

			if (this.options.formPluginEnabled && jQuery().ajaxSubmit == undefined) {
				this.options.formPluginEnabled = false;
				alert("the form plugin needs to be included");
			}

			/** 
			* Navigation event callbacks 
			*/
			var wizard = this;         // for closures in the next lines.
			this.nextButton.click(function () { return wizard._nextButton_click(this); });
			this.backButton.click(function () { return wizard._backButton_click(this); });
			this.backButton.val(this.options.textBack).text(this.options.textBack);
			$("input", this.element).attr("disabled", "disabled");
			this.steps.hide();
			this._renderStep();
			return $(this);
		},

		destroy: function () {
			this.element.removeClass("ui-formwizard ui-widget ui-helper-reset");
			this.steps.removeClass("ui-wizard-content ui-helper-reset ui-widget-content ui-corner-all")
			this.backButton.removeClass("ui-wizard-content ui-helper-reset ui-state-default ui-state-hover  ui-state-active  ui-state-disabled")
				.unbind("click");
			this.nextButton.removeClass("ui-wizard-content ui-helper-reset ui-state-default ui-state-hover  ui-state-active  ui-state-disabled")
				.unbind("click");
		},

		/* 
		***********************************
		*/
		activate: function (step) {
			gotoStep(step);
		},
		/* 
		***********************************
		*/
		_backButton_click: function (btn) {
			if (this.activatedSteps.length > 0) {
				if (this.options.historyEnabled) {
					history.back();
				}
				else {
					this._handleHistory(this.activatedSteps[this.activatedSteps.length - 2]);
				}
			}

			this._trigger("afterBack");

			return false;
		},
		/**
		* Checks if the step is the last step in a wizard route
		*
		* @name checkIflastStep
		* @type undefined
		* @param Number step The step to check.
		*/
		_checkIflastStep: function (step) {
			var link = this._getLink($(this.steps[step]));

			this.isLastStep = false;

			if ((("." + link) == this.options.submitStepClass) || (link == undefined && (step * 1) == this.steps.length - 1)) {
				this.isLastStep = true;
			}
		},

		/**
		* Continues to the next step in the wizard
		*/
		_continueToNextStep: function () {
			this._navigate(this.currentStep);
			this._renderStep();

			if (this.options.historyEnabled) {
				$.historyLoad(this.currentStep);
			}
			else {
				this._handleHistory(this.currentStep);
			}

			this._trigger("afterNext");
		},

		/* 
		***********************************
		*/
		disableBack: function () {
			this.backButton.attr('disabled', 'disabled').addClass("ui-state-disabled");
		},

		/* 
		***********************************
		*/
		disableNext: function () {
			this.nextButton.attr('disabled', 'disabled').addClass("ui-state-disabled");
		},

		/* 
		***********************************
		*/
		enableBack: function () {
			this.backButton.removeAttr("disabled").removeClass("ui-state-disabled");
		},
		/* 
		***********************************
		*/
		enableNext: function () {
			this.nextButton.removeAttr("disabled").removeClass("ui-state-disabled");
		},

		/**
		* Finds the valid link for the step (if there is one)
		*
		* @name getLink
		* @type String
		* @param Number step The step to search for valid links
		*/
		_getLink: function (step) {
			var link = undefined;
			var links = step.find(this.options.linkClass);

			if (links.length == 1) {
				link = links.val();
			}
			else if (links.length > 1) {
				// assume that the link is a radio button or checkbox
				link = step.find(this.options.linkClass + ":checked").val();
			}

			return link;
		},

		gotoStep: function (step) {
			var stepInx;
			if (typeof (step) == "string")
				stepInx = this.steps.index($(step));
			else
				stepInx = step;

			// If in range, go there.  Otherwise, silently exit.
			if (stepInx >= 0 && stepInx < this.steps.length) {
				jQuery(this.steps[this.currentStep]).hide();
				this._handleHistory(stepInx);
			}
		},

		/**
		* Handles back navigation (and browser back and forward buttons if history is enabled)
		*
		* @name handleHistory
		* @type undefined
		* @param String hash The hash used in the browser history
		
		If hash ==previous step, remove current step from activatedSteps, and then goto previous.
		Otherwise, add hash to end of activatedSteps, and go there.
		*/
		_handleHistory: function (hash) {
			var direction = false;
			if (!hash) {
				hash = 0;
			}
			if (this.activatedSteps[this.activatedSteps.length - 2] == hash) {
				this.activatedSteps.pop();
				direction = true;
			}
			else {
				this.activatedSteps.push(hash);
			}
			this.previousStep = this.currentStep;
			this.currentStep = hash;
			this._checkIflastStep(hash);
			this._renderStep();

			if (this.options.autoNextDisabled)
				this.disableNext();
			this._trigger("show", null, { step: $(this.steps[this.currentStep]), stepInx: this.currentStep, backward: direction });

		},
		moveNext: function () {
			this._nextButton_click(null);
		},
		/**
		* Decides and sets the current step in the wizard 
		*
		* @name navigate
		* @type undefined
		* @param Number step The step to navigate from.
		*/
		_navigate: function (stepInx) {
			var step = $(this.steps[stepInx]);
			var link = this._getLink(step);

			if (link) {
				var navigationTarget = this.steps.index($("#" + link));
				if (navigationTarget.length == 0) {
					return;
				}
				else {
					this.previousStep = this.currentStep;
					this.currentStep = navigationTarget;
				}

				this._checkIflastStep(step);
			}
			else if (!this.isLastStep) {
				this.previousStep = this.currentStep;
				this.currentStep++
				this._checkIflastStep(this.currentStep);
			}
		},
		/* 
		***********************************
		*/
		_nextButton_click: function (btn) {
			if (this.options.validationEnabled) {
				var valid = true;
				var form = this.element;
				$.each(form.find("input:enabled, select:enabled"), function () {
					if (form.validate().element($(this)) == false)
						valid = false;
				})
				if (!valid) return false;
			}

			if (this.isLastStep) {
				if (this.options.formPluginEnabled) {
					this.element.ajaxSubmit(this.options.formSettings);
					return false;
				}
				this.element.submit();
				return false;
			}

			// Doing server side validation for the steps
			if (this.options.serverSideValidationUrls) {
				var url = "";
				var errorCallback = undefined;
				$.each(this.options.serverSideValidationUrls, function () {
					if (this.validation.step == this.currentStep) {
						url = this.validation.url;
						errorCallback = this.validation.error;
					}
				});

				if (url != "") {
					this.element.ajaxSubmit(
							{ url: url,
								success: function () { this._continueToNextStep(); },
								error: function () { errorCallback(); }
							});
					alert("server side done");
					return false;
				}
			}
			this._continueToNextStep();
			return false;
		},

		/**
		* Renders the current step and disables the input fields in other steps
		*
		* @name renderStep
		* @type undefined
		*/
		_renderStep: function () {
			this.enableBack();
			this.nextButton.val(this.options.textNext).text(this.options.textNext);
			//            var steps = this._getData('steps');

			if (this.previousStep != undefined) {
				this.steps.eq(this.previousStep).hide()
					.find("input")
					.attr("disabled", "disabled");
			}
			var effect = this.options.animated;
			this.steps.eq(this.currentStep)[effect]()
			   .find("input").removeAttr("disabled");

			if (this.isLastStep) {
				for (var i = 0; i < this.activatedSteps.length; i++) {
					this.steps.eq(this.activatedSteps[i]).find("input").removeAttr("disabled");
				}
				this.nextButton.val(this.options.textSubmit).text(this.options.textSubmit);
			}
			else if (this.currentStep == 0) {
				this.disableBack();
			}
			this._showHelp();
		},

		_showHelp: function () {
			if (jQuery(".help", this.steps[this.currentStep]).length > 0) {
				this.helpButton.show();
			}
			else {
				this.helpButton.hide();
			}
		}
	});


	/**
	* Creates a wizard of all matched elements
	*
	* @constructor
	* @name $.formwizard
	* @param Hash wizardSettings A set of key/value pairs to set as configuration properties for the wizard plugin.
	* @param Hash validationSettings A set of key/value pairs to set as configuration properties for the validation plugin.
	* @param Hash formOptions A set of key/value pairs to set as configuration properties for the form plugin.
	*/

	$.fn.formwizard = function (wizardSettings, validationSettings, formOptions) {
		return this.formwizard($.extend(wizardSettings, { back: ":reset", next: ":submit", autoNextDisabled: false, validationOptions: validationSettings, formOptions: formOptions }));
	}
}
)(jQuery);

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) NovelTheory LLC
United States United States
20+ years as a developer : Assembly, C, C++ and C# (in that order) with sidelines in ASP/VBScript, ASP.Net, JavaScript, Perl, QuickBasic, VisualBasic, plus a few others which I'm not going to mention because if I did someone might ask me to use them again (shudder)

Microsoft MVP in VC++ (1994-2004)

I also run www.NJTheater.com as a hobby.

Full resume & stuff at NovelTheory.com

Underused blog at HonestIllusion.com

Comments and Discussions