jquery-dirtyforms-rails 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.md +45 -0
- data/Rakefile +2 -0
- data/lib/jquery-dirtyforms-rails.rb +10 -0
- data/lib/jquery-dirtyforms-rails/version.rb +7 -0
- data/vendor/assets/javascripts/jquery.dirtyforms.dialogs.blockui.js +97 -0
- data/vendor/assets/javascripts/jquery.dirtyforms.dialogs.bootstrap.js +110 -0
- data/vendor/assets/javascripts/jquery.dirtyforms.dialogs.facebox.js +126 -0
- data/vendor/assets/javascripts/jquery.dirtyforms.dialogs.jquery-ui.js +96 -0
- data/vendor/assets/javascripts/jquery.dirtyforms.dialogs.pnotify.js +159 -0
- data/vendor/assets/javascripts/jquery.dirtyforms.helpers.alwaysdirty.js +27 -0
- data/vendor/assets/javascripts/jquery.dirtyforms.helpers.ckeditor.js +67 -0
- data/vendor/assets/javascripts/jquery.dirtyforms.helpers.tinymce.js +158 -0
- data/vendor/assets/javascripts/jquery.dirtyforms.js +596 -0
- metadata +60 -0
@@ -0,0 +1,159 @@
|
|
1
|
+
/*!
|
2
|
+
PNotify dialog module (for jQuery Dirty Forms) | v | github.com/snikch/jquery.dirtyforms
|
3
|
+
(c) 2015-2016 Shad Storhaug
|
4
|
+
License MIT
|
5
|
+
*/
|
6
|
+
|
7
|
+
(function($, window, document, undefined) {
|
8
|
+
// Can't use ECMAScript 5's strict mode because several apps
|
9
|
+
// including ASP.NET trace the stack via arguments.caller.callee
|
10
|
+
// and Firefox dies if you try to trace through "use strict" call chains.
|
11
|
+
// See jQuery issue (#13335)
|
12
|
+
// Support: Firefox 18+
|
13
|
+
//"use strict";
|
14
|
+
|
15
|
+
var modal_overlay,
|
16
|
+
notice,
|
17
|
+
isPN1 = typeof PNotify !== 'function';
|
18
|
+
|
19
|
+
$.DirtyForms.dialog = {
|
20
|
+
|
21
|
+
// Custom properties and methods to allow overriding (may differ per dialog)
|
22
|
+
title: 'Are you sure you want to do that?',
|
23
|
+
class: 'dirty-dialog',
|
24
|
+
proceedButtonText: 'Leave This Page',
|
25
|
+
stayButtonText: 'Stay Here',
|
26
|
+
styling: 'bootstrap3',
|
27
|
+
width: '330',
|
28
|
+
|
29
|
+
// Typical Dirty Forms Properties and Methods
|
30
|
+
open: function (choice, message, ignoreClass) {
|
31
|
+
var content = $.extend(true, {}, {
|
32
|
+
title: this.title,
|
33
|
+
hide: false,
|
34
|
+
styling: this.styling,
|
35
|
+
width: this.width,
|
36
|
+
|
37
|
+
// 3.x and 2.x confirm buttons
|
38
|
+
confirm: {
|
39
|
+
confirm: true,
|
40
|
+
align: 'center',
|
41
|
+
buttons: [
|
42
|
+
{
|
43
|
+
text: this.proceedButtonText,
|
44
|
+
addClass: 'dirty-proceed ' + ignoreClass
|
45
|
+
},
|
46
|
+
{
|
47
|
+
text: this.stayButtonText,
|
48
|
+
addClass: 'dirty-stay ' + ignoreClass
|
49
|
+
}
|
50
|
+
]
|
51
|
+
},
|
52
|
+
// 3.x don't use history
|
53
|
+
history: {
|
54
|
+
history: false
|
55
|
+
},
|
56
|
+
// 3.x modal dialog
|
57
|
+
addclass: 'stack-modal ' + this.class,
|
58
|
+
stack: { 'dir1': 'down', 'dir2': 'right', 'modal': true },
|
59
|
+
|
60
|
+
// 3.x and 2.x hide closer and sticker
|
61
|
+
buttons: {
|
62
|
+
closer: false,
|
63
|
+
sticker: false
|
64
|
+
},
|
65
|
+
// 1.x hide closer and sticker
|
66
|
+
closer: false,
|
67
|
+
sticker: false,
|
68
|
+
|
69
|
+
// NOTE: Animate does not seem to work in 3.x in conjunction with confirm,
|
70
|
+
// but this is being added so the settings can be supplied externally should
|
71
|
+
// this issue be fixed. https://github.com/sciactive/pnotify/issues/224
|
72
|
+
animate: this.animate === undefined ? undefined : this.animate,
|
73
|
+
|
74
|
+
text: !isPN1 ? message :
|
75
|
+
'<span class="' + this.class + '">' +
|
76
|
+
'<p>' + message + '</p>' +
|
77
|
+
'<span style="display:block;text-align:center;">' +
|
78
|
+
'<button type="button" class="btn btn-default dirty-proceed ' + ignoreClass + '">' + this.proceedButtonText + '</button> ' +
|
79
|
+
'<button type="button" class="btn btn-default dirty-stay ' + ignoreClass + '">' + this.stayButtonText + '</button>' +
|
80
|
+
'</span>' +
|
81
|
+
'</span>',
|
82
|
+
|
83
|
+
// Not supported in 3.x
|
84
|
+
before_open: function (PNotify) {
|
85
|
+
if (!isPN1) {
|
86
|
+
// Position this notice in the center of the screen.
|
87
|
+
PNotify.get().css({
|
88
|
+
"top": ($(window).height() / 2) - (PNotify.get().height() / 2),
|
89
|
+
"left": ($(window).width() / 2) - (PNotify.get().width() / 2)
|
90
|
+
});
|
91
|
+
}
|
92
|
+
|
93
|
+
// Make a modal screen overlay.
|
94
|
+
if (modal_overlay) modal_overlay.fadeIn("fast");
|
95
|
+
else modal_overlay = $("<div />", {
|
96
|
+
"class": "ui-widget-overlay",
|
97
|
+
"css": {
|
98
|
+
"display": "none",
|
99
|
+
"position": "fixed",
|
100
|
+
"top": "0",
|
101
|
+
"bottom": "0",
|
102
|
+
"right": "0",
|
103
|
+
"left": "0"
|
104
|
+
}
|
105
|
+
}).appendTo("body").fadeIn("fast");
|
106
|
+
}
|
107
|
+
});
|
108
|
+
|
109
|
+
// Patch for PNotify 1.x
|
110
|
+
notice = !isPN1 ? new PNotify(content) : $.pnotify(content);
|
111
|
+
|
112
|
+
// Bind Events
|
113
|
+
choice.bindEnterKey = true;
|
114
|
+
choice.proceedSelector = '.' + this.class + ' .dirty-proceed';
|
115
|
+
choice.staySelector = '.' + this.class + ' .dirty-stay,.ui-widget-overlay';
|
116
|
+
|
117
|
+
// Support for Dirty Forms < 2.0
|
118
|
+
if (choice.isDF1) {
|
119
|
+
var close = function (decision) {
|
120
|
+
return function (e) {
|
121
|
+
if (e.type !== 'keydown' || (e.type === 'keydown' && (e.which == 27 || e.which == 13))) {
|
122
|
+
notice.remove();
|
123
|
+
if (modal_overlay) modal_overlay.fadeOut("fast");
|
124
|
+
decision(e);
|
125
|
+
return false;
|
126
|
+
}
|
127
|
+
};
|
128
|
+
};
|
129
|
+
// Trap the escape key and force a close. Cancel it so PNotify doesn't intercept it.
|
130
|
+
var decidingCancel = $.DirtyForms.decidingCancel;
|
131
|
+
$(document).keydown(close(decidingCancel));
|
132
|
+
$(choice.staySelector).click(close(decidingCancel));
|
133
|
+
$(choice.proceedSelector).click(close($.DirtyForms.decidingContinue));
|
134
|
+
}
|
135
|
+
},
|
136
|
+
close: function () {
|
137
|
+
notice.remove();
|
138
|
+
if (modal_overlay) modal_overlay.fadeOut("fast");
|
139
|
+
},
|
140
|
+
|
141
|
+
// Support for Dirty Forms < 2.0
|
142
|
+
fire: function (message, title) {
|
143
|
+
this.title = title;
|
144
|
+
this.open({ isDF1: true }, message, $.DirtyForms.ignoreClass);
|
145
|
+
},
|
146
|
+
|
147
|
+
// Support for Dirty Forms < 1.2
|
148
|
+
bind: function () {
|
149
|
+
},
|
150
|
+
stash: function () {
|
151
|
+
return false;
|
152
|
+
},
|
153
|
+
refire: function () {
|
154
|
+
return false;
|
155
|
+
},
|
156
|
+
selector: 'no-op'
|
157
|
+
};
|
158
|
+
|
159
|
+
})(jQuery, window, document);
|
@@ -0,0 +1,27 @@
|
|
1
|
+
/*!
|
2
|
+
Always dirty helper module (for jQuery Dirty Forms) | v | github.com/snikch/jquery.dirtyforms
|
3
|
+
(c) 2012-2016 Mal Curtis
|
4
|
+
License MIT
|
5
|
+
*/
|
6
|
+
|
7
|
+
// Example helper, the form is always considered dirty
|
8
|
+
|
9
|
+
(function($, window, document, undefined) {
|
10
|
+
// Can't use ECMAScript 5's strict mode because several apps
|
11
|
+
// including ASP.NET trace the stack via arguments.caller.callee
|
12
|
+
// and Firefox dies if you try to trace through "use strict" call chains.
|
13
|
+
// See jQuery issue (#13335)
|
14
|
+
// Support: Firefox 18+
|
15
|
+
//"use strict";
|
16
|
+
|
17
|
+
// Create a new object, with an isDirty method
|
18
|
+
var alwaysDirty = {
|
19
|
+
isDirty: function (node) {
|
20
|
+
// Perform dirty check on a given node (usually a form element)
|
21
|
+
return true;
|
22
|
+
}
|
23
|
+
};
|
24
|
+
// Push the new object onto the helpers array
|
25
|
+
$.DirtyForms.helpers.push(alwaysDirty);
|
26
|
+
|
27
|
+
})(jQuery, window, document);
|
@@ -0,0 +1,67 @@
|
|
1
|
+
/*!
|
2
|
+
CkEditor helper module (for jQuery Dirty Forms) | v | github.com/snikch/jquery.dirtyforms
|
3
|
+
(c) 2012-2016 Mal Curtis
|
4
|
+
License MIT
|
5
|
+
*/
|
6
|
+
|
7
|
+
(function($, window, document, undefined) {
|
8
|
+
// Can't use ECMAScript 5's strict mode because several apps
|
9
|
+
// including ASP.NET trace the stack via arguments.caller.callee
|
10
|
+
// and Firefox dies if you try to trace through "use strict" call chains.
|
11
|
+
// See jQuery issue (#13335)
|
12
|
+
// Support: Firefox 18+
|
13
|
+
//"use strict";
|
14
|
+
|
15
|
+
var ignoreSelector = '.cke_dialog_ui_button,.cke_tpl_list a';
|
16
|
+
|
17
|
+
var ckeditor = {
|
18
|
+
ignoreSelector: ignoreSelector,
|
19
|
+
isDirty: function ($form) {
|
20
|
+
var $editors = ckeditors($form),
|
21
|
+
isDirty = false;
|
22
|
+
if ($editors.length > 0) {
|
23
|
+
$.DirtyForms.dirtylog('Checking ' + $editors.length + ' ckeditors for dirtyness.');
|
24
|
+
$editors.each(function (editorIndex) {
|
25
|
+
if (this.checkDirty()) {
|
26
|
+
isDirty = true;
|
27
|
+
|
28
|
+
$.DirtyForms.dirtylog('CKEditor with index ' + editorIndex + ' was dirty, exiting...');
|
29
|
+
// Return false to break out of the .each() function
|
30
|
+
return false;
|
31
|
+
}
|
32
|
+
});
|
33
|
+
}
|
34
|
+
return isDirty;
|
35
|
+
},
|
36
|
+
setClean: function ($form) {
|
37
|
+
ckeditors($form).each(function () { this.resetDirty(); });
|
38
|
+
},
|
39
|
+
|
40
|
+
// Support for Dirty Forms < 2.0
|
41
|
+
ignoreAnchorSelector: ignoreSelector
|
42
|
+
};
|
43
|
+
var ckeditors = function (form) {
|
44
|
+
var $form = form.jquery ? form : $(form);
|
45
|
+
var editors = [];
|
46
|
+
if (!window.CKEDITOR || !window.CKEDITOR.instances) {
|
47
|
+
return $(editors);
|
48
|
+
}
|
49
|
+
try {
|
50
|
+
for (var key in window.CKEDITOR.instances) {
|
51
|
+
if (window.CKEDITOR.instances.hasOwnProperty(key)) {
|
52
|
+
var editor = window.CKEDITOR.instances[key];
|
53
|
+
if ($(editor.element.$).parents().index($form) != -1) {
|
54
|
+
$.DirtyForms.dirtylog('Adding CKEditor with key ' + key);
|
55
|
+
editors.push(editor);
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
catch (e) {
|
61
|
+
// Ignore, means there was no CKEDITOR variable
|
62
|
+
}
|
63
|
+
return $(editors);
|
64
|
+
};
|
65
|
+
$.DirtyForms.helpers.push(ckeditor);
|
66
|
+
|
67
|
+
})(jQuery, window, document);
|
@@ -0,0 +1,158 @@
|
|
1
|
+
/*!
|
2
|
+
TinyMCE helper module (for jQuery Dirty Forms) | v | github.com/snikch/jquery.dirtyforms
|
3
|
+
(c) 2013-2016 Mal Curtis
|
4
|
+
License MIT
|
5
|
+
*/
|
6
|
+
|
7
|
+
(function($, window, document, undefined) {
|
8
|
+
// Can't use ECMAScript 5's strict mode because several apps
|
9
|
+
// including ASP.NET trace the stack via arguments.caller.callee
|
10
|
+
// and Firefox dies if you try to trace through "use strict" call chains.
|
11
|
+
// See jQuery issue (#13335)
|
12
|
+
// Support: Firefox 18+
|
13
|
+
//"use strict";
|
14
|
+
|
15
|
+
var tinymceSelector = ':tinymce:not(.dirty-forms-temp)',
|
16
|
+
ignoreSelector = '.mceEditor a,.mceMenu a,[name^="mce_"]';
|
17
|
+
|
18
|
+
// Create a new object, with an isDirty method
|
19
|
+
var tinymce = {
|
20
|
+
// Dirty Forms properties and methods
|
21
|
+
ignoreSelector: ignoreSelector,
|
22
|
+
isDirty: function ($node) {
|
23
|
+
var isDirty = false;
|
24
|
+
if (hasTinyMCE($node)) {
|
25
|
+
// Search the current node and all descendant nodes that match the selector
|
26
|
+
$node.filter(tinymceSelector).add($node.find(tinymceSelector)).each(function () {
|
27
|
+
var $field = $(this);
|
28
|
+
|
29
|
+
$.DirtyForms.dirtylog('Checking node ' + $field.attr('id'));
|
30
|
+
if (typeof $field.data('df-tinymce-orig') === 'undefined') {
|
31
|
+
// For Dirty Forms < 2.0 and TinyMCE elements that were added via AJAX,
|
32
|
+
// we default to using TinyMCE's isDirty behavior (which is stateless).
|
33
|
+
if ($field.tinymce().isDirty()) {
|
34
|
+
isDirty = true;
|
35
|
+
$.DirtyForms.dirtylog('Node was totally dirty.');
|
36
|
+
// Return false to stop iterating.
|
37
|
+
return false;
|
38
|
+
}
|
39
|
+
} else {
|
40
|
+
// For Dirty Forms >= 2.0, we compare hash codes with the original content
|
41
|
+
var content = getTinyMceContent($field);
|
42
|
+
$.DirtyForms.dirtylog('TinyMCE content: ' + content);
|
43
|
+
var hash = getHashCode(content);
|
44
|
+
$.DirtyForms.dirtylog('TinyMCE hash: ' + hash);
|
45
|
+
var originalHash = $field.data('df-tinymce-orig');
|
46
|
+
$.DirtyForms.dirtylog('Original TinyMCE hash: ' + originalHash);
|
47
|
+
|
48
|
+
if (hash !== originalHash) {
|
49
|
+
isDirty = true;
|
50
|
+
$.DirtyForms.dirtylog('Node was totally dirty.');
|
51
|
+
// Return false to stop iterating.
|
52
|
+
return false;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
});
|
56
|
+
}
|
57
|
+
return isDirty;
|
58
|
+
},
|
59
|
+
setClean: function ($node) {
|
60
|
+
if (hasTinyMCE($node)) {
|
61
|
+
|
62
|
+
// Search the current node and all descendant nodes that match the selector
|
63
|
+
$node.filter(tinymceSelector).add($node.find(tinymceSelector)).each(function () {
|
64
|
+
var $field = $(this);
|
65
|
+
|
66
|
+
// Set TinyMCE clean
|
67
|
+
if ($field.tinymce().isDirty()) {
|
68
|
+
$.DirtyForms.dirtylog('Resetting isDirty on node ' + $field.attr('id'));
|
69
|
+
$field.tinymce().isNotDirty = 1; //Force not dirty state
|
70
|
+
}
|
71
|
+
|
72
|
+
// Forget the original value and reset to the current state
|
73
|
+
storeOriginalValue($field);
|
74
|
+
});
|
75
|
+
}
|
76
|
+
},
|
77
|
+
rescan: function ($node) {
|
78
|
+
if (hasTinyMCE($node)) {
|
79
|
+
$node.filter(tinymceSelector).add($node.find(tinymceSelector)).each(function () {
|
80
|
+
var $field = $(this);
|
81
|
+
|
82
|
+
if (typeof $field.data('df-tinymce-orig') !== 'undefined') {
|
83
|
+
storeOriginalValue($field);
|
84
|
+
}
|
85
|
+
});
|
86
|
+
}
|
87
|
+
},
|
88
|
+
|
89
|
+
// Patch for Dirty Forms < 2.0
|
90
|
+
ignoreAnchorSelector: ignoreSelector
|
91
|
+
};
|
92
|
+
|
93
|
+
// Push the new object onto the helpers array
|
94
|
+
$.DirtyForms.helpers.push(tinymce);
|
95
|
+
|
96
|
+
// Fix: tinymce throws an error if the selector doesn't match anything
|
97
|
+
// (such as when there are no textareas on the current page)
|
98
|
+
var hasTinyMCE = function ($node) {
|
99
|
+
try {
|
100
|
+
return $node.filter(tinymceSelector).length > 0 || $node.find(tinymceSelector).length > 0;
|
101
|
+
}
|
102
|
+
catch (e) {
|
103
|
+
return false;
|
104
|
+
}
|
105
|
+
};
|
106
|
+
|
107
|
+
var getTinyMceContent = function ($field) {
|
108
|
+
// Hack: TinyMCE puts an extra <br> tag at the end of a paragraph when it is edited, so ignore that case.
|
109
|
+
return $field.tinymce().getContent({ format: 'raw' }).replace(/<br><\/p>/mg, '</p>');
|
110
|
+
};
|
111
|
+
|
112
|
+
var storeOriginalValue = function ($field) {
|
113
|
+
var content = getTinyMceContent($field);
|
114
|
+
$.DirtyForms.dirtylog('Original TinyMCE content: ' + content);
|
115
|
+
|
116
|
+
var hash = getHashCode(content);
|
117
|
+
$.DirtyForms.dirtylog('Original TinyMCE hash: ' + hash);
|
118
|
+
|
119
|
+
$field.data('df-tinymce-orig', hash);
|
120
|
+
};
|
121
|
+
|
122
|
+
// When TinyMCE is found, store the original value as a hash so we can see if there are changes later.
|
123
|
+
var init = function ($node) {
|
124
|
+
if (hasTinyMCE($node)) {
|
125
|
+
$node.filter(tinymceSelector).add($node.find(tinymceSelector)).each(function () {
|
126
|
+
storeOriginalValue($(this));
|
127
|
+
});
|
128
|
+
}
|
129
|
+
};
|
130
|
+
|
131
|
+
$(document).bind('scan.dirtyforms', function (ev) {
|
132
|
+
// Hack: TinyMCE doesn't have a global init event. So, we create a new
|
133
|
+
// TinyMCE editor within an invisible div and respond to its init event.
|
134
|
+
// There doesn't seem to be a reasonable way
|
135
|
+
// to remove the control again, so we simply ignore it.
|
136
|
+
var $form = $(ev.target);
|
137
|
+
var $editor = $('<div style="display:none;" class="dirty-forms-temp"></div>');
|
138
|
+
$form.append($editor);
|
139
|
+
$editor.tinymce({
|
140
|
+
oninit: function () {
|
141
|
+
init($form);
|
142
|
+
}
|
143
|
+
});
|
144
|
+
});
|
145
|
+
|
146
|
+
// Simple way to hash a string: http://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript-jquery
|
147
|
+
var getHashCode = function (str) {
|
148
|
+
var hash = 0, i, chr, len;
|
149
|
+
if (str.length === 0) return hash;
|
150
|
+
for (i = 0, len = str.length; i < len; i++) {
|
151
|
+
chr = str.charCodeAt(i);
|
152
|
+
hash = ((hash << 5) - hash) + chr;
|
153
|
+
hash |= 0; // Convert to 32bit integer
|
154
|
+
}
|
155
|
+
return hash;
|
156
|
+
};
|
157
|
+
|
158
|
+
})(jQuery, window, document);
|
@@ -0,0 +1,596 @@
|
|
1
|
+
/*!
|
2
|
+
Dirty Forms jQuery Plugin | v | github.com/snikch/jquery.dirtyforms
|
3
|
+
(c) 2010-2016 Mal Curtis
|
4
|
+
License MIT
|
5
|
+
*/
|
6
|
+
|
7
|
+
(function($, window, document, undefined) {
|
8
|
+
// Can't use ECMAScript 5's strict mode because several apps
|
9
|
+
// including ASP.NET trace the stack via arguments.caller.callee
|
10
|
+
// and Firefox dies if you try to trace through "use strict" call chains.
|
11
|
+
// See jQuery issue (#13335)
|
12
|
+
// Support: Firefox 18+
|
13
|
+
//"use strict";
|
14
|
+
|
15
|
+
if (!$.fn.on) {
|
16
|
+
// Patch jQuery 1.4.2 - 1.7 with an on function (that uses delegate).
|
17
|
+
$.fn.on = function (events, selector, data, handler) {
|
18
|
+
return this.delegate(selector, events, data, handler);
|
19
|
+
};
|
20
|
+
}
|
21
|
+
|
22
|
+
$.fn.dirtyForms = function (method) {
|
23
|
+
// Method calling logic
|
24
|
+
if (methods[method]) {
|
25
|
+
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
26
|
+
} else if (typeof method === 'object' || !method) {
|
27
|
+
return methods.init.apply(this, arguments);
|
28
|
+
} else {
|
29
|
+
$.error('Method ' + method + ' does not exist on jQuery.dirtyForms');
|
30
|
+
}
|
31
|
+
};
|
32
|
+
|
33
|
+
// Public Element methods ( $('form').dirtyForms('methodName', args) )
|
34
|
+
var methods = {
|
35
|
+
init: function (options) {
|
36
|
+
var data = {};
|
37
|
+
|
38
|
+
if (!state.initialized) {
|
39
|
+
// Override any default options
|
40
|
+
$.extend(true, $.DirtyForms, options);
|
41
|
+
|
42
|
+
$(document).trigger('bind.dirtyforms', [events]);
|
43
|
+
events.bind(window, document, data);
|
44
|
+
|
45
|
+
state.initialized = true;
|
46
|
+
}
|
47
|
+
|
48
|
+
this.filter('form').not(':dirtylistening').each(function () {
|
49
|
+
var $form = $(this);
|
50
|
+
dirtylog('Adding form ' + $form.attr('id') + ' to forms to watch');
|
51
|
+
|
52
|
+
// Store original values of the fields
|
53
|
+
$form.find($.DirtyForms.fieldSelector).each(function () {
|
54
|
+
storeOriginalValue($(this));
|
55
|
+
});
|
56
|
+
|
57
|
+
$form.trigger('scan.dirtyforms');
|
58
|
+
events.bindForm($form, data);
|
59
|
+
});
|
60
|
+
return this;
|
61
|
+
},
|
62
|
+
// Returns true if any of the selected elements or their children are dirty
|
63
|
+
isDirty: function (excludeHelpers) {
|
64
|
+
var ignoreSelector = getIgnoreSelector(),
|
65
|
+
dirtyClass = $.DirtyForms.dirtyClass,
|
66
|
+
isDirty = false;
|
67
|
+
|
68
|
+
this.each(function (index) {
|
69
|
+
var $node = $(this),
|
70
|
+
ignored = isFieldIgnored($node, ignoreSelector);
|
71
|
+
|
72
|
+
if ($node.hasClass(dirtyClass) && !ignored) {
|
73
|
+
isDirty = true;
|
74
|
+
// Exit out of the .each() function
|
75
|
+
return false;
|
76
|
+
}
|
77
|
+
|
78
|
+
// Check any descendant nodes (if this is a container element)
|
79
|
+
$node.find('.' + dirtyClass).each(function () {
|
80
|
+
if (!isFieldIgnored($(this), ignoreSelector)) {
|
81
|
+
isDirty = true;
|
82
|
+
// Exit out of the .each() function
|
83
|
+
return false;
|
84
|
+
}
|
85
|
+
});
|
86
|
+
// Exit out of the .each() function
|
87
|
+
if (isDirty) return false;
|
88
|
+
|
89
|
+
if (!ignored && !excludeHelpers) {
|
90
|
+
// Test helpers for this node.
|
91
|
+
$.each($.DirtyForms.helpers, function (i, helper) {
|
92
|
+
if (helper.isDirty && helper.isDirty($node, index)) {
|
93
|
+
isDirty = true;
|
94
|
+
// Exit out of the .each() function
|
95
|
+
return false;
|
96
|
+
}
|
97
|
+
});
|
98
|
+
|
99
|
+
// Exit out of the .each() function
|
100
|
+
if (isDirty) return false;
|
101
|
+
}
|
102
|
+
});
|
103
|
+
|
104
|
+
return isDirty;
|
105
|
+
},
|
106
|
+
// Marks the element(s) and any helpers within the element not dirty.
|
107
|
+
// If all of the fields in a form are marked not dirty, the form itself will be marked not dirty even
|
108
|
+
// if it is not included in the selector. Also resets original values to the current state -
|
109
|
+
// essentially "forgetting" the node or its descendants are dirty.
|
110
|
+
setClean: function (excludeIgnored, excludeHelpers) {
|
111
|
+
dirtylog('setClean called');
|
112
|
+
|
113
|
+
var doSetClean = function () {
|
114
|
+
var $field = $(this);
|
115
|
+
|
116
|
+
// Reset by storing the original value again
|
117
|
+
storeOriginalValue($field);
|
118
|
+
|
119
|
+
// Remove the dirty class
|
120
|
+
setDirtyStatus($field, false);
|
121
|
+
};
|
122
|
+
|
123
|
+
elementsInRange(this, $.DirtyForms.fieldSelector, excludeIgnored)
|
124
|
+
.each(doSetClean)
|
125
|
+
.parents('form').trigger('setclean.dirtyforms', [excludeIgnored]);
|
126
|
+
|
127
|
+
if (excludeHelpers) return this;
|
128
|
+
return fireHelperMethod(this, 'setClean', excludeIgnored, getIgnoreSelector());
|
129
|
+
},
|
130
|
+
// Scans the selected elements and descendants for any new fields and stores their original values.
|
131
|
+
// Ignores any original values that had been set previously. Also resets the dirty status of all fields
|
132
|
+
// whose ignore status has changed since the last scan.
|
133
|
+
rescan: function (excludeIgnored, excludeHelpers) {
|
134
|
+
dirtylog('rescan called');
|
135
|
+
|
136
|
+
var doRescan = function () {
|
137
|
+
var $field = $(this);
|
138
|
+
|
139
|
+
// Skip previously added fields
|
140
|
+
if (!hasOriginalValue($field)) {
|
141
|
+
// Store the original value
|
142
|
+
storeOriginalValue($field);
|
143
|
+
}
|
144
|
+
|
145
|
+
// Set the dirty status
|
146
|
+
setDirtyStatus($field, isFieldDirty($field));
|
147
|
+
};
|
148
|
+
|
149
|
+
elementsInRange(this, $.DirtyForms.fieldSelector, excludeIgnored)
|
150
|
+
.each(doRescan)
|
151
|
+
.parents('form').trigger('rescan.dirtyforms', [excludeIgnored]);
|
152
|
+
|
153
|
+
if (excludeHelpers) return this;
|
154
|
+
return fireHelperMethod(this, 'rescan', excludeIgnored, getIgnoreSelector());
|
155
|
+
}
|
156
|
+
};
|
157
|
+
|
158
|
+
// Custom selectors $('form:dirty')
|
159
|
+
$.extend($.expr[":"], {
|
160
|
+
dirty: function (element) {
|
161
|
+
var $element = $(element);
|
162
|
+
return $element.hasClass($.DirtyForms.dirtyClass) && !$element.is(':dirtyignored');
|
163
|
+
},
|
164
|
+
dirtylistening: function (element) {
|
165
|
+
return $(element).hasClass($.DirtyForms.listeningClass);
|
166
|
+
},
|
167
|
+
dirtyignored: function (element) {
|
168
|
+
return isFieldIgnored($(element), false);
|
169
|
+
}
|
170
|
+
});
|
171
|
+
|
172
|
+
// Public General Plugin properties and methods $.DirtyForms
|
173
|
+
$.DirtyForms = {
|
174
|
+
message: "You've made changes on this page which aren't saved. If you leave you will lose these changes.",
|
175
|
+
dirtyClass: 'dirty',
|
176
|
+
listeningClass: 'dirtylisten',
|
177
|
+
ignoreClass: 'dirtyignore',
|
178
|
+
ignoreSelector: '',
|
179
|
+
// exclude all HTML 4 except checkbox, option, text and password, but include HTML 5 except search
|
180
|
+
fieldSelector: "input:not([type='button'],[type='image'],[type='submit']," +
|
181
|
+
"[type='reset'],[type='file'],[type='search']),select,textarea",
|
182
|
+
/*<log>*/
|
183
|
+
debug: false,
|
184
|
+
dirtylog: function (msg) {
|
185
|
+
dirtylog(msg);
|
186
|
+
},
|
187
|
+
/*</log>*/
|
188
|
+
helpers: [],
|
189
|
+
dialog: false
|
190
|
+
};
|
191
|
+
|
192
|
+
// Private State Management
|
193
|
+
var state = {
|
194
|
+
initialized: false,
|
195
|
+
formStash: false,
|
196
|
+
dialogStash: false,
|
197
|
+
deciding: false,
|
198
|
+
decidingEvent: false
|
199
|
+
};
|
200
|
+
|
201
|
+
// Dialog Decision Management
|
202
|
+
var choice;
|
203
|
+
|
204
|
+
var bindKeys = function (ev) {
|
205
|
+
if (ev.data.bindEscKey && ev.which == 27 || ev.data.bindEnterKey && ev.which == 13) {
|
206
|
+
return doCommit(ev, false);
|
207
|
+
}
|
208
|
+
};
|
209
|
+
|
210
|
+
var bindDialog = function (choice) {
|
211
|
+
var staySelector = choice.staySelector,
|
212
|
+
proceedSelector = choice.proceedSelector;
|
213
|
+
|
214
|
+
if (staySelector !== '') {
|
215
|
+
$(staySelector).unbind('click', doCommit)
|
216
|
+
.click(doCommit);
|
217
|
+
}
|
218
|
+
if (proceedSelector !== '') {
|
219
|
+
$(proceedSelector).unbind('click', doProceed)
|
220
|
+
.click(doProceed);
|
221
|
+
}
|
222
|
+
if (choice.bindEscKey || choice.bindEnterKey) {
|
223
|
+
$(document).unbind('keydown', bindKeys)
|
224
|
+
.keydown(choice, bindKeys);
|
225
|
+
}
|
226
|
+
};
|
227
|
+
|
228
|
+
var callDialogClose = function (proceeding, unstashing) {
|
229
|
+
if ($.isFunction($.DirtyForms.dialog.close)) {
|
230
|
+
dirtylog('Calling dialog close');
|
231
|
+
$.DirtyForms.dialog.close(proceeding, unstashing);
|
232
|
+
}
|
233
|
+
};
|
234
|
+
|
235
|
+
var doProceed = function (ev) {
|
236
|
+
return doCommit(ev, true);
|
237
|
+
};
|
238
|
+
|
239
|
+
var doCommit = function (ev, proceeding) {
|
240
|
+
if (!state.deciding) return;
|
241
|
+
ev.preventDefault();
|
242
|
+
|
243
|
+
if (proceeding === true) {
|
244
|
+
var refireEvent = state.decidingEvent;
|
245
|
+
$(document).trigger('proceed.dirtyforms', [refireEvent]);
|
246
|
+
events.clearUnload(); // fix for chrome/safari
|
247
|
+
callDialogClose(proceeding, false);
|
248
|
+
refire(refireEvent);
|
249
|
+
} else {
|
250
|
+
$(document).trigger('stay.dirtyforms');
|
251
|
+
var isUnstashing = $.DirtyForms.dialog !== false && state.dialogStash !== false && $.isFunction($.DirtyForms.dialog.unstash);
|
252
|
+
callDialogClose(proceeding, isUnstashing);
|
253
|
+
if (isUnstashing) {
|
254
|
+
dirtylog('Refiring the dialog with stashed content');
|
255
|
+
$.DirtyForms.dialog.unstash(state.dialogStash, ev);
|
256
|
+
}
|
257
|
+
$(document).trigger('afterstay.dirtyforms');
|
258
|
+
}
|
259
|
+
|
260
|
+
state.deciding = state.decidingEvent = state.dialogStash = state.formStash = false;
|
261
|
+
return false;
|
262
|
+
};
|
263
|
+
|
264
|
+
// Event management
|
265
|
+
var events = {
|
266
|
+
bind: function (window, document, data) {
|
267
|
+
$(window).bind('beforeunload', data, events.onBeforeUnload);
|
268
|
+
$(document).on('click', 'a:not([target="_blank"])', data, events.onAnchorClick)
|
269
|
+
.on('submit', 'form', data, events.onSubmit);
|
270
|
+
},
|
271
|
+
bindForm: function ($form, data) {
|
272
|
+
var dirtyForms = $.DirtyForms;
|
273
|
+
|
274
|
+
// Test whether we are dealing with IE < 10
|
275
|
+
var isIE8_9 = ('onpropertychange' in document.createElement('input'));
|
276
|
+
var inputEvents = 'change input' + (isIE8_9 ? ' keyup selectionchange cut paste' : '');
|
277
|
+
$form.addClass(dirtyForms.listeningClass)
|
278
|
+
.on('focus keydown', dirtyForms.fieldSelector, data, events.onFocus)
|
279
|
+
.on(inputEvents, dirtyForms.fieldSelector, data, events.onFieldChange)
|
280
|
+
.bind('reset', data, events.onReset);
|
281
|
+
},
|
282
|
+
// For any fields added after the form was initialized, store the value when focused.
|
283
|
+
onFocus: function (ev) {
|
284
|
+
var $field = $(ev.target);
|
285
|
+
if (!hasOriginalValue($field)) {
|
286
|
+
storeOriginalValue($field);
|
287
|
+
}
|
288
|
+
},
|
289
|
+
onFieldChange: function (ev) {
|
290
|
+
var $field = $(ev.target);
|
291
|
+
if (ev.type !== 'change') {
|
292
|
+
delay(function () { setFieldStatus($field); }, 100);
|
293
|
+
} else {
|
294
|
+
setFieldStatus($field);
|
295
|
+
}
|
296
|
+
},
|
297
|
+
onReset: function (ev) {
|
298
|
+
var $form = $(ev.target).closest('form');
|
299
|
+
// Need a delay here because reset is called before the state of the form is reset.
|
300
|
+
setTimeout(function () { $form.dirtyForms('setClean'); }, 100);
|
301
|
+
},
|
302
|
+
onAnchorClick: function (ev) {
|
303
|
+
bindFn(ev);
|
304
|
+
},
|
305
|
+
onSubmit: function (ev) {
|
306
|
+
bindFn(ev);
|
307
|
+
},
|
308
|
+
onBeforeUnload: function (ev) {
|
309
|
+
var result = bindFn(ev);
|
310
|
+
|
311
|
+
if (result && state.doubleunloadfix !== true) {
|
312
|
+
dirtylog('Before unload will be called, resetting');
|
313
|
+
state.deciding = false;
|
314
|
+
}
|
315
|
+
|
316
|
+
state.doubleunloadfix = true;
|
317
|
+
setTimeout(function () { state.doubleunloadfix = false; }, 200);
|
318
|
+
|
319
|
+
// Only return the result if it is a string, otherwise don't return anything.
|
320
|
+
if (typeof result === 'string') {
|
321
|
+
// For IE and Firefox prior to version 4, set the returnValue.
|
322
|
+
ev.returnValue = result;
|
323
|
+
return result;
|
324
|
+
}
|
325
|
+
},
|
326
|
+
onRefireClick: function (ev) {
|
327
|
+
var event = new $.Event('click');
|
328
|
+
$(ev.target).trigger(event);
|
329
|
+
if (!event.isDefaultPrevented()) {
|
330
|
+
events.onRefireAnchorClick(ev);
|
331
|
+
}
|
332
|
+
},
|
333
|
+
onRefireAnchorClick: function (ev) {
|
334
|
+
var href = $(ev.target).closest('a[href]').attr('href');
|
335
|
+
if (href !== undefined) {
|
336
|
+
dirtylog('Sending location to ' + href);
|
337
|
+
window.location.href = href;
|
338
|
+
}
|
339
|
+
},
|
340
|
+
clearUnload: function () {
|
341
|
+
// I'd like to just be able to unbind this but there seems
|
342
|
+
// to be a bug in jQuery which doesn't unbind onbeforeunload
|
343
|
+
dirtylog('Clearing the beforeunload event');
|
344
|
+
$(window).unbind('beforeunload', events.onBeforeUnload);
|
345
|
+
window.onbeforeunload = null;
|
346
|
+
$(document).trigger('beforeunload.dirtyforms');
|
347
|
+
}
|
348
|
+
};
|
349
|
+
|
350
|
+
var elementsInRange = function ($this, selector, excludeIgnored) {
|
351
|
+
var $elements = $this.filter(selector).add($this.find(selector));
|
352
|
+
if (excludeIgnored) {
|
353
|
+
$elements = $elements.not(':dirtyignored');
|
354
|
+
}
|
355
|
+
return $elements;
|
356
|
+
};
|
357
|
+
|
358
|
+
var fireHelperMethod = function ($this, method, excludeIgnored, ignoreSelector) {
|
359
|
+
return $this.each(function (index) {
|
360
|
+
var $node = $(this);
|
361
|
+
|
362
|
+
if (!excludeIgnored || !isFieldIgnored($node, ignoreSelector)) {
|
363
|
+
$.each($.DirtyForms.helpers, function (i, helper) {
|
364
|
+
if (helper[method]) { helper[method]($node, index, excludeIgnored); }
|
365
|
+
});
|
366
|
+
}
|
367
|
+
});
|
368
|
+
};
|
369
|
+
|
370
|
+
var getFieldValue = function ($field) {
|
371
|
+
var value;
|
372
|
+
if ($field.is('select')) {
|
373
|
+
value = '';
|
374
|
+
$field.find('option').each(function () {
|
375
|
+
var $option = $(this);
|
376
|
+
if ($option.is(':selected')) {
|
377
|
+
if (value.length > 0) { value += ','; }
|
378
|
+
value += $option.val();
|
379
|
+
}
|
380
|
+
});
|
381
|
+
} else if ($field.is(":checkbox,:radio")) {
|
382
|
+
value = $field.is(':checked');
|
383
|
+
} else {
|
384
|
+
value = $field.val();
|
385
|
+
}
|
386
|
+
|
387
|
+
return value;
|
388
|
+
};
|
389
|
+
|
390
|
+
var storeOriginalValue = function ($field) {
|
391
|
+
dirtylog('Storing original value for ' + $field.attr('name'));
|
392
|
+
$field.data('df-orig', getFieldValue($field));
|
393
|
+
var isEmpty = ($field.data('df-orig') === undefined);
|
394
|
+
$field.data('df-empty', isEmpty);
|
395
|
+
};
|
396
|
+
|
397
|
+
var hasOriginalValue = function ($field) {
|
398
|
+
return ($field.data('df-orig') !== undefined || $field.data('df-empty') === true);
|
399
|
+
};
|
400
|
+
|
401
|
+
var getIgnoreSelector = function () {
|
402
|
+
var dirtyForms = $.DirtyForms,
|
403
|
+
result = dirtyForms.ignoreSelector;
|
404
|
+
$.each(dirtyForms.helpers, function (key, obj) {
|
405
|
+
if ('ignoreSelector' in obj) {
|
406
|
+
if (result.length > 0) { result += ','; }
|
407
|
+
result += obj.ignoreSelector;
|
408
|
+
}
|
409
|
+
});
|
410
|
+
return result;
|
411
|
+
};
|
412
|
+
|
413
|
+
var isFieldIgnored = function ($field, ignoreSelector) {
|
414
|
+
if (!ignoreSelector) {
|
415
|
+
ignoreSelector = getIgnoreSelector();
|
416
|
+
}
|
417
|
+
return $field.is(ignoreSelector) || $field.closest('.' + $.DirtyForms.ignoreClass).length > 0;
|
418
|
+
};
|
419
|
+
|
420
|
+
var isFieldDirty = function ($field, ignoreSelector) {
|
421
|
+
if (!hasOriginalValue($field) || isFieldIgnored($field, ignoreSelector)) return false;
|
422
|
+
return (getFieldValue($field) != $field.data('df-orig'));
|
423
|
+
};
|
424
|
+
|
425
|
+
var setFieldStatus = function ($field, ignoreSelector) {
|
426
|
+
if (isFieldIgnored($field, ignoreSelector)) return;
|
427
|
+
|
428
|
+
// Option groups are a special case because they change more than the current element.
|
429
|
+
if ($field.is(':radio[name]')) {
|
430
|
+
var name = $field.attr('name'),
|
431
|
+
$form = $field.parents('form');
|
432
|
+
|
433
|
+
$form.find(":radio[name='" + name + "']").each(function () {
|
434
|
+
var $radio = $(this);
|
435
|
+
setDirtyStatus($radio, isFieldDirty($radio, ignoreSelector));
|
436
|
+
});
|
437
|
+
} else {
|
438
|
+
setDirtyStatus($field, isFieldDirty($field, ignoreSelector));
|
439
|
+
}
|
440
|
+
};
|
441
|
+
|
442
|
+
var setDirtyStatus = function ($field, isDirty) {
|
443
|
+
dirtylog('Setting dirty status to ' + isDirty + ' on field ' + $field.attr('id'));
|
444
|
+
var dirtyClass = $.DirtyForms.dirtyClass,
|
445
|
+
$form = $field.parents('form');
|
446
|
+
|
447
|
+
// Mark the field dirty/clean
|
448
|
+
$field.toggleClass(dirtyClass, isDirty);
|
449
|
+
var changed = (isDirty !== ($form.hasClass(dirtyClass) && $form.find(':dirty').length === 0));
|
450
|
+
|
451
|
+
if (changed) {
|
452
|
+
dirtylog('Setting dirty status to ' + isDirty + ' on form ' + $form.attr('id'));
|
453
|
+
$form.toggleClass(dirtyClass, isDirty);
|
454
|
+
|
455
|
+
if (isDirty) $form.trigger('dirty.dirtyforms');
|
456
|
+
if (!isDirty) $form.trigger('clean.dirtyforms');
|
457
|
+
}
|
458
|
+
};
|
459
|
+
|
460
|
+
// A delay to keep the key events from slowing down when changing the dirty status on the fly.
|
461
|
+
var delay = (function () {
|
462
|
+
var timer = 0;
|
463
|
+
return function (callback, ms) {
|
464
|
+
clearTimeout(timer);
|
465
|
+
timer = setTimeout(callback, ms);
|
466
|
+
};
|
467
|
+
})();
|
468
|
+
|
469
|
+
var bindFn = function (ev) {
|
470
|
+
var $element = $(ev.target),
|
471
|
+
eventType = ev.type,
|
472
|
+
dirtyForms = $.DirtyForms;
|
473
|
+
dirtylog('Entering: Leaving Event fired, type: ' + eventType + ', element: ' + ev.target + ', class: ' + $element.attr('class') + ' and id: ' + ev.target.id);
|
474
|
+
|
475
|
+
// Important: Do this check before calling events.clearUnload()
|
476
|
+
if (ev.isDefaultPrevented()) {
|
477
|
+
dirtylog('Leaving: Event has been stopped elsewhere');
|
478
|
+
return false;
|
479
|
+
}
|
480
|
+
|
481
|
+
if (eventType == 'beforeunload' && state.doubleunloadfix) {
|
482
|
+
dirtylog('Skip this unload, Firefox bug triggers the unload event multiple times');
|
483
|
+
state.doubleunloadfix = false;
|
484
|
+
return false;
|
485
|
+
}
|
486
|
+
|
487
|
+
if ($element.is(':dirtyignored')) {
|
488
|
+
dirtylog('Leaving: Element has ignore class or a descendant of an ignored element');
|
489
|
+
events.clearUnload();
|
490
|
+
return false;
|
491
|
+
}
|
492
|
+
|
493
|
+
if (state.deciding) {
|
494
|
+
dirtylog('Leaving: Already in the deciding process');
|
495
|
+
return false;
|
496
|
+
}
|
497
|
+
|
498
|
+
if (!$('form:dirtylistening').dirtyForms('isDirty')) {
|
499
|
+
dirtylog('Leaving: Not dirty');
|
500
|
+
events.clearUnload();
|
501
|
+
return false;
|
502
|
+
}
|
503
|
+
|
504
|
+
if (eventType == 'submit' && $element.dirtyForms('isDirty')) {
|
505
|
+
dirtylog('Leaving: Form submitted is a dirty form');
|
506
|
+
events.clearUnload();
|
507
|
+
return true;
|
508
|
+
}
|
509
|
+
|
510
|
+
// Callback for page access in current state
|
511
|
+
$(document).trigger('defer.dirtyforms');
|
512
|
+
|
513
|
+
if (eventType == 'beforeunload') {
|
514
|
+
dirtylog('Returning to beforeunload browser handler with: ' + dirtyForms.message);
|
515
|
+
return dirtyForms.message;
|
516
|
+
}
|
517
|
+
if (!dirtyForms.dialog) return;
|
518
|
+
|
519
|
+
// Using the GUI dialog...
|
520
|
+
ev.preventDefault();
|
521
|
+
ev.stopImmediatePropagation();
|
522
|
+
|
523
|
+
dirtylog('Setting deciding active');
|
524
|
+
state.deciding = true;
|
525
|
+
state.decidingEvent = ev;
|
526
|
+
|
527
|
+
// Stash the dialog (with a form). This is done so it can be shown again via unstash().
|
528
|
+
if ($.isFunction(dirtyForms.dialog.stash)) {
|
529
|
+
dirtylog('Stashing dialog content');
|
530
|
+
state.dialogStash = dirtyForms.dialog.stash();
|
531
|
+
dirtylog('Dialog Stash: ' + state.dialogStash);
|
532
|
+
}
|
533
|
+
|
534
|
+
// Stash the form from the dialog. This is done so we can fire events on it if the user makes a proceed choice.
|
535
|
+
var stashSelector = dirtyForms.dialog.stashSelector;
|
536
|
+
if (typeof stashSelector === 'string' && $element.is('form') && $element.parents(stashSelector).length > 0) {
|
537
|
+
dirtylog('Stashing form');
|
538
|
+
state.formStash = $element.clone(true).hide();
|
539
|
+
} else {
|
540
|
+
state.formStash = false;
|
541
|
+
}
|
542
|
+
|
543
|
+
dirtylog('Deferring to the dialog');
|
544
|
+
|
545
|
+
// Create a new choice object
|
546
|
+
choice = {
|
547
|
+
proceed: false,
|
548
|
+
commit: function (ev) {
|
549
|
+
return doCommit(ev, choice.proceed);
|
550
|
+
},
|
551
|
+
bindEscKey: true,
|
552
|
+
bindEnterKey: false,
|
553
|
+
proceedSelector: '',
|
554
|
+
staySelector: ''
|
555
|
+
};
|
556
|
+
|
557
|
+
dirtyForms.dialog.open(choice, dirtyForms.message, dirtyForms.ignoreClass);
|
558
|
+
bindDialog(choice);
|
559
|
+
};
|
560
|
+
|
561
|
+
var refire = function (ev) {
|
562
|
+
if (ev.type === 'click') {
|
563
|
+
dirtylog("Refiring click event");
|
564
|
+
events.onRefireClick(ev);
|
565
|
+
} else {
|
566
|
+
dirtylog("Refiring " + ev.type + " event on " + ev.target);
|
567
|
+
var target;
|
568
|
+
if (state.formStash) {
|
569
|
+
dirtylog('Appending stashed form to body');
|
570
|
+
target = state.formStash;
|
571
|
+
$('body').append(target);
|
572
|
+
}
|
573
|
+
else {
|
574
|
+
target = $(ev.target).closest('form');
|
575
|
+
}
|
576
|
+
target.trigger(ev.type);
|
577
|
+
}
|
578
|
+
};
|
579
|
+
|
580
|
+
/*<log>*/
|
581
|
+
var dirtylog = function (msg) {
|
582
|
+
if (!$.DirtyForms.debug) return;
|
583
|
+
var hasFirebug = 'console' in window && 'firebug' in window.console,
|
584
|
+
hasConsoleLog = 'console' in window && 'log' in window.console;
|
585
|
+
msg = '[DirtyForms] ' + msg;
|
586
|
+
if (hasFirebug) {
|
587
|
+
console.log(msg);
|
588
|
+
} else if (hasConsoleLog) {
|
589
|
+
window.console.log(msg);
|
590
|
+
} else {
|
591
|
+
alert(msg);
|
592
|
+
}
|
593
|
+
};
|
594
|
+
/*</log>*/
|
595
|
+
|
596
|
+
})(jQuery, window, document);
|