romo 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.gitignore +2 -0
  2. data/Gemfile +9 -1
  3. data/README.md +1 -1
  4. data/assets/css/romo/_mixins.scss +486 -0
  5. data/assets/css/romo/_vars.scss +159 -0
  6. data/assets/css/romo/base.scss +454 -0
  7. data/assets/css/romo/buttons.scss +211 -0
  8. data/assets/css/romo/datepicker.scss +73 -0
  9. data/assets/css/romo/dropdown.scss +33 -0
  10. data/assets/css/romo/forms.scss +193 -0
  11. data/assets/css/romo/grid.scss +271 -0
  12. data/assets/css/romo/grid_table.scss +129 -0
  13. data/assets/css/romo/labels.scss +41 -0
  14. data/assets/css/romo/lists.scss +37 -0
  15. data/assets/css/romo/modal.scss +32 -0
  16. data/assets/css/romo/normalize.scss +425 -0
  17. data/assets/css/romo/select.scss +89 -0
  18. data/assets/css/romo/sortable.scss +14 -0
  19. data/assets/css/romo/table.scss +99 -0
  20. data/assets/css/romo/tabs.scss +71 -0
  21. data/assets/css/romo/tooltip.scss +89 -0
  22. data/assets/css/romo/z_index.scss +26 -0
  23. data/assets/js/romo/base.js +177 -0
  24. data/assets/js/romo/datepicker.js +541 -0
  25. data/assets/js/romo/dropdown.js +309 -0
  26. data/assets/js/romo/dropdown_form.js +92 -0
  27. data/assets/js/romo/form.js +182 -0
  28. data/assets/js/romo/indicator.js +88 -0
  29. data/assets/js/romo/inline.js +77 -0
  30. data/assets/js/romo/inline_form.js +86 -0
  31. data/assets/js/romo/invoke.js +87 -0
  32. data/assets/js/romo/modal.js +311 -0
  33. data/assets/js/romo/modal_form.js +101 -0
  34. data/assets/js/romo/select.js +139 -0
  35. data/assets/js/romo/select_dropdown.js +325 -0
  36. data/assets/js/romo/sortable.js +201 -0
  37. data/assets/js/romo/tooltip.js +258 -0
  38. data/lib/romo/dassets.rb +64 -0
  39. data/lib/romo/version.rb +1 -1
  40. data/lib/romo.rb +9 -0
  41. data/romo.gemspec +4 -2
  42. data/test/support/.gitkeep +0 -0
  43. data/test/system/.gitkeep +0 -0
  44. data/test/unit/dassets_tests.rb +67 -0
  45. data/test/unit/romo_tests.rb +21 -0
  46. metadata +53 -10
@@ -0,0 +1,309 @@
1
+ $.fn.romoDropdown = function() {
2
+ return $.map(this, function(element) {
3
+ return new RomoDropdown(element);
4
+ });
5
+ }
6
+
7
+ var RomoDropdown = function(element) {
8
+ this.elem = $(element);
9
+ this.popupElem = $('<div class="romo-dropdown-popup"><div class="romo-dropdown-body"></div></div>');
10
+ this.popupElem.appendTo('body');
11
+ this.doSetPopupZIndex(this.elem);
12
+ this.bodyElem = this.popupElem.find('> .romo-dropdown-body');
13
+ this.contentElem = $();
14
+ this.romoInvoke = this.elem.romoInvoke()[0];
15
+
16
+ var positionData = this._parsePositionData(this.elem.data('romo-dropdown-position'));
17
+ this.popupPosition = positionData.position || 'bottom';
18
+ this.popupAlignment = positionData.alignment || 'left';
19
+ this.popupElem.attr('data-romo-dropdown-position', this.popupPosition);
20
+ this.popupElem.attr('data-romo-dropdown-alignment', this.popupAlignment);
21
+ this.popupElem.attr('data-romo-dropdown-fixed', this.elem.data('romo-dropdown-fixed'));
22
+ // don't propagate click events on the popup elem. this prevents the popup
23
+ // from closing when clicked (see body click event bind on popup open)
24
+ this.popupElem.on('click', function(e) {
25
+ if (e !== undefined) {
26
+ e.stopPropagation();
27
+ }
28
+ })
29
+
30
+ if (this.elem.data('romo-dropdown-style-class') !== undefined) {
31
+ this.bodyElem.addClass(this.elem.data('romo-dropdown-style-class'));
32
+ }
33
+
34
+ this.elem.unbind('click');
35
+ this.elem.on('click', $.proxy(this.onToggleClick, this));
36
+ this.elem.on('dropdown:triggerToggle', $.proxy(this.onToggleClick, this));
37
+ this.elem.on('dropdown:triggerPopupOpen', $.proxy(this.onPopupOpen, this));
38
+ this.elem.on('dropdown:triggerPopupClose', $.proxy(this.onPopupClose, this));
39
+ this.elem.on('invoke:loadStart', $.proxy(function(e, invoke) {
40
+ this.doLoadBodyStart();
41
+ }, this));
42
+ this.elem.on('invoke:loadSuccess', $.proxy(function(e, data, invoke) {
43
+ this.doLoadBodySuccess(data);
44
+ }, this));
45
+ this.elem.on('invoke:loadError', $.proxy(function(e, xhr, invoke) {
46
+ this.doLoadBodyError(xhr);
47
+ }, this));
48
+ this.elem.on('keyup', $.proxy(this.onElemKeyUp, this));
49
+ this.popupElem.on('keyup', $.proxy(this.onElemKeyUp, this));
50
+
51
+ this.doInit();
52
+ this.doInitBody();
53
+
54
+ this.elem.trigger('dropdown:ready', [this]);
55
+ }
56
+
57
+ RomoDropdown.prototype.doInit = function() {
58
+ // override as needed
59
+ }
60
+
61
+ RomoDropdown.prototype.doInitBody = function() {
62
+ this.doResetBody();
63
+
64
+ this.contentElem = this.bodyElem.find('.romo-dropdown-content').last();
65
+ if (this.contentElem.size() === 0) {
66
+ this.contentElem = this.bodyElem;
67
+ }
68
+ this.closeElem = this.popupElem.find('[data-romo-dropdown-close="true"]');
69
+
70
+ this.contentElem.css({
71
+ 'min-height': this.elem.data('romo-dropdown-min-height'),
72
+ 'height': this.elem.data('romo-dropdown-height'),
73
+ 'overflow-x': this.elem.data('romo-dropdown-overflow-x') || 'auto',
74
+ 'overflow-y': this.elem.data('romo-dropdown-overflow-y') || 'auto'
75
+ });
76
+
77
+ if (this.elem.data('romo-dropdown-max-height') === undefined) {
78
+ this.elem.attr('data-romo-dropdown-max-height', 'detect');
79
+ }
80
+ if (this.elem.data('romo-dropdown-max-height') !== 'detect') {
81
+ this.contentElem.css({
82
+ 'max-height': this.elem.data('romo-dropdown-max-height')
83
+ });
84
+ }
85
+
86
+ if (this.elem.data('romo-dropdown-width') === 'elem') {
87
+ this.popupElem.css({
88
+ 'width': this.elem.css('width')
89
+ });
90
+ } else {
91
+ this.contentElem.css({
92
+ 'min-width': this.elem.data('romo-dropdown-min-width'),
93
+ 'max-width': this.elem.data('romo-dropdown-max-width'),
94
+ 'width': this.elem.data('romo-dropdown-width')
95
+ });
96
+ }
97
+
98
+ this.closeElem.unbind('click');
99
+ this.closeElem.on('click', $.proxy(this.onPopupClose, this));
100
+ }
101
+
102
+ RomoDropdown.prototype.doResetBody = function() {
103
+ this.contentElem.css({
104
+ 'min-width': '',
105
+ 'max-width': '',
106
+ 'width': '',
107
+ 'min-height': '',
108
+ 'max-height': '',
109
+ 'height': '',
110
+ 'overflow-x': '',
111
+ 'overflow-y': ''
112
+ });
113
+ }
114
+
115
+ RomoDropdown.prototype.doLoadBodyStart = function() {
116
+ this.bodyElem.html('');
117
+ this.doInitBody();
118
+ this.doPlacePopupElem();
119
+ this.elem.trigger('dropdown:loadBodyStart', [this]);
120
+ }
121
+
122
+ RomoDropdown.prototype.doLoadBodySuccess = function(data) {
123
+ Romo.initHtml(this.bodyElem, data);
124
+ this.doInitBody();
125
+ this.doPlacePopupElem();
126
+ this.elem.trigger('dropdown:loadBodySuccess', [data, this]);
127
+ }
128
+
129
+ RomoDropdown.prototype.doLoadBodyError = function(xhr) {
130
+ this.elem.trigger('dropdown:loadBodyError', [xhr, this]);
131
+ }
132
+
133
+ RomoDropdown.prototype.onToggleClick = function(e) {
134
+ if (e !== undefined) {
135
+ e.preventDefault();
136
+ }
137
+
138
+ if (this.elem.hasClass('disabled') === false &&
139
+ this.elem.data('romo-dropdown-disable-toggle') !== true) {
140
+ this.doToggle();
141
+ return true;
142
+ }
143
+ return false;
144
+ }
145
+
146
+ RomoDropdown.prototype.doToggle = function() {
147
+ if (this.popupElem.hasClass('romo-dropdown-open')) {
148
+ setTimeout($.proxy(function() {
149
+ this.doPopupClose();
150
+ }, this), 100);
151
+ } else {
152
+ setTimeout($.proxy(function() {
153
+ this.doPopupOpen();
154
+ }, this), 100);
155
+ }
156
+ this.elem.trigger('dropdown:toggle', [this]);
157
+ }
158
+
159
+ RomoDropdown.prototype.onPopupOpen = function(e) {
160
+ if (e !== undefined) {
161
+ e.preventDefault();
162
+ }
163
+
164
+ if (this.elem.hasClass('disabled') === false &&
165
+ this.popupElem.hasClass('romo-dropdown-open') === false) {
166
+ setTimeout($.proxy(function() {
167
+ this.doPopupOpen();
168
+ }, this), 100);
169
+ }
170
+ }
171
+
172
+ RomoDropdown.prototype.doPopupOpen = function() {
173
+ this.romoInvoke.doInvoke();
174
+ this.popupElem.addClass('romo-dropdown-open');
175
+ this.doPlacePopupElem();
176
+
177
+ // bind an event to close the popup when clicking away from the
178
+ // popup. Bind on a timeout to allow time for any toggle
179
+ // click event to propagate. If no timeout, we'll bind this
180
+ // event, then the toggle click will propagate which will call
181
+ // this event and immediately close the popup.
182
+ setTimeout($.proxy(function() {
183
+ $('body').on('click', $.proxy(this.onWindowBodyClick, this));
184
+ $('body').on('modal:mousedown', $.proxy(this.onWindowBodyClick, this));
185
+ }, this), 100);
186
+ $('body').on('keyup', $.proxy(this.onWindowBodyKeyUp, this));
187
+ $(window).on('resize', $.proxy(this.onResizeWindow, this));
188
+
189
+ this.elem.trigger('dropdown:popupOpen', [this]);
190
+ }
191
+
192
+ RomoDropdown.prototype.onPopupClose = function(e) {
193
+ if (e !== undefined) {
194
+ e.preventDefault();
195
+ }
196
+
197
+ if (this.elem.hasClass('disabled') === false &&
198
+ this.popupElem.hasClass('romo-dropdown-open') === true) {
199
+ setTimeout($.proxy(function() {
200
+ this.doPopupClose();
201
+ }, this), 100);
202
+ }
203
+ }
204
+
205
+ RomoDropdown.prototype.doPopupClose = function() {
206
+ this.popupElem.removeClass('romo-dropdown-open');
207
+
208
+ $('body').off('click', $.proxy(this.onWindowBodyClick, this));
209
+ $('body').off('modal:mousedown', $.proxy(this.onWindowBodyClick, this));
210
+ $('body').off('keyup', $.proxy(this.onWindowBodyKeyUp, this));
211
+ $(window).off('resize', $.proxy(this.onResizeWindow, this));
212
+
213
+ this.elem.trigger('dropdown:popupClose', [this]);
214
+ }
215
+
216
+ RomoDropdown.prototype.onElemKeyUp = function(e) {
217
+ if (this.elem.hasClass('disabled') === false) {
218
+ if (this.popupElem.hasClass('romo-dropdown-open')) {
219
+ if(e.keyCode === 27 /* Esc */ ) {
220
+ this.doPopupClose();
221
+ return false;
222
+ } else {
223
+ return true;
224
+ }
225
+ } else {
226
+ return true;
227
+ }
228
+ }
229
+ return true;
230
+ }
231
+
232
+ RomoDropdown.prototype.onWindowBodyClick = function(e) {
233
+ // if not clicked on the popup elem or the elem
234
+ var target = $(e.target);
235
+ if (e !== undefined &&
236
+ target.parents('.romo-dropdown-popup').size() === 0 &&
237
+ target.closest(this.elem).size() === 0) {
238
+ this.doPopupClose();
239
+ }
240
+ return true;
241
+ }
242
+
243
+ RomoDropdown.prototype.onWindowBodyKeyUp = function(e) {
244
+ if (e.keyCode === 27 /* Esc */) {
245
+ this.doPopupClose();
246
+ }
247
+ return true;
248
+ }
249
+
250
+ RomoDropdown.prototype.onResizeWindow = function(e) {
251
+ this.doPlacePopupElem();
252
+ return true;
253
+ }
254
+
255
+ RomoDropdown.prototype.doPlacePopupElem = function() {
256
+ if (this.elem.parents('.romo-modal-popup').size() !== 0) {
257
+ this.popupElem.css({'position': 'fixed'});
258
+ }
259
+
260
+ var pos = $.extend({}, this.elem[0].getBoundingClientRect(), this.elem.offset());
261
+ var w = this.popupElem[0].offsetWidth;
262
+ var h = this.popupElem[0].offsetHeight;
263
+ var pad = 2;
264
+ var offset = {};
265
+
266
+ switch (this.popupPosition) {
267
+ case 'top':
268
+ $.extend(offset, { top: pos.top - h - pad });
269
+ break;
270
+ case 'bottom':
271
+ $.extend(offset, { top: pos.top + pos.height + pad });
272
+ break;
273
+ }
274
+ switch (this.popupAlignment) {
275
+ case 'left':
276
+ $.extend(offset, { left: pos.left });
277
+ break;
278
+ case 'right':
279
+ $.extend(offset, { left: pos.right - w });
280
+ break;
281
+ }
282
+
283
+ this.popupElem.offset(offset);
284
+
285
+ if (this.elem.data('romo-dropdown-max-height') === 'detect') {
286
+ var pad = this.elem.data('romo-dropdown-max-height-detect-pad') || 10;
287
+ var contentTop = this.contentElem[0].getBoundingClientRect().top;
288
+ var contentBottom = this.contentElem[0].getBoundingClientRect().bottom;
289
+ var bodyBottom = this.bodyElem[0].getBoundingClientRect().bottom;
290
+ var padBottom = bodyBottom - contentBottom;
291
+
292
+ var maxHeight = $(window).height() - contentTop - padBottom - pad;
293
+ this.contentElem.css({'max-height': maxHeight.toString() + 'px'});
294
+ }
295
+ }
296
+
297
+ RomoDropdown.prototype.doSetPopupZIndex = function(relativeElem) {
298
+ var relativeZIndex = Romo.parseZIndex(relativeElem);
299
+ this.popupElem.css({'z-index': relativeZIndex + 1200}); // see z-index.css
300
+ }
301
+
302
+ RomoDropdown.prototype._parsePositionData = function(posString) {
303
+ var posData = (posString || '').split(',');
304
+ return { position: posData[0], alignment: posData[1] };
305
+ }
306
+
307
+ Romo.onInitUI(function(e) {
308
+ Romo.initUIElems(e, '[data-romo-dropdown-auto="true"]').romoDropdown();
309
+ });
@@ -0,0 +1,92 @@
1
+ $.fn.romoDropdownForm = function() {
2
+ return $.map(this, function(element) {
3
+ return new RomoDropdownForm(element);
4
+ });
5
+ }
6
+
7
+ var RomoDropdownForm = function(element) {
8
+ this.elem = $(element);
9
+
10
+ this.dropdown = this.elem.romoDropdown()[0];
11
+ this.doBindDropdown();
12
+
13
+ this.form = undefined;
14
+ this.elem.on('dropdownForm:form:triggerSubmit', $.proxy(function(e) {
15
+ if (this.form != undefined) {
16
+ this.form.elem.trigger('form:triggerSubmit', []);
17
+ }
18
+ }, this));
19
+ this.doBindForm();
20
+ this.elem.on('dropdown:loadBodySuccess', $.proxy(function(e, data, dropdown) {
21
+ this.doBindForm();
22
+ }, this));
23
+
24
+ this.doInit();
25
+ this.elem.trigger('dropdownForm:ready', [this]);
26
+ }
27
+
28
+ RomoDropdownForm.prototype.doInit = function() {
29
+ // override as needed
30
+ }
31
+
32
+ RomoDropdownForm.prototype.doBindDropdown = function() {
33
+ this.elem.on('dropdown:ready', $.proxy(function(e, dropdown) {
34
+ this.elem.trigger('dropdownForm:dropdown:ready', [dropdown, this]);
35
+ }, this));
36
+ this.elem.on('dropdown:toggle', $.proxy(function(e, dropdown) {
37
+ this.elem.trigger('dropdownForm:dropdown:toggle', [dropdown, this]);
38
+ }, this));
39
+ this.elem.on('dropdown:popupOpen', $.proxy(function(e, dropdown) {
40
+ this.elem.trigger('dropdownForm:dropdown:popupOpen', [dropdown, this]);
41
+ }, this));
42
+ this.elem.on('dropdown:popupClose', $.proxy(function(e, dropdown) {
43
+ this.elem.trigger('dropdownForm:dropdown:popupClose', [dropdown, this]);
44
+ }, this));
45
+ this.elem.on('dropdown:loadBodyStart', $.proxy(function(e, dropdown) {
46
+ this.elem.trigger('dropdownForm:dropdown:loadBodyStart', [dropdown, this]);
47
+ }, this));
48
+ this.elem.on('dropdown:loadBodySuccess', $.proxy(function(e, data, dropdown) {
49
+ this.elem.trigger('dropdownForm:dropdown:loadBodySuccess', [data, dropdown, this]);
50
+ }, this));
51
+ this.elem.on('dropdown:loadBodyError', $.proxy(function(e, xhr, dropdown) {
52
+ this.elem.trigger('dropdownForm:dropdown:loadBodyError', [xhr, dropdown, this]);
53
+ }, this));
54
+ this.elem.on('dropdown:dismiss', $.proxy(function(e, dropdown) {
55
+ this.elem.trigger('dropdownForm:dropdown:dismiss', [dropdown, this]);
56
+ }, this));
57
+ }
58
+
59
+ RomoDropdownForm.prototype.doBindForm = function() {
60
+ var formElem = this.dropdown.popupElem.find('[data-romo-form-auto="dropdownForm"]');
61
+
62
+ formElem.on('form:clearMsgs', $.proxy(function(e, form) {
63
+ this.elem.trigger('dropdownForm:form:clearMsgs', [form, this]);
64
+ }, this));
65
+ formElem.on('form:ready', $.proxy(function(e, form) {
66
+ this.elem.trigger('dropdownForm:form:ready', [form, this]);
67
+ }, this));
68
+ formElem.on('form:beforeSubmit', $.proxy(function(e, form) {
69
+ this.elem.trigger('dropdownForm:form:beforeSubmit', [form, this]);
70
+ }, this));
71
+ formElem.on('form:submitSuccess', $.proxy(function(e, data, form) {
72
+ this.elem.trigger('dropdownForm:form:submitSuccess', [data, form, this]);
73
+ }, this));
74
+ formElem.on('form:submitInvalidMsgs', $.proxy(function(e, msgs, xhr, form) {
75
+ this.elem.trigger('dropdownForm:form:submitInvalidMsgs', [msgs, xhr, form, this]);
76
+ }, this));
77
+ formElem.on('form:submitXhrError', $.proxy(function(e, xhr, form) {
78
+ this.elem.trigger('dropdownForm:form:submitXhrError', [xhr, form, this]);
79
+ }, this));
80
+ formElem.on('form:submitError', $.proxy(function(e, xhr, form) {
81
+ this.elem.trigger('dropdownForm:form:submitError', [xhr, form, this]);
82
+ }, this));
83
+
84
+ var submitElement = this.dropdown.popupElem.find('[data-romo-form-submit="true"]')[0];
85
+ var indicatorElements = this.dropdown.popupElem.find('[data-romo-indicator-auto="true"]');
86
+ this.form = formElem.romoForm(submitElement, indicatorElements)[0];
87
+ }
88
+
89
+ Romo.onInitUI(function(e) {
90
+ Romo.initUIElems(e, '[data-romo-dropdownForm-auto="true"]').romoDropdownForm();
91
+ });
92
+
@@ -0,0 +1,182 @@
1
+ $.fn.romoForm = function(givenSubmitElement, givenIndicatorElements) {
2
+ return $.map(this, function(element) {
3
+ return new RomoForm(element, givenSubmitElement, givenIndicatorElements);
4
+ });
5
+ }
6
+
7
+ var RomoForm = function(element, givenSubmitElement, givenIndicatorElements) {
8
+ this.elem = $(element);
9
+ this.defaultSubmitElem = this.elem.find('button[type="submit"], input[type="submit"], [data-romo-form-submit="true"]');
10
+ this.submitElem = $(givenSubmitElement || this.defaultSubmitElem);
11
+ this.defaultIndicatorElems = this.elem.find('[data-romo-indicator-auto="true"]');
12
+ this.indicatorElems = $(givenIndicatorElements || this.defaultIndicatorElems);
13
+ this.changeSubmitElems = this.elem.find('[data-romo-form-change-submit="true"]');
14
+
15
+ this.elem.on('keypress', $.proxy(this.onFormKeyPress, this));
16
+ this.defaultSubmitElem.unbind('click');
17
+ this.submitElem.unbind('click');
18
+ this.submitElem.on('click', $.proxy(this.onSubmitClick, this));
19
+ this.changeSubmitElems.on('change', $.proxy(function(e) {
20
+ this.elem.trigger('form:triggerSubmit');
21
+ }, this));
22
+ this.elem.on('form:triggerSubmit', $.proxy(this.onSubmitClick, this));
23
+
24
+ if (this.elem.data('romo-form-reload-page') === true) {
25
+ this.elem.on('form:submitSuccess', function(e, data, form) {
26
+ Romo.reloadPage();
27
+ })
28
+ }
29
+
30
+ this.defaultListValuesDelim = ',';
31
+
32
+ this.removeEmptyGetParams = this.elem.data('romo-form-remove-empty-get-params')
33
+ if (this.removeEmptyGetParams === undefined) {
34
+ this.removeEmptyGetParams = true;
35
+ }
36
+
37
+ this.decodeParams = this.elem.data('romo-form-decode-params')
38
+ if (this.decodeParams === undefined) {
39
+ this.decodeParams = true;
40
+ }
41
+
42
+ this.doInit();
43
+ this.elem.trigger('form:clearMsgs', [this]);
44
+ this.elem.trigger('form:ready', [this]);
45
+ }
46
+
47
+ RomoForm.prototype.doInit = function() {
48
+ // override as needed
49
+ }
50
+
51
+ RomoForm.prototype.onFormKeyPress = function(e) {
52
+ var target = $(e.target);
53
+
54
+ if(target.is(':not(TEXTAREA)') && e.which === 13 /* Enter */) {
55
+ e.preventDefault();
56
+ this.onSubmitClick();
57
+ }
58
+ }
59
+
60
+ RomoForm.prototype.onSubmitClick = function(e) {
61
+ if (e !== undefined) {
62
+ e.preventDefault();
63
+ }
64
+
65
+ if (this.submitElem.hasClass('disabled') === false) {
66
+ this.doSubmit();
67
+ }
68
+ }
69
+
70
+ RomoForm.prototype.doSubmit = function() {
71
+ this.elem.trigger('form:beforeSubmit', [this]);
72
+ this.indicatorElems.trigger('indicator:triggerStart');
73
+
74
+ if (this.elem.attr('method').toUpperCase() === 'GET') {
75
+ this._doGetSubmit();
76
+ } else {
77
+ this._doNonGetSubmit();
78
+ }
79
+ }
80
+
81
+ RomoForm.prototype.onSubmitSuccess = function(data, status, xhr) {
82
+ this.elem.trigger('form:clearMsgs');
83
+ this.elem.trigger('form:submitSuccess', [data, this]);
84
+ }
85
+
86
+ RomoForm.prototype.onSubmitError = function(xhr, errorType, error) {
87
+ this.elem.trigger('form:clearMsgs');
88
+
89
+ if(xhr.status === 422) {
90
+ this.elem.trigger('form:submitInvalidMsgs', [$.parseJSON(xhr.responseText), xhr, this]);
91
+ } else {
92
+ this.elem.trigger('form:submitXhrError', [xhr, this]);
93
+ }
94
+ this.elem.trigger('form:submitError', [xhr, this]);
95
+ this.indicatorElems.trigger('indicator:triggerStop');
96
+ }
97
+
98
+ RomoForm.prototype._doGetSubmit = function() {
99
+ var data = this._getSerializeObj();
100
+
101
+ if (this.elem.data('romo-form-redirect-page') === true) {
102
+ var paramString = Romo.param(data, {
103
+ removeEmpty: this.removeEmptyGetParams,
104
+ decodeValues: this.decodeParams
105
+ });
106
+ if (paramString !== '') {
107
+ Romo.redirectPage(this.elem.attr('action') + '?' + paramString);
108
+ } else {
109
+ Romo.redirectPage(this.elem.attr('action'));
110
+ }
111
+
112
+ } else {
113
+ this._doAjaxSubmit(data, true);
114
+ }
115
+ }
116
+
117
+ RomoForm.prototype._doNonGetSubmit = function() {
118
+ this._doAjaxSubmit(this._getFormData(), false);
119
+ }
120
+
121
+ RomoForm.prototype._doAjaxSubmit = function(data, process) {
122
+ $.ajax({
123
+ url: this.elem.attr('action'),
124
+ type: this.elem.attr('method'),
125
+ dataType: this._getXhrDataType(),
126
+ data: data,
127
+ processData: process,
128
+ contentType: false,
129
+ success: $.proxy(this.onSubmitSuccess, this),
130
+ error: $.proxy(this.onSubmitError, this)
131
+ });
132
+ }
133
+
134
+ RomoForm.prototype._getFormData = function() {
135
+ var formData = new FormData();
136
+
137
+ $.each(this._getSerializeObj(), function(k, v){ formData.append(k, v) });
138
+ $.each(this.elem.find('INPUT[type="file"]'), function(i, fileInput) {
139
+ var attrName = $(fileInput).attr('name')
140
+ $.each(fileInput.files, function(i, file) { formData.append(attrName, file) });
141
+ });
142
+
143
+ return formData;
144
+ }
145
+
146
+ RomoForm.prototype._getSerializeObj = function() {
147
+ var listNamesDelims = this._getListValueInputNamesDelims();
148
+
149
+ return this.elem.serializeArray().reduce(function(prev, curr) {
150
+ if (listNamesDelims[curr.name] !== undefined) {
151
+ prev[curr.name] = $.map([prev[curr.name], curr.value], function(v) {
152
+ return v; // $.map removes null/undefined vals, this acts like a compact function
153
+ }).join(listNamesDelims[curr.name])
154
+ } else {
155
+ prev[curr.name] = curr.value;
156
+ }
157
+
158
+ return prev;
159
+ }, {});
160
+ }
161
+
162
+ RomoForm.prototype._getListValueInputNamesDelims = function() {
163
+ return $.map(this.elem.find('[data-romo-form-list-values="true"]'), function(item){
164
+ return item; // converts the collection to an array
165
+ }).reduce($.proxy(function(prev, curr) {
166
+ prev[$(curr).attr('name')] = $(curr).data('romo-form-list-values-delim') || this.defaultListValuesDelim;
167
+ return prev;
168
+ }, this), {});
169
+ }
170
+
171
+ RomoForm.prototype._getXhrDataType = function() {
172
+ if(this.elem.data('romo-form-xhr-data-type') !== undefined) {
173
+ return this.elem.data('romo-form-xhr-data-type');
174
+ } else {
175
+ return 'json';
176
+ }
177
+ }
178
+
179
+ Romo.onInitUI(function(e) {
180
+ Romo.initUIElems(e, '[data-romo-form-auto="true"]').romoForm();
181
+ });
182
+
@@ -0,0 +1,88 @@
1
+ //fgnass.github.com/spin.js#v2.0.1
2
+ !function(a,b){"object"==typeof exports?module.exports=b():"function"==typeof define&&define.amd?define(b):a.Spinner=b()}(this,function(){"use strict";function a(a,b){var c,d=document.createElement(a||"div");for(c in b)d[c]=b[c];return d}function b(a){for(var b=1,c=arguments.length;c>b;b++)a.appendChild(arguments[b]);return a}function c(a,b,c,d){var e=["opacity",b,~~(100*a),c,d].join("-"),f=.01+c/d*100,g=Math.max(1-(1-a)/b*(100-f),a),h=j.substring(0,j.indexOf("Animation")).toLowerCase(),i=h&&"-"+h+"-"||"";return l[e]||(m.insertRule("@"+i+"keyframes "+e+"{0%{opacity:"+g+"}"+f+"%{opacity:"+a+"}"+(f+.01)+"%{opacity:1}"+(f+b)%100+"%{opacity:"+a+"}100%{opacity:"+g+"}}",m.cssRules.length),l[e]=1),e}function d(a,b){var c,d,e=a.style;for(b=b.charAt(0).toUpperCase()+b.slice(1),d=0;d<k.length;d++)if(c=k[d]+b,void 0!==e[c])return c;return void 0!==e[b]?b:void 0}function e(a,b){for(var c in b)a.style[d(a,c)||c]=b[c];return a}function f(a){for(var b=1;b<arguments.length;b++){var c=arguments[b];for(var d in c)void 0===a[d]&&(a[d]=c[d])}return a}function g(a,b){return"string"==typeof a?a:a[b%a.length]}function h(a){this.opts=f(a||{},h.defaults,n)}function i(){function c(b,c){return a("<"+b+' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">',c)}m.addRule(".spin-vml","behavior:url(#default#VML)"),h.prototype.lines=function(a,d){function f(){return e(c("group",{coordsize:k+" "+k,coordorigin:-j+" "+-j}),{width:k,height:k})}function h(a,h,i){b(m,b(e(f(),{rotation:360/d.lines*a+"deg",left:~~h}),b(e(c("roundrect",{arcsize:d.corners}),{width:j,height:d.width,left:d.radius,top:-d.width>>1,filter:i}),c("fill",{color:g(d.color,a),opacity:d.opacity}),c("stroke",{opacity:0}))))}var i,j=d.length+d.width,k=2*j,l=2*-(d.width+d.length)+"px",m=e(f(),{position:"absolute",top:l,left:l});if(d.shadow)for(i=1;i<=d.lines;i++)h(i,-2,"progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)");for(i=1;i<=d.lines;i++)h(i);return b(a,m)},h.prototype.opacity=function(a,b,c,d){var e=a.firstChild;d=d.shadow&&d.lines||0,e&&b+d<e.childNodes.length&&(e=e.childNodes[b+d],e=e&&e.firstChild,e=e&&e.firstChild,e&&(e.opacity=c))}}var j,k=["webkit","Moz","ms","O"],l={},m=function(){var c=a("style",{type:"text/css"});return b(document.getElementsByTagName("head")[0],c),c.sheet||c.styleSheet}(),n={lines:12,length:7,width:5,radius:10,rotate:0,corners:1,color:"#000",direction:1,speed:1,trail:100,opacity:.25,fps:20,zIndex:2e9,className:"spinner",top:"50%",left:"50%",position:"absolute"};h.defaults={},f(h.prototype,{spin:function(b){this.stop();{var c=this,d=c.opts,f=c.el=e(a(0,{className:d.className}),{position:d.position,width:0,zIndex:d.zIndex});d.radius+d.length+d.width}if(e(f,{left:d.left,top:d.top}),b&&b.insertBefore(f,b.firstChild||null),f.setAttribute("role","progressbar"),c.lines(f,c.opts),!j){var g,h=0,i=(d.lines-1)*(1-d.direction)/2,k=d.fps,l=k/d.speed,m=(1-d.opacity)/(l*d.trail/100),n=l/d.lines;!function o(){h++;for(var a=0;a<d.lines;a++)g=Math.max(1-(h+(d.lines-a)*n)%l*m,d.opacity),c.opacity(f,a*d.direction+i,g,d);c.timeout=c.el&&setTimeout(o,~~(1e3/k))}()}return c},stop:function(){var a=this.el;return a&&(clearTimeout(this.timeout),a.parentNode&&a.parentNode.removeChild(a),this.el=void 0),this},lines:function(d,f){function h(b,c){return e(a(),{position:"absolute",width:f.length+f.width+"px",height:f.width+"px",background:b,boxShadow:c,transformOrigin:"left",transform:"rotate("+~~(360/f.lines*k+f.rotate)+"deg) translate("+f.radius+"px,0)",borderRadius:(f.corners*f.width>>1)+"px"})}for(var i,k=0,l=(f.lines-1)*(1-f.direction)/2;k<f.lines;k++)i=e(a(),{position:"absolute",top:1+~(f.width/2)+"px",transform:f.hwaccel?"translate3d(0,0,0)":"",opacity:f.opacity,animation:j&&c(f.opacity,f.trail,l+k*f.direction,f.lines)+" "+1/f.speed+"s linear infinite"}),f.shadow&&b(i,e(h("#000","0 0 4px #000"),{top:"2px"})),b(d,b(i,h(g(f.color,k),"0 0 1px rgba(0,0,0,.1)")));return d},opacity:function(a,b,c){b<a.childNodes.length&&(a.childNodes[b].style.opacity=c)}});var o=e(a("group"),{behavior:"url(#default#VML)"});return!d(o,"transform")&&o.adj?i():j=d(o,"animation"),h});
3
+
4
+ $.fn.romoIndicator = function() {
5
+ return $.map(this, function(element) {
6
+ return new RomoIndicator(element);
7
+ });
8
+ }
9
+
10
+ var RomoIndicator = function(element) {
11
+ this.elem = $(element);
12
+ this.spinnerOpts = {
13
+ lines: 11, // The number of lines to draw
14
+ width: 2, // The line thickness
15
+ length: parseInt(this.elem.css('font-size')) / 2, // The length of each line
16
+ radius: parseInt(this.elem.css('font-size')) / 3, // The radius of the inner circle
17
+ corners: 1, // Corner roundness (0..1)
18
+ rotate: 0, // The rotation offset
19
+ direction: 1, // 1: clockwise, -1: counterclockwise
20
+ color: this.elem.css('color'), // #rgb or #rrggbb or array of colors
21
+ speed: 1, // Rounds per second
22
+ trail: 60, // Afterglow percentage
23
+ shadow: false, // Whether to render a shadow
24
+ hwaccel: false, // Whether to use hardware acceleration
25
+ className: 'spinner', // The CSS class to assign to the spinner
26
+ zIndex: 1000, // The z-index (defaults to 2000000000)
27
+ top: '50%', // Top position relative to parent
28
+ left: '50%'// Left position relative to parent
29
+ };
30
+
31
+ this.doInit();
32
+ this.spinner = new Spinner(this.spinnerOpts);
33
+
34
+ this.elemHtml = this.elem.html();
35
+ this.elem.css({
36
+ 'position': 'relative',
37
+ 'width': this.elem.css('width'),
38
+ 'height': this.elem.css('height'),
39
+ });
40
+ this.elem.on('indicator:triggerStart', $.proxy(this.onStart, this));
41
+ this.elem.on('indicator:triggerStop', $.proxy(this.onStop, this));
42
+
43
+ $(window).on("pageshow", $.proxy(function(e) {
44
+ this.elem.trigger('indicator:triggerStop');
45
+ }, this));
46
+
47
+ this.elem.trigger('indicator:ready', [this]);
48
+ }
49
+
50
+ RomoIndicator.prototype.doInit = function() {
51
+ // override as needed
52
+ }
53
+
54
+ RomoIndicator.prototype.onStart = function(e) {
55
+ if (e !== undefined) {
56
+ e.preventDefault();
57
+ }
58
+
59
+ if (this.elem.hasClass('disabled') === false) {
60
+ this.doStart();
61
+ }
62
+ }
63
+
64
+ RomoIndicator.prototype.onStop = function(e) {
65
+ if (e !== undefined) {
66
+ e.preventDefault();
67
+ }
68
+
69
+ if (this.elem.hasClass('disabled') === false) {
70
+ this.doStop();
71
+ }
72
+ }
73
+
74
+ RomoIndicator.prototype.doStart = function() {
75
+ this.elem.html('');
76
+ this.spinner.spin(this.elem[0]);
77
+ this.elem.trigger('indicator:start', [this]);
78
+ }
79
+
80
+ RomoIndicator.prototype.doStop = function() {
81
+ this.spinner.stop();
82
+ this.elem.html(this.elemHtml);
83
+ this.elem.trigger('indicator:stop', [this]);
84
+ }
85
+
86
+ Romo.onInitUI(function(e) {
87
+ Romo.initUIElems(e, '[data-romo-indicator-auto="true"]').romoIndicator();
88
+ });