text: /**
   * Dirty form management
   *
   * jQuery isDirty plugin ->
   *   Test whether a jQuery collection of form elements contains unsaved changes.
   *
   * jQuery removeDirtyWarning plugin ->
   *   Make a given form exempt from dirty form warnings.
   *
   * Additionally, by loading this file, a warning is displayed by default whenever
   * the user navigates away from a page while a textarea contains unsaved changes.
   */
  define('jira/jquery/plugins/isdirty', ['jira/util/logger', 'jira/util/formatter', 'jira/util/events', 'jira/util/browser', 'jquery'], function (logger, formatter, Events, Browser, $) {
  
      var DIRTY_WARNING_EXEMPT = "ajs-dirty-warning-exempt";
      var DIRTY_WARNING_SANCTIONED = "ajs-dirty-warning-whitelist";
      var DIRTY_WARNING_BY_DEFAULT = "ajs-dirty-warning-by-default";
      var DIRTY_WARNING_SUBSEQUENT_SUBMITS_ALLOWED = "ajs-dirty-warning-subsequent-submits-allowed";
      var formIsBeingSubmitted = false;
      var defaultDirtyMessage = formatter.I18n.getText("common.forms.dirty.message");
      var dirtyMessage = defaultDirtyMessage;
  
      /**
       * Checks whether the given jQuery collection contains unsaved changes, only for text fields.
       *
       * @function external:"jQuery.fn".isDirty
       * @return {boolean}
       */
      $.fn.isDirty = function () {
          // Have put this as just text inputs but have raised JRA-25803
          var $fields = this.find("*").andSelf().filter(":input");
          for (var i = 0; i < $fields.length; i++) {
              if (isElementDirty($fields[i])) {
                  return true;
              }
          }
          return false;
      };
  
      /**
       * Prevent the form (or the form this element belongs to) from participating in
       * dirty form warnings.
       *
       * @function external:"jQuery.fn".removeDirtyWarning
       * @return {jQuery} -- The original jQuery object, for chaining
       */
      $.fn.removeDirtyWarning = function () {
          $(this.form || this).closest("form").addClass(DIRTY_WARNING_EXEMPT);
          return this;
      };
  
      // If a dialog submission has triggered a window location change, giving the user the option to remain on the page
      // is a bad idea (e.g. staying on an issue page after it is deleted). Skip the dirty check!�
      Events.bind('page-unload.location-change.from-dialog', function () {
          window.onbeforeunload = function () {}; // skip dirty check
      });
  
      // Dialogs that refresh the page probably shouldn't - the page could be updated dynamically. Until that happens,
      // just give the user a slightly more descriptive message.
      Events.bind('page-unload.refresh.from-dialog', function () {
          dirtyMessage = formatter.I18n.getText("common.forms.dirty.message.from.dialog");
      });
  
      // Returns the dirty message and resets it to the default message.
      function getDirtyMessage() {
          var msg = dirtyMessage;
          dirtyMessage = defaultDirtyMessage;
          return "***\n\n" + msg + "\n\n***";
      }
  
      /**
       * Provides globally-available methods to determine the dirty status of
       * JIRA forms.
       *
       * The selector below 'input[type='text'], textarea[name]' purposely uses input[type='text']
       * instead of :text as IE8 is returning select elements when it is used (thanks IE).
       */
      var DirtyForm = {
          getInputsToCheck: function getInputsToCheck() {
              return $("input[type='text'], textarea[name], ." + DIRTY_WARNING_SANCTIONED);
          },
  
          getDirtyWarning: function getDirtyWarning() {
              var $inputs = DirtyForm.getInputsToCheck();
              for (var i = 0, ii = $inputs.size(); i < ii; i++) {
                  if (!formIsBeingSubmitted && isElementDirty($inputs[i])) {
                      return getDirtyMessage(); // returning a string forces the warning to be displayed onbeforeunload
                  }
              }
              if (formIsBeingSubmitted) {
                  formIsBeingSubmitted = false;
              }
              // return nothing - means no warning displayed on beforeunload
          },
  
          ClassNames: {
              SANCTIONED: DIRTY_WARNING_SANCTIONED,
              EXEMPT: DIRTY_WARNING_EXEMPT,
              BY_DEFAULT: DIRTY_WARNING_BY_DEFAULT,
              SUBSEQUENT_SUBMITS_ALLOWED: DIRTY_WARNING_SUBSEQUENT_SUBMITS_ALLOWED
          }
      };
  
      /**
       * Prompts the user really wants to leave the current page by utilising onbeforeunload.
       *
       * At onbeforeunload if any texarea containing forms on the page are dirty
       * (excepting a form currently being submitted) the warning is displayed.
       *
       * Selenium tests often don't clean up after themselves, and a dirty form warning dialog
       * will cause any test to fail, so the onbeforeunload handler is only defined outside
       * Selenium.
       */
      if (!Browser.isSelenium()) {
          window.onbeforeunload = DirtyForm.getDirtyWarning;
      }
  
      /**
       * Checks whether a form element has unsaved changes.
       *
       * @param {HTMLElement} element -- The form element to check.
       * @return {boolean}
       */
      function isElementDirty(element) {
          var $element = $(element),
              $form = $(element.form),
              type = element.type;
  
          if ($form.hasClass(DIRTY_WARNING_EXEMPT) || $element.hasClass(DIRTY_WARNING_EXEMPT)) {
              // Forms and elements with class="ajs-dirty-warning-exempt" are never considered dirty.
              return false;
          }
          // check if the form was rendered directly in a dirty state
          else if ($form.hasClass(DIRTY_WARNING_BY_DEFAULT)) {
                  return true;
              }
  
          if ($element.is(":hidden") && !$element.hasClass(DIRTY_WARNING_SANCTIONED)) {
              // Hidden elements are never considered dirty.
              return false;
          }
  
          // not in DOM anymore
          if (!$element.parent().length) {
              return false;
          }
  
          /* Remove functionality which deems a form with an error as being dirty.
           * See: https://jira.atlassian.com/browse/JRA-25167
           */
  
          if ($form.has(".error, .errMsg, .errorMessage, .errLabel, .ajaxerror").length > 0 || $form.prev(".error").length > 0 || $form.children('.error').length > 0) {
              // Treat the form as dirty if it contains errors -- the form data is unlikely to be saved.
              return true;
          }
  
          if ((type === "hidden" || type === "submit" || type === "button") && !$element.hasClass(DIRTY_WARNING_SANCTIONED)) {
              // Hidden inputs are never considered dirty.
              return false;
          }
  
          if (type === "select-one" || type === "select-multiple") {
              // A select box is dirty if it contains an option whose selected state differs to its defaultSelected state.
              var options = element.options;
              for (var i = 0; i < options.length; i++) {
                  var option = options[i];
                  if (option.selected !== option.defaultSelected) {
                      return true;
                  }
              }
              return false;
          }
  
          if (type === "checkbox" || type === "radio") {
              // The checkbox or radio has been changed since the page loaded.
              return element.checked !== element.defaultChecked;
          }
  
          // The field value has been changed since the page loaded.
          return element.value !== element.defaultValue;
      }
  
      /**
       * Sets any form which is submitted or cancelled (per below) as the "active" form.
       *
       * If the active form contains a textarea with a name attribute, and it's value has
       * changed from it's defaultValue then the form is considered dirty.
       */
      $(document).delegate("form", "submit", function () {
          var $form = $(this);
          if (givenFormIsNotDialogForm($form) && !givenFormAllowsSubsequentSubmits($form)) {
              formIsBeingSubmitted = true;
          }
  
          function givenFormIsNotDialogForm($form) {
              return $form.parents('.jira-dialog.jira-dialog-open').length <= 0;
          }
  
          function givenFormAllowsSubsequentSubmits($form) {
              return $form.hasClass(DIRTY_WARNING_SUBSEQUENT_SUBMITS_ALLOWED);
          }
      });
  
      /**
       * Binds the cancel event to the cancelForm function.
       *
       * The cancel event is triggered when a user clicks on the
       * cancel link/button.
       */
      //$(document).delegate(".cancel", "click", cancelForm);
      $(function () {
          $('.cancel').bind("click", cancelForm);
      });
  
      /**
       * Handles events propagated from legacy cancel buttons.
       *
       * Special treatment for legacy forms that use location.href = "returnUrl" on cancel button.
       * We need cancelForm to run before inline onclick handler (otherwise this would cause an error in IE
       * and incorrectly show the dirty form dialog).
       */
      $(function () {
          $("#cancelButton").bind("mousedown keydown click", cancelForm);
      });
  
      /**
       * Triggers the cancel event on a form or an element's form.
       *
       * If the element passed in does not have a form property, then the
       * closest ancestor form element is used.
       */
      function cancelForm() {
          logger.log('the cancel event on forms is deprecated');
          $(this.form || this).closest("form").trigger("cancel");
      }
  
      return DirtyForm;
  });
  
  AJS.namespace('JIRA.DirtyForm', null, require('jira/jquery/plugins/isdirty'));