fomantic-ui-sass 2.7.4 → 2.7.5

Sign up to get free protection for your applications and to get access to all the features.
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() )