diff --git a/lib/jquery.dialog2.js b/lib/jquery.dialog2.js index 5d894f0..9d21342 100644 --- a/lib/jquery.dialog2.js +++ b/lib/jquery.dialog2.js @@ -1,86 +1,86 @@ /* * Dialog2: Yet another dialog plugin for jQuery. - * - * This time based on bootstrap styles with some nice ajax control features, + * + * This time based on bootstrap styles with some nice ajax control features, * zero dependencies to jQuery.UI and basic options to control it. - * - * Licensed under the MIT license - * http://www.opensource.org/licenses/mit-license.php - * + * + * Licensed under the MIT license + * http://www.opensource.org/licenses/mit-license.php + * * @version: 2.0.0 (22/03/2012) - * - * @requires jQuery >= 1.4 - * - * @requires jQuery.form plugin (http://jquery.malsup.com/form/) >= 2.8 for ajax form submit + * + * @requires jQuery >= 1.4 + * + * @requires jQuery.form plugin (http://jquery.malsup.com/form/) >= 2.8 for ajax form submit * @requires jQuery.controls plugin (https://github.com/Nikku/jquery-controls) >= 0.9 for ajax link binding support - * + * * @requires bootstrap styles (twitter.github.com/bootstrap) in version 2.x to look nice - * + * * @author nico.rehwaldt */ (function($) { - + /** * Dialog html markup */ - var __DIALOG_HTML = ""; - + /** - * Constructor of Dialog2 internal representation + * Constructor of Dialog2 internal representation */ var Dialog2 = function(element, options) { this.__init(element, options); - + var dialog = this; var handle = this.__handle; - - this.__ajaxCompleteTrigger = $.proxy(function() { - this.trigger("dialog2.ajax-complete"); + + this.__ajaxCompleteTrigger = $.proxy(function(response,status,xhr,form) { + this.trigger("dialog2.ajax-complete",[response,status,xhr,form,dialog]); this.trigger("dialog2.content-update"); }, handle); - + this.__ajaxStartTrigger = $.proxy(function() { this.trigger("dialog2.ajax-start"); }, handle); - - /* - * Allows us to change the xhr based on our dialog, e.g. - * attach url parameter or http header to identify it) + + /* + * Allows us to change the xhr based on our dialog, e.g. + * attach url parameter or http header to identify it) */ this.__ajaxBeforeSend = $.proxy(function(xhr, settings) { handle.trigger("dialog2.before-send", [xhr, settings]); - + if ($.isFunction($.fn.dialog2.defaults.beforeSend)) { $.fn.dialog2.defaults.beforeSend.call(this, xhr, settings); } }, handle); - + this.__removeDialog = $.proxy(this.__remove, this); - + handle.bind("dialog2.ajax-start", function() { dialog.options({buttons: options.autoAddCancelButton ? localizedCancelButton() : {}}); handle.parent().addClass("loading"); }); - + handle.bind("dialog2.content-update", function() { dialog.__ajaxify(); dialog.__updateMarkup(); dialog.__focus(); }); - + handle.bind("dialog2.ajax-complete", function() { handle.parent().removeClass("loading"); }); - + // Apply options to make title and stuff shine this.options(options); @@ -88,30 +88,30 @@ // aka apply ajax styles in case this is a inpage dialog handle.trigger("dialog2.content-update"); }; - + /** * Dialog2 api; methods starting with underscore (__) are regarded internal * and should not be used in production environments */ Dialog2.prototype = { - + /** * Core function for creating new dialogs. * Transforms a jQuery selection into dialog content, following these rules: - * + * * // selector is a dialog? Does essentially nothing * $(".selector").dialog2(); - * + * * // .selector known? * // creates a dialog wrapped around .selector * $(".selector").dialog2(); - * + * * // creates a dialog wrapped around .selector with id foo * $(".selector").dialog2({id: "foo"}); - * + * * // .unknown-selector not known? Creates a new dialog with id foo and no content * $(".unknown-selector").dialog2({id: "foo"}); - */ + */ __init: function(element, options) { var selection = $(element); var handle; @@ -119,12 +119,12 @@ if (!selection.is(".modal-body")) { var overlay = $('').hide(); var parentHtml = $(__DIALOG_HTML); - + if (options.modalClass) { parentHtml.addClass(options.modalClass); delete options.modalClass; } - + $(".modal-header a.close", parentHtml) .text(unescape("%D7")) .click(function(event) { @@ -137,13 +137,13 @@ }); $("body").append(overlay).append(parentHtml); - + handle = $(".modal-body", parentHtml); // Create dialog body from current jquery selection - // If specified body is a div element and only one element is + // If specified body is a div element and only one element is // specified, make it the new modal dialog body - // Allows us to do something like this + // Allows us to do something like this // $('
').dialog2(); $("#foo").dialog2("open"); if (selection.is("div") && selection.length == 1) { handle.replaceWith(selection); @@ -161,37 +161,37 @@ } else { handle = selection; } - + this.__handle = handle; this.__overlay = handle.parent().prev(".modal-backdrop"); - + this.__addFocusCatchers(parentHtml); - }, - + }, + __addFocusCatchers: function(parentHandle) { parentHandle.prepend(new FocusCatcher(this.__handle, true)); parentHandle.append(new FocusCatcher(this.__handle, false)); - }, - + }, + /** * Parse dialog content for markup changes (new buttons or title) */ __updateMarkup: function() { var dialog = this; var e = dialog.__handle; - + e.trigger("dialog2.before-update-markup"); - + // New options for dialog var options = {}; - // Add buttons to dialog for all buttons found within + // Add buttons to dialog for all buttons found within // a .form-actions area inside the dialog - - // Instead of hiding .form-actions we remove it from view to fix an issue with ENTER not submitting forms + + // Instead of hiding .form-actions we remove it from view to fix an issue with ENTER not submitting forms // when the submit button is not displayed var actions = $(".form-actions", e).css({ position: "absolute", left: "-9999px", width: "1px", height: "1px" }); - + var buttons = actions.find("input[type=submit], input[type=button], input[type=reset], button, .btn"); if (buttons.length) { @@ -203,7 +203,7 @@ options.buttons[name] = { primary: button.is("input[type=submit] .btn-primary"), - type: button.attr("class"), + type: button.attr("class"), click: function(event) { // simulate click on the original button // to not destroy any event handlers @@ -225,10 +225,10 @@ // apply options on dialog dialog.options(options); - + e.trigger("dialog2.after-update-markup"); }, - + /** * Apply ajax specific dialog behavior to the dialogs contents */ @@ -237,83 +237,83 @@ var e = this.__handle; e.trigger("dialog2.before-ajaxify"); - + e.find("a.ajax").click(function(event) { var url = $(this).attr("href"); dialog.load(url); event.preventDefault(); }).removeClass("ajax"); - // Make submitable for an ajax form + // Make submitable for an ajax form // if the jquery.form plugin is provided if ($.fn.ajaxForm) { var options = { target: e, success: dialog.__ajaxCompleteTrigger, - beforeSubmit: dialog.__ajaxStartTrigger, - beforeSend: dialog.__ajaxBeforeSend, + beforeSubmit: dialog.__ajaxStartTrigger, + beforeSend: dialog.__ajaxBeforeSend, error: function() { throw dialogError("Form submit failed: " + $.makeArray(arguments)); } }; - + $("form.ajax", e) .removeClass("ajax") .ajaxForm(options); } - + e.trigger("dialog2.after-ajaxify"); }, - + /** - * Removes the dialog instance and its + * Removes the dialog instance and its * overlay from the DOM */ __remove: function() { this.__overlay.remove(); this.__handle.removeData("dialog2").parent().remove(); - }, - + }, + /** * Focuses the dialog which will essentially focus the first * focusable element in it (e.g. a link or a button on the button bar). - * + * * @param backwards whether to focus backwards or not */ __focus: function(backwards) { var dialog = this.__handle; - + // Focus first focusable element in dialog var focusable = dialog .find("a, input:not(*[type=hidden]), .btn, select, textarea, button") .filter(function() { return $(this).parents(".form-actions").length == 0; }).eq(0); - + // may be a button, too var focusableButtons = dialog .parent() .find(".modal-footer") .find("input[type=submit], input[type=button], .btn, button"); - + var focusableElements = focusable.add(focusableButtons); var focusedElement = focusableElements[backwards ? "last" : "first"](); - + // Focus the element focusedElement.focus(); - + dialog.trigger("dialog2.focussed", [focusedElement.get(0)]); return this; - }, - + }, + /** * Focuses the dialog which will essentially focus the first * focusable element in it (e.g. a link or a button on the button bar). */ focus: function() { return this.__focus(); - }, - + }, + /** * Close the dialog, removing its contents from the DOM if that is * configured. @@ -321,24 +321,24 @@ close: function() { var dialog = this.__handle; var overlay = this.__overlay; - + overlay.hide(); - + dialog .parent().hide().end() .trigger("dialog2.closed") .removeClass("opened"); }, - + /** * Open a dialog, if it is not opened already */ open: function() { var dialog = this.__handle; - + if (!dialog.is(".opened")) { this.__overlay.show(); - + dialog .trigger("dialog2.before-open") .addClass("opened") @@ -346,21 +346,21 @@ .show() .end() .trigger("dialog2.opened"); - + this.__focus(); } - }, - + }, + /** * Add button with the given name and options to the dialog - * + * * @param name of the button - * @param options either function or options object configuring + * @param options either function or options object configuring * the behaviour and markup of the button */ addButton: function(name, options) { var handle = this.__handle; - + var callback = $.isFunction(options) ? options : options.click; var footer = handle.siblings(".modal-footer"); @@ -381,90 +381,90 @@ } footer.append(button); - }, - + }, + /** * Remove button with the given name - * + * * @param name of the button to be removed */ removeButton: function(name) { var footer = this.__handle.siblings(".modal-footer"); - + footer .find("a.btn") .filter(function(i, e) {return $(e).text() == name;}) .remove(); - + return this; - }, - + }, + /** * Load the given url as content of this dialog - * + * * @param url to be loaded via GET */ load: function(url) { var handle = this.__handle; - + if (handle.is(":empty")) { var loadText = this.options().initialLoadText; handle.html($("").text(loadText)); } - + handle .trigger("dialog2.ajax-start"); - + dialogLoad.call(handle, url, this.__ajaxCompleteTrigger, this.__ajaxBeforeSend); - + return this; }, - + /** * Apply the given options to the dialog - * + * * @param options to be applied */ options: function(options) { var storedOptions = this.__handle.data("options"); - + // Return stored options if getter was called if (!options) { return storedOptions; } - + var buttons = options.buttons; delete options.buttons; - + // Store options if none have been stored so far if (!storedOptions) { this.__handle.data("options", options); } - + var dialog = this; - + var handle = dialog.__handle; var overlay = dialog.__overlay; - + var parentHtml = handle.parent(); - + if (options.title) { $(".modal-header h3", parentHtml).text(options.title); } - + if (buttons) { if (buttons.__mode != "append") { $(".modal-footer", parentHtml).empty(); } - + $.each(buttons, function(name, value) { dialog.addButton(name, value); }); } - + if (__boolean(options.closeOnOverlayClick)) { overlay.unbind("click"); - + if (options.closeOnOverlayClick) { overlay.click(function(event) { if ($(event.target).is(".modal-backdrop")) { @@ -473,36 +473,36 @@ }); } } - + if (__boolean(options.showCloseHandle)) { var closeHandleMode = options.showCloseHandle ? "show" : "hide"; $(".modal-header .close", parentHtml)[closeHandleMode](); } - + if (__boolean(options.removeOnClose)) { handle.unbind("dialog2.closed", this.__removeDialog); - + if (options.removeOnClose) { handle.bind("dialog2.closed", this.__removeDialog); } } - + if (options.autoOpen === true) { this.open(); } - + if (options.content) { this.load(options.content); } - + delete options.buttons; - + options = $.extend(true, {}, storedOptions, options); this.__handle.data("options", options); - + return this; - }, - + }, + /** * Returns the html handle of this dialog */ @@ -510,10 +510,10 @@ return this.__handle; } }; - + /** - * Returns a simple DOM node which -- while being invisible to the user -- - * should focus the given argument when the focus is directed to itself. + * Returns a simple DOM node which -- while being invisible to the user -- + * should focus the given argument when the focus is directed to itself. */ function FocusCatcher(dialog, reverse) { return $("") @@ -524,38 +524,38 @@ event.preventDefault(); }); }; - + /** * Plugging the extension into the jQuery API */ $.extend($.fn, { - + /** * options = { - * title: "Some title", - * id: "my-id", + * title: "Some title", + * id: "my-id", * buttons: { - * "Name": Object || function + * "Name": Object || function * } * }; - * + * * $(".selector").dialog2(options); - * - * or - * + * + * or + * * $(".selector").dialog2("method", arguments); */ dialog2: function() { var args = $.makeArray(arguments); var arg0 = args.shift(); - + var dialog = $(this).data("dialog2"); if (!dialog) { var options = $.extend(true, {}, $.fn.dialog2.defaults); if ($.isPlainObject(arg0)) { options = $.extend(true, options, arg0); } - + dialog = new Dialog2(this, options); dialog.handle().data("dialog2", dialog); } else { @@ -567,7 +567,7 @@ } else { throw new __error("Unknown API method '" + arg0 + "'"); } - } else + } else if ($.isPlainObject(arg0)) { dialog.options(arg0); } else { @@ -578,11 +578,11 @@ return dialog.handle(); } }); - + /*********************************************************************** * Closing dialog via ESCAPE key ***********************************************************************/ - + $(document).ready(function() { $(document).keyup(function(event) { if (event.which == 27) { // ESCAPE key pressed @@ -595,8 +595,8 @@ } }); }); - - + + /*********************************************************************** * Limit TAB integration in open modals via keypress ***********************************************************************/ @@ -618,7 +618,7 @@ return; } } - + // Did not return; have to focus active modal dialog $(".modal-body.opened").dialog2("focus"); }, 0); @@ -628,43 +628,43 @@ }); /** - * Random helper functions; today: + * Random helper functions; today: * Returns true if value is a boolean - * + * * @param value the value to check * @return true if the value is a boolean */ function __boolean(value) { return typeof value == "boolean"; }; - + /** * Creates a dialog2 error with the given message - * + * * @param errorMessage stuff to signal the user * @returns the error object to be thrown */ function __error(errorMessage) { new Error("[jquery.dialog2] " + errorMessage); }; - + /** * Dialog2 plugin defaults (may be overriden) */ $.fn.dialog2.defaults = { - autoOpen: true, - closeOnOverlayClick: true, - removeOnClose: true, - showCloseHandle: true, - initialLoadText: "", - closeOnEscape: true, + autoOpen: true, + closeOnOverlayClick: true, + removeOnClose: true, + showCloseHandle: true, + initialLoadText: "", + closeOnEscape: true, beforeSend: null }; - + /*********************************************************************** * Localization ***********************************************************************/ - + $.fn.dialog2.localization = { "de": { cancel: "Abbrechen" @@ -673,27 +673,27 @@ cancel: "Cancel" } }; - + var lang = $.fn.dialog2.localization["en"]; - + /** * Localizes a given key using the selected language - * + * * @param key the key to localize * @return the localization of the key or the key itself if it could not be localized. */ function localize(key) { return lang[key.toLowerCase()] || key; }; - + /** - * Creates a localized button and returns the buttons object specifying + * Creates a localized button and returns the buttons object specifying * a number of buttons. May pass a buttons object to add the button to. - * + * * @param name to be used as a button label (localized) * @param functionOrOptions function or options to attach to the button * @param buttons object to attach the button to (may be null to create new one) - * + * * @returns buttons object or new object with the button added */ function localizedButton(name, functionOrOptions, buttons) { @@ -701,14 +701,14 @@ buttons[localize(name)] = functionOrOptions; return buttons; }; - + /** * Expose some localization helper methods via $.fn.dialog2.localization */ $.extend($.fn.dialog2.localization, { - localizedButton: localizedButton, - get: localize, - + localizedButton: localizedButton, + get: localize, + setLocale: function(key) { var localization = $.fn.dialog2.localization[key]; @@ -719,10 +719,10 @@ } } }); - + /** * Returns a localized cancel button - * @return a buttons object containing a localized cancel button + * @return a buttons object containing a localized cancel button * (including its close functionality) */ function localizedCancelButton() { @@ -730,12 +730,12 @@ $(this).dialog2("close"); }); }; - + /*********************************************************************** * jQuery load with before send integration * copied from jQuery.fn.load but with beforeSendCallback support ***********************************************************************/ - + function dialogLoad(url, completeCallback, beforeSendCallback) { // Don't do a request if no elements are being requested if ( !this.length ) { @@ -750,7 +750,7 @@ selector = url.slice( off, url.length ); url = url.slice( 0, off ); } - + // Request the remote document jQuery.ajax({ url: url, @@ -758,7 +758,7 @@ // if "type" variable is undefined, then "GET" method will be used type: type, dataType: "html", - beforeSend: beforeSendCallback, + beforeSend: beforeSendCallback, complete: function( jqXHR, status ) { if ( completeCallback ) { self.each( completeCallback, response || [ jqXHR.responseText, status, jqXHR ] ); @@ -789,23 +789,23 @@ return this; } - + /*********************************************************************** * Integration with jquery.controls ***********************************************************************/ - + /** * Register opening of a dialog on annotated links - * (works only if jquery.controls plugin is installed). + * (works only if jquery.controls plugin is installed). */ if ($.fn.controls && $.fn.controls.bindings) { $.extend($.fn.controls.bindings, { "a.open-dialog": function() { var a = $(this).removeClass("open-dialog"); - + var id = a.attr("rel"); var content = a.attr("href"); - + var options = { modal: true }; @@ -822,24 +822,24 @@ options.id = id; } } - + if (a.attr("data-dialog-class")) { options.modalClass = a.attr("data-dialog-class"); } - + if (a.attr("title")) { options.title = a.attr("title"); } - + $.each($.fn.dialog2.defaults, function(key, value) { if (a.attr(key)) { options[key] = a.attr(key) == "true"; } }); - + if (content && content != "#") { options.content = content; - + a.click(function(event) { event.preventDefault(); $(element || "
").dialog2(options); @@ -847,12 +847,12 @@ } else { options.removeOnClose = false; options.autoOpen = false; - + element = element || "
"; - + // Pre initialize dialog $(element).dialog2(options); - + a.attr("href", "#") .click(function(event) { event.preventDefault(); @@ -862,4 +862,4 @@ } }); }; -})(jQuery); \ No newline at end of file +})(jQuery);