fomantic-ui-sass 2.7.4 → 2.7.5

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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/app/assets/javascripts/semantic-ui/accordion.js +2 -2
  4. data/app/assets/javascripts/semantic-ui/api.js +7 -3
  5. data/app/assets/javascripts/semantic-ui/calendar.js +45 -17
  6. data/app/assets/javascripts/semantic-ui/checkbox.js +2 -2
  7. data/app/assets/javascripts/semantic-ui/dimmer.js +2 -2
  8. data/app/assets/javascripts/semantic-ui/dropdown.js +13 -3
  9. data/app/assets/javascripts/semantic-ui/embed.js +2 -2
  10. data/app/assets/javascripts/semantic-ui/form.js +364 -152
  11. data/app/assets/javascripts/semantic-ui/modal.js +13 -7
  12. data/app/assets/javascripts/semantic-ui/nag.js +2 -2
  13. data/app/assets/javascripts/semantic-ui/popup.js +8 -4
  14. data/app/assets/javascripts/semantic-ui/progress.js +31 -26
  15. data/app/assets/javascripts/semantic-ui/rating.js +2 -2
  16. data/app/assets/javascripts/semantic-ui/search.js +3 -3
  17. data/app/assets/javascripts/semantic-ui/shape.js +2 -2
  18. data/app/assets/javascripts/semantic-ui/sidebar.js +2 -2
  19. data/app/assets/javascripts/semantic-ui/site.js +2 -2
  20. data/app/assets/javascripts/semantic-ui/slider.js +2 -2
  21. data/app/assets/javascripts/semantic-ui/state.js +2 -2
  22. data/app/assets/javascripts/semantic-ui/sticky.js +2 -2
  23. data/app/assets/javascripts/semantic-ui/tab.js +2 -2
  24. data/app/assets/javascripts/semantic-ui/toast.js +2 -2
  25. data/app/assets/javascripts/semantic-ui/transition.js +5 -2
  26. data/app/assets/javascripts/semantic-ui/visibility.js +2 -2
  27. data/app/assets/stylesheets/semantic-ui/collections/_breadcrumb.scss +20 -3
  28. data/app/assets/stylesheets/semantic-ui/collections/_form.scss +36 -5
  29. data/app/assets/stylesheets/semantic-ui/collections/_grid.scss +24 -18
  30. data/app/assets/stylesheets/semantic-ui/collections/_menu.scss +24 -14
  31. data/app/assets/stylesheets/semantic-ui/collections/_message.scss +8 -2
  32. data/app/assets/stylesheets/semantic-ui/collections/_table.scss +196 -101
  33. data/app/assets/stylesheets/semantic-ui/elements/_button.scss +13 -3
  34. data/app/assets/stylesheets/semantic-ui/elements/_container.scss +11 -5
  35. data/app/assets/stylesheets/semantic-ui/elements/_divider.scss +38 -3
  36. data/app/assets/stylesheets/semantic-ui/elements/_flag.scss +995 -2
  37. data/app/assets/stylesheets/semantic-ui/elements/_header.scss +8 -2
  38. data/app/assets/stylesheets/semantic-ui/elements/_icon.scss +11808 -774
  39. data/app/assets/stylesheets/semantic-ui/elements/_image.scss +8 -2
  40. data/app/assets/stylesheets/semantic-ui/elements/_input.scss +15 -2
  41. data/app/assets/stylesheets/semantic-ui/elements/_label.scss +29 -2
  42. data/app/assets/stylesheets/semantic-ui/elements/_list.scss +10 -2
  43. data/app/assets/stylesheets/semantic-ui/elements/_loader.scss +8 -2
  44. data/app/assets/stylesheets/semantic-ui/elements/_placeholder.scss +8 -2
  45. data/app/assets/stylesheets/semantic-ui/elements/_rail.scss +8 -2
  46. data/app/assets/stylesheets/semantic-ui/elements/_reveal.scss +8 -2
  47. data/app/assets/stylesheets/semantic-ui/elements/_segment.scss +21 -2
  48. data/app/assets/stylesheets/semantic-ui/elements/_step.scss +21 -4
  49. data/app/assets/stylesheets/semantic-ui/elements/_text.scss +7 -1
  50. data/app/assets/stylesheets/semantic-ui/globals/_reset.scss +349 -2
  51. data/app/assets/stylesheets/semantic-ui/globals/_site.scss +8 -2
  52. data/app/assets/stylesheets/semantic-ui/modules/_accordion.scss +30 -2
  53. data/app/assets/stylesheets/semantic-ui/modules/_calendar.scss +8 -2
  54. data/app/assets/stylesheets/semantic-ui/modules/_checkbox.scss +36 -2
  55. data/app/assets/stylesheets/semantic-ui/modules/_dimmer.scss +9 -3
  56. data/app/assets/stylesheets/semantic-ui/modules/_dropdown.scss +147 -71
  57. data/app/assets/stylesheets/semantic-ui/modules/_embed.scss +8 -2
  58. data/app/assets/stylesheets/semantic-ui/modules/_modal.scss +22 -18
  59. data/app/assets/stylesheets/semantic-ui/modules/_nag.scss +8 -2
  60. data/app/assets/stylesheets/semantic-ui/modules/_popup.scss +9 -3
  61. data/app/assets/stylesheets/semantic-ui/modules/_progress.scss +8 -2
  62. data/app/assets/stylesheets/semantic-ui/modules/_rating.scss +11 -10
  63. data/app/assets/stylesheets/semantic-ui/modules/_search.scss +9 -3
  64. data/app/assets/stylesheets/semantic-ui/modules/_shape.scss +8 -2
  65. data/app/assets/stylesheets/semantic-ui/modules/_sidebar.scss +8 -2
  66. data/app/assets/stylesheets/semantic-ui/modules/_slider.scss +6 -0
  67. data/app/assets/stylesheets/semantic-ui/modules/_sticky.scss +8 -2
  68. data/app/assets/stylesheets/semantic-ui/modules/_tab.scss +8 -2
  69. data/app/assets/stylesheets/semantic-ui/modules/_toast.scss +2 -2
  70. data/app/assets/stylesheets/semantic-ui/modules/_transition.scss +1201 -2
  71. data/app/assets/stylesheets/semantic-ui/views/_ad.scss +9 -3
  72. data/app/assets/stylesheets/semantic-ui/views/_card.scss +24 -5
  73. data/app/assets/stylesheets/semantic-ui/views/_comment.scss +8 -2
  74. data/app/assets/stylesheets/semantic-ui/views/_feed.scss +8 -2
  75. data/app/assets/stylesheets/semantic-ui/views/_item.scss +11 -5
  76. data/app/assets/stylesheets/semantic-ui/views/_statistic.scss +9 -3
  77. data/lib/fomantic/ui/sass/version.rb +2 -2
  78. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e921c327518557405aa86a5ad6e2060e154d583fe562cd4407cab946c8149603
4
- data.tar.gz: cde98b84b3b18b07d6b4395ee9c9bd948edd7bd283eec7623026cba72d7dc0c3
3
+ metadata.gz: 8c6369c7f8f43fbecef5574b565038205d8851cd957391e36bf2e7cac1dc3cec
4
+ data.tar.gz: edd7cd8730578df73f8f6cbb11648df79f03b68f4f21d199f611fff719ccdbf8
5
5
  SHA512:
6
- metadata.gz: 0d604afa6884f11b7139ea18e49a83bffef9f1f49822ab79a08ac68e354e9cd7dd48ade74c5a26ed75ffba9b62c6bdb52bf94dd87196bf1c5edbf4e078d8d050
7
- data.tar.gz: b532c13391486c3038a1d1a2cbcb450ae569587a463dafcdbeaed0f4fe9d4e5cfc03c5f31f74038563eaa2b512258ee05e5b1d936e04f703d93c12964557263b
6
+ metadata.gz: fd22a9899b8e32d3cd4b53dcbc53e4e9c7e13a341b8be1f56b88f5eed6b326a5737ae847094ab41e8a404b23381039f83b9c6d834045f96f64ac9c86e22f1d99
7
+ data.tar.gz: f44dee604b3ffa145e8884cdd7cc84971c0f0f3a201322495aa7e007c4a14ace0f1cc5e391350ce5aa8a329bd320a96eda0f4e7ffa07dbb632194b7a20952082
@@ -1,3 +1,11 @@
1
+ ## 2.7.4
2
+
3
+ Update Fomantic UI to [2.7.4](https://github.com/fomantic/Fomantic-UI/releases/tag/2.7.4)
4
+
5
+ ## 2.7.3
6
+
7
+ Update Fomantic UI to [2.7.3](https://github.com/fomantic/Fomantic-UI/releases/tag/2.7.3)
8
+
1
9
  ## 2.7.2
2
10
 
3
11
  Update Fomantic UI to [2.7.2](https://github.com/fomantic/Fomantic-UI/releases/tag/2.7.2)
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * # Semantic UI - Accordion
3
- * http://github.com/semantic-org/semantic-ui/
2
+ * # Fomantic-UI - Accordion
3
+ * http://github.com/fomantic/Fomantic-UI/
4
4
  *
5
5
  *
6
6
  * Released under the MIT license
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * # Semantic UI - API
3
- * http://github.com/semantic-org/semantic-ui/
2
+ * # Fomantic-UI - API
3
+ * http://github.com/fomantic/Fomantic-UI/
4
4
  *
5
5
  *
6
6
  * Released under the MIT license
@@ -336,6 +336,10 @@ $.api = $.fn.api = function(parameters) {
336
336
  return (module.cancelled || false);
337
337
  },
338
338
  succesful: function() {
339
+ module.verbose('This behavior will be deleted due to typo. Use "was successful" instead.');
340
+ return module.was.successful();
341
+ },
342
+ successful: function() {
339
343
  return (module.request && module.request.state() == 'resolved');
340
344
  },
341
345
  failure: function() {
@@ -548,7 +552,7 @@ $.api = $.fn.api = function(parameters) {
548
552
  response
549
553
  ;
550
554
  // have to guess callback parameters based on request success
551
- if( module.was.succesful() ) {
555
+ if( module.was.successful() ) {
552
556
  response = firstParameter;
553
557
  xhr = secondParameter;
554
558
  }
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * # Semantic UI - Calendar
3
- * http://github.com/semantic-org/semantic-ui/
2
+ * # Fomantic-UI - Calendar
3
+ * http://github.com/fomantic/Fomantic-UI/
4
4
  *
5
5
  *
6
6
  * Released under the MIT license
@@ -336,11 +336,20 @@ $.fn.calendar = function(parameters) {
336
336
  var adjacent = isDay && cellDate.getMonth() !== ((month + 12) % 12);
337
337
  var disabled = (!settings.selectAdjacentDays && adjacent) || !module.helper.isDateInRange(cellDate, mode) || settings.isDisabled(cellDate, mode) || module.helper.isDisabled(cellDate, mode) || !module.helper.isEnabled(cellDate, mode);
338
338
  if (disabled) {
339
- var disabledReason = module.helper.disabledReason(cellDate, mode);
340
- if (disabledReason !== null) {
341
- cell.attr("data-tooltip", disabledReason[metadata.message]);
339
+ var disabledDate = module.helper.findDayAsObject(cellDate, mode, settings.disabledDates);
340
+ if (disabledDate !== null && disabledDate[metadata.message]) {
341
+ cell.attr("data-tooltip", disabledDate[metadata.message]);
342
342
  cell.attr("data-position", tooltipPosition);
343
343
  }
344
+ } else {
345
+ var eventDate = module.helper.findDayAsObject(cellDate, mode, settings.eventDates);
346
+ if (eventDate !== null) {
347
+ cell.addClass(eventDate[metadata.class] || settings.eventClass);
348
+ if (eventDate[metadata.message]) {
349
+ cell.attr("data-tooltip", eventDate[metadata.message]);
350
+ cell.attr("data-position", tooltipPosition);
351
+ }
352
+ }
344
353
  }
345
354
  var active = module.helper.dateEqual(cellDate, date, mode);
346
355
  var isToday = module.helper.dateEqual(cellDate, today, mode);
@@ -483,7 +492,7 @@ $.fn.calendar = function(parameters) {
483
492
  var date = target.data(metadata.date);
484
493
  var focusDate = target.data(metadata.focusDate);
485
494
  var mode = target.data(metadata.mode);
486
- if (date) {
495
+ if (date && settings.onSelect.call(element, date, module.get.mode()) !== false) {
487
496
  var forceSet = target.hasClass(className.today);
488
497
  module.selectDate(date, forceSet);
489
498
  }
@@ -577,6 +586,9 @@ $.fn.calendar = function(parameters) {
577
586
  date: function () {
578
587
  return module.helper.sanitiseDate($module.data(metadata.date)) || null;
579
588
  },
589
+ inputDate: function() {
590
+ return $input.val();
591
+ },
580
592
  focusDate: function () {
581
593
  return $module.data(metadata.focusDate) || null;
582
594
  },
@@ -643,7 +655,7 @@ $.fn.calendar = function(parameters) {
643
655
  return null;
644
656
  }
645
657
  if (!(selector instanceof $)) {
646
- selector = $module.parent().children(selector).first();
658
+ selector = $(selector).first();
647
659
  }
648
660
  //assume range related calendars are using the same namespace
649
661
  return selector.data(moduleNamespace);
@@ -842,14 +854,19 @@ $.fn.calendar = function(parameters) {
842
854
  return true;
843
855
  }
844
856
  },
845
- disabledReason: function(date, mode) {
857
+ findDayAsObject: function(date, mode, dates) {
846
858
  if (mode === 'day') {
847
- for (var i = 0; i < settings.disabledDates.length; i++) {
848
- var d = settings.disabledDates[i];
849
- if (d !== null && typeof d === 'object' && module.helper.dateEqual(date, d[metadata.date], mode)) {
850
- var reason = {};
851
- reason[metadata.message] = d[metadata.message];
852
- return reason;
859
+ var i = 0, il = dates.length;
860
+ var d;
861
+ for (; i < il; i++) {
862
+ d = dates[i];
863
+ if (d instanceof Date && module.helper.dateEqual(date, d, mode)) {
864
+ var dateObject = {};
865
+ dateObject[metadata.date] = d;
866
+ return dateObject;
867
+ }
868
+ else if (d !== null && typeof d === 'object' && d[metadata.date] && module.helper.dateEqual(date, d[metadata.date], mode) ) {
869
+ return d;
853
870
  }
854
871
  }
855
872
  }
@@ -862,7 +879,7 @@ $.fn.calendar = function(parameters) {
862
879
  if (!(date instanceof Date)) {
863
880
  date = parser.date('' + date, settings);
864
881
  }
865
- if (isNaN(date.getTime())) {
882
+ if (!date || date === null || isNaN(date.getTime())) {
866
883
  return undefined;
867
884
  }
868
885
  return date;
@@ -1143,6 +1160,7 @@ $.fn.calendar.settings = {
1143
1160
  disabledDates : [], // specific day(s) which won't be selectable and contain additional information.
1144
1161
  disabledDaysOfWeek : [], // day(s) which won't be selectable(s) (0 = Sunday)
1145
1162
  enabledDates : [], // specific day(s) which will be selectable, all other days will be disabled
1163
+ eventDates : [], // specific day(s) which will be shown in a different color and using tooltips
1146
1164
  centuryBreak : 60, // starting short year until 99 where it will be assumed to belong to the last century
1147
1165
  currentCentury : 2000, // century to be added to 2-digit years (00 to {centuryBreak}-1)
1148
1166
  selectAdjacentDays : false, // The calendar can show dates from adjacent month. These adjacent month dates can also be made selectable.
@@ -1236,6 +1254,9 @@ $.fn.calendar.settings = {
1236
1254
 
1237
1255
  parser: {
1238
1256
  date: function (text, settings) {
1257
+ if (text instanceof Date) {
1258
+ return text;
1259
+ }
1239
1260
  if (!text) {
1240
1261
  return null;
1241
1262
  }
@@ -1457,6 +1478,10 @@ $.fn.calendar.settings = {
1457
1478
  onHidden: function () {
1458
1479
  },
1459
1480
 
1481
+ // callback before item is selected, return false to prevent selection
1482
+ onSelect: function (date, mode) {
1483
+ },
1484
+
1460
1485
  // is the given date disabled?
1461
1486
  isDisabled: function (date, mode) {
1462
1487
  return false;
@@ -1510,8 +1535,11 @@ $.fn.calendar.settings = {
1510
1535
  maxDate: 'maxDate',
1511
1536
  mode: 'mode',
1512
1537
  monthOffset: 'monthOffset',
1513
- message: 'message'
1514
- }
1538
+ message: 'message',
1539
+ class: 'class'
1540
+ },
1541
+
1542
+ eventClass: 'blue'
1515
1543
  };
1516
1544
 
1517
1545
  })(jQuery, window, document);
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * # Semantic UI - Checkbox
3
- * http://github.com/semantic-org/semantic-ui/
2
+ * # Fomantic-UI - Checkbox
3
+ * http://github.com/fomantic/Fomantic-UI/
4
4
  *
5
5
  *
6
6
  * Released under the MIT license
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * # Semantic UI - Dimmer
3
- * http://github.com/semantic-org/semantic-ui/
2
+ * # Fomantic-UI - Dimmer
3
+ * http://github.com/fomantic/Fomantic-UI/
4
4
  *
5
5
  *
6
6
  * Released under the MIT license
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * # Semantic UI - Dropdown
3
- * http://github.com/semantic-org/semantic-ui/
2
+ * # Fomantic-UI - Dropdown
3
+ * http://github.com/fomantic/Fomantic-UI/
4
4
  *
5
5
  *
6
6
  * Released under the MIT license
@@ -140,6 +140,9 @@ $.fn.dropdown = function(parameters) {
140
140
  destroy: function() {
141
141
  module.verbose('Destroying previous dropdown', $module);
142
142
  module.remove.tabbable();
143
+ module.remove.active();
144
+ $menu.transition('stop all');
145
+ $menu.removeClass(className.visible).addClass(className.hidden);
143
146
  $module
144
147
  .off(eventNamespace)
145
148
  .removeData(moduleNamespace)
@@ -529,6 +532,8 @@ $.fn.dropdown = function(parameters) {
529
532
  callback.call(element);
530
533
  });
531
534
  }
535
+ } else if( module.can.click() ) {
536
+ module.unbind.intent();
532
537
  }
533
538
  },
534
539
 
@@ -2718,7 +2723,12 @@ $.fn.dropdown = function(parameters) {
2718
2723
  $label
2719
2724
  .addClass(className.hidden)
2720
2725
  .insertBefore($next)
2721
- .transition(settings.label.transition, settings.label.duration)
2726
+ .transition({
2727
+ animation : settings.label.transition,
2728
+ debug : settings.debug,
2729
+ verbose : settings.verbose,
2730
+ duration : settings.label.duration
2731
+ })
2722
2732
  ;
2723
2733
  }
2724
2734
  else {
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * # Semantic UI - Embed
3
- * http://github.com/semantic-org/semantic-ui/
2
+ * # Fomantic-UI - Embed
3
+ * http://github.com/fomantic/Fomantic-UI/
4
4
  *
5
5
  *
6
6
  * Released under the MIT license
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * # Semantic UI - Form Validation
3
- * http://github.com/semantic-org/semantic-ui/
2
+ * # Fomantic-UI - Form Validation
3
+ * http://github.com/fomantic/Fomantic-UI/
4
4
  *
5
5
  *
6
6
  * Released under the MIT license
@@ -68,6 +68,10 @@ $.fn.form = function(parameters) {
68
68
  moduleNamespace,
69
69
  eventNamespace,
70
70
 
71
+ submitting = false,
72
+ dirty = false,
73
+ history = ['clean', 'clean'],
74
+
71
75
  instance,
72
76
  module
73
77
  ;
@@ -125,19 +129,16 @@ $.fn.form = function(parameters) {
125
129
 
126
130
  submit: function() {
127
131
  module.verbose('Submitting form', $module);
128
- $module
129
- .submit()
130
- ;
132
+ submitting = true;
133
+ $module.submit();
131
134
  },
132
135
 
133
136
  attachEvents: function(selector, action) {
134
137
  action = action || 'submit';
135
- $(selector)
136
- .on('click' + eventNamespace, function(event) {
137
- module[action]();
138
- event.preventDefault();
139
- })
140
- ;
138
+ $(selector).on('click' + eventNamespace, function(event) {
139
+ module[action]();
140
+ event.preventDefault();
141
+ });
141
142
  },
142
143
 
143
144
  bindEvents: function() {
@@ -150,92 +151,113 @@ $.fn.form = function(parameters) {
150
151
  .on('click' + eventNamespace, selector.clear, module.clear)
151
152
  ;
152
153
  if(settings.keyboardShortcuts) {
153
- $module
154
- .on('keydown' + eventNamespace, selector.field, module.event.field.keydown)
154
+ $module.on('keydown' + eventNamespace, selector.field, module.event.field.keydown);
155
+ }
156
+ $field.each(function(index, el) {
157
+ var
158
+ $input = $(el),
159
+ type = $input.prop('type'),
160
+ inputEvent = module.get.changeEvent(type, $input)
155
161
  ;
162
+ $input.on(inputEvent + eventNamespace, module.event.field.change);
163
+ });
164
+
165
+ // Dirty events
166
+ if (settings.preventLeaving) {
167
+ $(window).on('beforeunload' + eventNamespace, module.event.beforeUnload);
156
168
  }
157
- $field
158
- .each(function() {
159
- var
160
- $input = $(this),
161
- type = $input.prop('type'),
162
- inputEvent = module.get.changeEvent(type, $input)
163
- ;
164
- $(this)
165
- .on(inputEvent + eventNamespace, module.event.field.change)
166
- ;
167
- })
168
- ;
169
+
170
+ $field.on('change click keyup keydown blur', function(e) {
171
+ $(this).trigger(e.type + ".dirty");
172
+ });
173
+
174
+ $field.on('change.dirty click.dirty keyup.dirty keydown.dirty blur.dirty', module.determine.isDirty);
175
+
176
+ $module.on('dirty' + eventNamespace, function(e) {
177
+ settings.onDirty.call();
178
+ });
179
+
180
+ $module.on('clean' + eventNamespace, function(e) {
181
+ settings.onClean.call();
182
+ })
169
183
  },
170
184
 
171
185
  clear: function() {
172
- $field
173
- .each(function () {
174
- var
175
- $field = $(this),
176
- $element = $field.parent(),
177
- $fieldGroup = $field.closest($group),
178
- $prompt = $fieldGroup.find(selector.prompt),
179
- defaultValue = $field.data(metadata.defaultValue) || '',
180
- isCheckbox = $element.is(selector.uiCheckbox),
181
- isDropdown = $element.is(selector.uiDropdown),
182
- isErrored = $fieldGroup.hasClass(className.error)
183
- ;
184
- if(isErrored) {
185
- module.verbose('Resetting error on field', $fieldGroup);
186
- $fieldGroup.removeClass(className.error);
187
- $prompt.remove();
188
- }
189
- if(isDropdown) {
190
- module.verbose('Resetting dropdown value', $element, defaultValue);
191
- $element.dropdown('clear');
192
- }
193
- else if(isCheckbox) {
194
- $field.prop('checked', false);
195
- }
196
- else {
197
- module.verbose('Resetting field value', $field, defaultValue);
198
- $field.val('');
199
- }
200
- })
201
- ;
186
+ $field.each(function (index, el) {
187
+ var
188
+ $field = $(el),
189
+ $element = $field.parent(),
190
+ $fieldGroup = $field.closest($group),
191
+ $prompt = $fieldGroup.find(selector.prompt),
192
+ $calendar = $field.closest(selector.uiCalendar),
193
+ defaultValue = $field.data(metadata.defaultValue) || '',
194
+ isCheckbox = $element.is(selector.uiCheckbox),
195
+ isDropdown = $element.is(selector.uiDropdown),
196
+ isCalendar = ($calendar.length > 0),
197
+ isErrored = $fieldGroup.hasClass(className.error)
198
+ ;
199
+ if(isErrored) {
200
+ module.verbose('Resetting error on field', $fieldGroup);
201
+ $fieldGroup.removeClass(className.error);
202
+ $prompt.remove();
203
+ }
204
+ if(isDropdown) {
205
+ module.verbose('Resetting dropdown value', $element, defaultValue);
206
+ $element.dropdown('clear');
207
+ }
208
+ else if(isCheckbox) {
209
+ $field.prop('checked', false);
210
+ }
211
+ else if (isCalendar) {
212
+ $calendar.calendar('clear');
213
+ }
214
+ else {
215
+ module.verbose('Resetting field value', $field, defaultValue);
216
+ $field.val('');
217
+ }
218
+ });
202
219
  },
203
220
 
204
221
  reset: function() {
205
- $field
206
- .each(function () {
207
- var
208
- $field = $(this),
209
- $element = $field.parent(),
210
- $fieldGroup = $field.closest($group),
211
- $prompt = $fieldGroup.find(selector.prompt),
212
- defaultValue = $field.data(metadata.defaultValue),
213
- isCheckbox = $element.is(selector.uiCheckbox),
214
- isDropdown = $element.is(selector.uiDropdown),
215
- isErrored = $fieldGroup.hasClass(className.error)
216
- ;
217
- if(defaultValue === undefined) {
218
- return;
219
- }
220
- if(isErrored) {
221
- module.verbose('Resetting error on field', $fieldGroup);
222
- $fieldGroup.removeClass(className.error);
223
- $prompt.remove();
224
- }
225
- if(isDropdown) {
226
- module.verbose('Resetting dropdown value', $element, defaultValue);
227
- $element.dropdown('restore defaults');
228
- }
229
- else if(isCheckbox) {
230
- module.verbose('Resetting checkbox value', $element, defaultValue);
231
- $field.prop('checked', defaultValue);
232
- }
233
- else {
234
- module.verbose('Resetting field value', $field, defaultValue);
235
- $field.val(defaultValue);
236
- }
237
- })
238
- ;
222
+ $field.each(function (index, el) {
223
+ var
224
+ $field = $(el),
225
+ $element = $field.parent(),
226
+ $fieldGroup = $field.closest($group),
227
+ $calendar = $field.closest(selector.uiCalendar),
228
+ $prompt = $fieldGroup.find(selector.prompt),
229
+ defaultValue = $field.data(metadata.defaultValue),
230
+ isCheckbox = $element.is(selector.uiCheckbox),
231
+ isDropdown = $element.is(selector.uiDropdown),
232
+ isCalendar = ($calendar.length > 0),
233
+ isErrored = $fieldGroup.hasClass(className.error)
234
+ ;
235
+ if(defaultValue === undefined) {
236
+ return;
237
+ }
238
+ if(isErrored) {
239
+ module.verbose('Resetting error on field', $fieldGroup);
240
+ $fieldGroup.removeClass(className.error);
241
+ $prompt.remove();
242
+ }
243
+ if(isDropdown) {
244
+ module.verbose('Resetting dropdown value', $element, defaultValue);
245
+ $element.dropdown('restore defaults');
246
+ }
247
+ else if(isCheckbox) {
248
+ module.verbose('Resetting checkbox value', $element, defaultValue);
249
+ $field.prop('checked', defaultValue);
250
+ }
251
+ else if (isCalendar) {
252
+ $calendar.calendar('set date', defaultValue);
253
+ }
254
+ else {
255
+ module.verbose('Resetting field value', $field, defaultValue);
256
+ $field.val(defaultValue);
257
+ }
258
+ });
259
+
260
+ module.determine.isDirty();
239
261
  },
240
262
 
241
263
  determine: {
@@ -249,6 +271,37 @@ $.fn.form = function(parameters) {
249
271
  }
250
272
  });
251
273
  return allValid;
274
+ },
275
+ isDirty: function(e) {
276
+ var formIsDirty = false;
277
+
278
+ $field.each(function(index, el) {
279
+ var
280
+ $el = $(el),
281
+ isCheckbox = ($el.filter(selector.checkbox).length > 0),
282
+ isDirty
283
+ ;
284
+
285
+ if (isCheckbox) {
286
+ isDirty = module.is.checkboxDirty($el);
287
+ } else {
288
+ isDirty = module.is.fieldDirty($el);
289
+ }
290
+
291
+ $el.data(settings.metadata.isDirty, isDirty);
292
+
293
+ formIsDirty |= isDirty;
294
+ });
295
+
296
+ if (formIsDirty) {
297
+ module.set.dirty();
298
+ } else {
299
+ module.set.clean();
300
+ }
301
+
302
+ if (e) {
303
+ e.stopImmediatePropagation();
304
+ }
252
305
  }
253
306
  },
254
307
 
@@ -298,22 +351,49 @@ $.fn.form = function(parameters) {
298
351
  });
299
352
  return allValid;
300
353
  }
354
+ },
355
+ dirty: function() {
356
+ return dirty;
357
+ },
358
+ clean: function() {
359
+ return !dirty;
360
+ },
361
+ fieldDirty: function($el) {
362
+ var initialValue = $el.data(metadata.defaultValue);
363
+ // Explicitly check for null/undefined here as value may be `false`, so ($el.data(dataInitialValue) || '') would not work
364
+ if (initialValue == null) { initialValue = ''; }
365
+ var currentValue = $el.val();
366
+ if (currentValue == null) { currentValue = ''; }
367
+
368
+ // Boolean values can be encoded as "true/false" or "True/False" depending on underlying frameworks so we need a case insensitive comparison
369
+ var boolRegex = /^(true|false)$/i;
370
+ var isBoolValue = boolRegex.test(initialValue) && boolRegex.test(currentValue);
371
+ if (isBoolValue) {
372
+ var regex = new RegExp("^" + initialValue + "$", "i");
373
+ return !regex.test(currentValue);
374
+ }
375
+
376
+ return currentValue !== initialValue;
377
+ },
378
+ checkboxDirty: function($el) {
379
+ var initialValue = $el.data(metadata.defaultValue);
380
+ var currentValue = $el.is(":checked");
381
+
382
+ return initialValue !== currentValue;
383
+ },
384
+ justDirty: function() {
385
+ return (history[0] === 'dirty');
386
+ },
387
+ justClean: function() {
388
+ return (history[0] === 'clean');
301
389
  }
302
390
  },
303
391
 
304
392
  removeEvents: function() {
305
- $module
306
- .off(eventNamespace)
307
- ;
308
- $field
309
- .off(eventNamespace)
310
- ;
311
- $submit
312
- .off(eventNamespace)
313
- ;
314
- $field
315
- .off(eventNamespace)
316
- ;
393
+ $module.off(eventNamespace);
394
+ $field.off(eventNamespace);
395
+ $submit.off(eventNamespace);
396
+ $field.off(eventNamespace);
317
397
  },
318
398
 
319
399
  event: {
@@ -338,9 +418,7 @@ $.fn.form = function(parameters) {
338
418
  }
339
419
  if(!event.ctrlKey && key == keyCode.enter && isInput && !isInDropdown && !isCheckbox) {
340
420
  if(!keyHeldDown) {
341
- $field
342
- .one('keyup' + eventNamespace, module.event.field.keyup)
343
- ;
421
+ $field.one('keyup' + eventNamespace, module.event.field.keyup);
344
422
  module.submit();
345
423
  module.debug('Enter pressed on input submitting form');
346
424
  }
@@ -382,6 +460,19 @@ $.fn.form = function(parameters) {
382
460
  }, settings.delay);
383
461
  }
384
462
  }
463
+ },
464
+ beforeUnload: function(event) {
465
+ if (module.is.dirty() && !submitting) {
466
+ var event = event || window.event;
467
+
468
+ // For modern browsers
469
+ if (event) {
470
+ event.returnValue = settings.text.leavingMessage;
471
+ }
472
+
473
+ // For olders...
474
+ return settings.text.leavingMessage;
475
+ }
385
476
  }
386
477
 
387
478
  },
@@ -579,13 +670,15 @@ $.fn.form = function(parameters) {
579
670
  ;
580
671
  $fields.each(function(index, field) {
581
672
  var
582
- $field = $(field),
583
- name = $field.prop('name'),
584
- value = $field.val(),
585
- isCheckbox = $field.is(selector.checkbox),
586
- isRadio = $field.is(selector.radio),
587
- isMultiple = (name.indexOf('[]') !== -1),
588
- isChecked = (isCheckbox)
673
+ $field = $(field),
674
+ $calendar = $field.closest(selector.uiCalendar),
675
+ name = $field.prop('name'),
676
+ value = $field.val(),
677
+ isCheckbox = $field.is(selector.checkbox),
678
+ isRadio = $field.is(selector.radio),
679
+ isMultiple = (name.indexOf('[]') !== -1),
680
+ isCalendar = ($calendar.length > 0),
681
+ isChecked = (isCheckbox)
589
682
  ? $field.is(':checked')
590
683
  : false
591
684
  ;
@@ -624,13 +717,58 @@ $.fn.form = function(parameters) {
624
717
  values[name] = false;
625
718
  }
626
719
  }
627
- else {
720
+ else if(isCalendar) {
721
+ var date = $calendar.calendar('get date');
722
+
723
+ if (date !== null) {
724
+ if (settings.dateHandling == 'date') {
725
+ values[name] = date;
726
+ } else if(settings.dateHandling == 'input') {
727
+ values[name] = $calendar.calendar('get input date')
728
+ } else if (settings.dateHandling == 'formatter') {
729
+ var type = $calendar.calendar('setting', 'type');
730
+
731
+ switch(type) {
732
+ case 'date':
733
+ values[name] = settings.formatter.date(date);
734
+ break;
735
+
736
+ case 'datetime':
737
+ values[name] = settings.formatter.datetime(date);
738
+ break;
739
+
740
+ case 'time':
741
+ values[name] = settings.formatter.time(date);
742
+ break;
743
+
744
+ case 'month':
745
+ values[name] = settings.formatter.month(date);
746
+ break;
747
+
748
+ case 'year':
749
+ values[name] = settings.formatter.year(date);
750
+ break;
751
+
752
+ default:
753
+ module.debug('Wrong calendar mode', $calendar, type);
754
+ values[name] = '';
755
+ }
756
+ }
757
+ } else {
758
+ values[name] = '';
759
+ }
760
+ } else {
628
761
  values[name] = value;
629
762
  }
630
763
  }
631
764
  }
632
765
  });
633
766
  return values;
767
+ },
768
+ dirtyFields: function() {
769
+ return $field.filter(function(index, e) {
770
+ return $(e).data(metadata.isDirty);
771
+ });
634
772
  }
635
773
  },
636
774
 
@@ -836,18 +974,22 @@ $.fn.form = function(parameters) {
836
974
  ;
837
975
  },
838
976
  defaults: function () {
839
- $field
840
- .each(function () {
841
- var
842
- $field = $(this),
843
- isCheckbox = ($field.filter(selector.checkbox).length > 0),
844
- value = (isCheckbox)
845
- ? $field.is(':checked')
846
- : $field.val()
847
- ;
848
- $field.data(metadata.defaultValue, value);
849
- })
850
- ;
977
+ $field.each(function (index, el) {
978
+ var
979
+ $el = $(el),
980
+ $parent = $el.parent(),
981
+ isCheckbox = ($el.filter(selector.checkbox).length > 0),
982
+ isDropdown = $parent.is(selector.uiDropdown),
983
+ value = (isCheckbox)
984
+ ? $el.is(':checked')
985
+ : $el.val()
986
+ ;
987
+ if (isDropdown) {
988
+ $parent.dropdown('save defaults');
989
+ }
990
+ $el.data(metadata.defaultValue, value);
991
+ $el.data(metadata.isDirty, false);
992
+ });
851
993
  },
852
994
  error: function() {
853
995
  $module
@@ -915,15 +1057,41 @@ $.fn.form = function(parameters) {
915
1057
  }
916
1058
  }
917
1059
  });
1060
+ },
1061
+ dirty: function() {
1062
+ module.verbose('Setting state dirty');
1063
+ dirty = true;
1064
+ history[0] = history[1];
1065
+ history[1] = 'dirty';
1066
+
1067
+ if (module.is.justClean()) {
1068
+ $module.trigger('dirty');
1069
+ }
1070
+ },
1071
+ clean: function() {
1072
+ module.verbose('Setting state clean');
1073
+ dirty = false;
1074
+ history[0] = history[1];
1075
+ history[1] = 'clean';
1076
+
1077
+ if (module.is.justDirty()) {
1078
+ $module.trigger('clean');
1079
+ }
1080
+ },
1081
+ asClean: function() {
1082
+ module.set.defaults();
1083
+ module.set.clean();
1084
+ },
1085
+ asDirty: function() {
1086
+ module.set.defaults();
1087
+ module.set.dirty();
918
1088
  }
919
1089
  },
920
1090
 
921
1091
  validate: {
922
1092
 
923
1093
  form: function(event, ignoreCallbacks) {
924
- var
925
- values = module.get.values()
926
- ;
1094
+ var values = module.get.values();
927
1095
 
928
1096
  // input keydown event will fire submit repeatedly by browser default
929
1097
  if(keyHeldDown) {
@@ -946,7 +1114,7 @@ $.fn.form = function(parameters) {
946
1114
  module.add.errors(formErrors);
947
1115
  }
948
1116
  // prevent ajax submit
949
- if($module.data('moduleApi') !== undefined) {
1117
+ if(event && $module.data('moduleApi') !== undefined) {
950
1118
  event.stopImmediatePropagation();
951
1119
  }
952
1120
  if(ignoreCallbacks !== true) {
@@ -1044,7 +1212,7 @@ $.fn.form = function(parameters) {
1044
1212
  ? ''
1045
1213
  : (settings.shouldTrim) ? $.trim(value + '') : String(value + '')
1046
1214
  ;
1047
- return ruleFunction.call(field, value, ancillary);
1215
+ return ruleFunction.call(field, value, ancillary, $module);
1048
1216
  }
1049
1217
  ;
1050
1218
  if( !$.isFunction(ruleFunction) ) {
@@ -1256,14 +1424,20 @@ $.fn.form.settings = {
1256
1424
  transition : 'scale',
1257
1425
  duration : 200,
1258
1426
 
1427
+ preventLeaving : false,
1428
+ dateHandling : 'date', // 'date', 'input', 'formatter'
1429
+
1259
1430
  onValid : function() {},
1260
1431
  onInvalid : function() {},
1261
1432
  onSuccess : function() { return true; },
1262
1433
  onFailure : function() { return false; },
1434
+ onDirty : function() {},
1435
+ onClean : function() {},
1263
1436
 
1264
1437
  metadata : {
1265
1438
  defaultValue : 'default',
1266
- validate : 'validate'
1439
+ validate : 'validate',
1440
+ isDirty : 'isDirty'
1267
1441
  },
1268
1442
 
1269
1443
  regExp: {
@@ -1280,7 +1454,8 @@ $.fn.form.settings = {
1280
1454
 
1281
1455
  text: {
1282
1456
  unspecifiedRule : 'Please enter a valid value',
1283
- unspecifiedField : 'This field'
1457
+ unspecifiedField : 'This field',
1458
+ leavingMessage : 'There are unsaved changes on this page which will be discarded if you continue.'
1284
1459
  },
1285
1460
 
1286
1461
  prompt: {
@@ -1324,7 +1499,8 @@ $.fn.form.settings = {
1324
1499
  reset : '.reset:not([type="reset"])',
1325
1500
  submit : '.submit:not([type="submit"])',
1326
1501
  uiCheckbox : '.ui.checkbox',
1327
- uiDropdown : '.ui.dropdown'
1502
+ uiDropdown : '.ui.dropdown',
1503
+ uiCalendar : '.ui.calendar'
1328
1504
  },
1329
1505
 
1330
1506
  className : {
@@ -1364,6 +1540,40 @@ $.fn.form.settings = {
1364
1540
  }
1365
1541
  },
1366
1542
 
1543
+ formatter: {
1544
+ date: function(date) {
1545
+ return Intl.DateTimeFormat('en-GB').format(date);
1546
+ },
1547
+ datetime: function(date) {
1548
+ return Intl.DateTimeFormat('en-GB', {
1549
+ year: "numeric",
1550
+ month: "2-digit",
1551
+ day: "2-digit",
1552
+ hour: '2-digit',
1553
+ minute: '2-digit',
1554
+ second: '2-digit'
1555
+ }).format(date);
1556
+ },
1557
+ time: function(date) {
1558
+ return Intl.DateTimeFormat('en-GB', {
1559
+ hour: '2-digit',
1560
+ minute: '2-digit',
1561
+ second: '2-digit'
1562
+ }).format(date);
1563
+ },
1564
+ month: function(date) {
1565
+ return Intl.DateTimeFormat('en-GB', {
1566
+ month: '2-digit',
1567
+ year: 'numeric'
1568
+ }).format(date);
1569
+ },
1570
+ year: function(date) {
1571
+ return Intl.DateTimeFormat('en-GB', {
1572
+ year: 'numeric'
1573
+ }).format(date);
1574
+ }
1575
+ },
1576
+
1367
1577
  rules: {
1368
1578
 
1369
1579
  // is not empty or blank string
@@ -1548,21 +1758,22 @@ $.fn.form.settings = {
1548
1758
  },
1549
1759
 
1550
1760
  // matches another field
1551
- match: function(value, identifier) {
1761
+ match: function(value, identifier, $module) {
1552
1762
  var
1553
- matchingValue
1763
+ matchingValue,
1764
+ matchingElement
1554
1765
  ;
1555
- if( $('[data-validate="'+ identifier +'"]').length > 0 ) {
1556
- matchingValue = $('[data-validate="'+ identifier +'"]').val();
1766
+ if((matchingElement = $module.find('[data-validate="'+ identifier +'"]')).length > 0 ) {
1767
+ matchingValue = matchingElement.val();
1557
1768
  }
1558
- else if($('#' + identifier).length > 0) {
1559
- matchingValue = $('#' + identifier).val();
1769
+ else if((matchingElement = $module.find('#' + identifier)).length > 0) {
1770
+ matchingValue = matchingElement.val();
1560
1771
  }
1561
- else if($('[name="' + identifier +'"]').length > 0) {
1562
- matchingValue = $('[name="' + identifier + '"]').val();
1772
+ else if((matchingElement = $module.find('[name="' + identifier +'"]')).length > 0) {
1773
+ matchingValue = matchingElement.val();
1563
1774
  }
1564
- else if( $('[name="' + identifier +'[]"]').length > 0 ) {
1565
- matchingValue = $('[name="' + identifier +'[]"]');
1775
+ else if((matchingElement = $module.find('[name="' + identifier +'[]"]')).length > 0 ) {
1776
+ matchingValue = matchingElement;
1566
1777
  }
1567
1778
  return (matchingValue !== undefined)
1568
1779
  ? ( value.toString() == matchingValue.toString() )
@@ -1571,22 +1782,23 @@ $.fn.form.settings = {
1571
1782
  },
1572
1783
 
1573
1784
  // different than another field
1574
- different: function(value, identifier) {
1785
+ different: function(value, identifier, $module) {
1575
1786
  // use either id or name of field
1576
1787
  var
1577
- matchingValue
1788
+ matchingValue,
1789
+ matchingElement
1578
1790
  ;
1579
- if( $('[data-validate="'+ identifier +'"]').length > 0 ) {
1580
- matchingValue = $('[data-validate="'+ identifier +'"]').val();
1791
+ if((matchingElement = $module.find('[data-validate="'+ identifier +'"]')).length > 0 ) {
1792
+ matchingValue = matchingElement.val();
1581
1793
  }
1582
- else if($('#' + identifier).length > 0) {
1583
- matchingValue = $('#' + identifier).val();
1794
+ else if((matchingElement = $module.find('#' + identifier)).length > 0) {
1795
+ matchingValue = matchingElement.val();
1584
1796
  }
1585
- else if($('[name="' + identifier +'"]').length > 0) {
1586
- matchingValue = $('[name="' + identifier + '"]').val();
1797
+ else if((matchingElement = $module.find('[name="' + identifier +'"]')).length > 0) {
1798
+ matchingValue = matchingElement.val();
1587
1799
  }
1588
- else if( $('[name="' + identifier +'[]"]').length > 0 ) {
1589
- matchingValue = $('[name="' + identifier +'[]"]');
1800
+ else if((matchingElement = $module.find('[name="' + identifier +'[]"]')).length > 0 ) {
1801
+ matchingValue = matchingElement;
1590
1802
  }
1591
1803
  return (matchingValue !== undefined)
1592
1804
  ? ( value.toString() !== matchingValue.toString() )