formize 0.0.12 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.12
1
+ 0.0.13
@@ -1,149 +1,137 @@
1
1
  /*jslint devel: true, browser: true, sloppy: true, vars: true, white: true, maxerr: 50, indent: 2 */
2
2
 
3
- var Formize = {
4
- uniqueID: function() {
5
- var uid = 'u'+((new Date()).getTime() + "" + Math.floor(Math.random() * 1000000)).substr(0, 18);
6
- return uid;
7
- }
8
- };
9
-
10
- Formize.Overlay = {
11
-
12
- count: 0,
13
-
14
- add: function (body) {
15
- var overlay = $('#overlay')[0];
16
- if (overlay === null || overlay === undefined) {
17
- overlay = $(document.createElement('div'));
18
- overlay.attr({id: 'overlay', style: 'position:fixed; top:0; left: 0; display: none'});
19
- $('body').append(overlay);
20
- this.resize();
21
- overlay.fadeIn('fast');
22
- }
23
- this.count += 1;
24
- overlay.css('z-index', this.zIndex());
25
- return overlay;
26
- },
27
-
28
- resize: function () {
29
- var height = $(document).height(), width = $(document).width();
30
- var overlay = $('#overlay');
31
- overlay.css({width: width+'px', height: height+'px'});
32
- },
33
-
34
- remove: function() {
35
- this.count -= 1;
36
- var overlay = $('#overlay');
37
- if (overlay !== null) {
38
- if (this.count <= 0) {
39
- overlay.fadeOut(400, function() { $(this).remove(); });
40
- } else {
41
- overlay.css('z-index', this.zIndex());
42
- }
3
+ (function ($) {
4
+ // Replaces `$(selector).live("ready", handler)` which don't work
5
+ // It rebinds automatically after each ajax request all not-binded.
6
+ $.behaviours = [];
7
+ $.behave = function (selector, eventType, handler) {
8
+ if (eventType == "load") {
9
+ $(document).ready(function(event) {
10
+ $(selector).each(handler);
11
+ $(selector).prop('alreadyBound', true);
12
+ });
13
+ $.behaviours.push({selector: selector, handler: handler});
14
+ } else {
15
+ $(selector).live(eventType, handler);
43
16
  }
44
- return this.count;
45
- },
46
-
47
- // Computes a big z-index with interval in order to intercalate dialogs
48
- zIndex: function() {
49
- return (10*this.count + 10000);
50
17
  }
51
- };
52
-
53
-
18
+ // Rebinds unbound elements on each ajax request.
19
+ $(document).ajaxStop(function () {
20
+ for (var behaviour in $.behaviours) {
21
+ behaviour = $.behaviours[behaviour];
22
+ $(behaviour.selector).each(function(index, element){
23
+ if ($(element).prop('alreadyBound') !== true) {
24
+ behaviour.handler.call($(element));
25
+ $(element).prop('alreadyBound', true);
26
+ }
27
+ });
28
+ }
29
+ });
54
30
 
55
- Formize.Dialog = {
56
31
 
57
- // Opens a div like a virtual popup
58
- open: function (url, updated, ratio) {
59
- var height = $(document).height(), width = $(document).width();
60
- var dialog_id = 'dialog'+Formize.Overlay.count;
61
- if (isNaN(ratio)) { ratio = 0.6; }
62
-
63
- Formize.Overlay.add();
32
+ $.ajaxDialogCount = 0;
64
33
 
34
+ $.ajaxDialog = function (url, settings) {
35
+ var frame_id = "dialog-" + $.ajaxDialogCount, width = $(document).width();
36
+ var defaultSettings = {
37
+ header: "X-Return-Code"
38
+ };
39
+ if (settings === null || settings === undefined) { settings = {}; }
40
+ settings = $.extend({}, defaultSettings, settings);
65
41
  $.ajax(url, {
66
- data: {dialog: dialog_id},
42
+ data: {dialog: frame_id},
67
43
  success: function(data, textStatus, jqXHR) {
68
- var dialog = $(document.createElement('div'));
69
- dialog.attr({id: dialog_id, 'data-ratio': ratio, 'data-dialog-update': updated, flex: '1', 'class': 'dialog', style: 'z-index:'+(Formize.Overlay.zIndex()+1)+'; position:fixed; display: none;'});
70
- $('body').append(dialog);
71
- dialog.html(data);
72
- Formize.Dialog.resize(dialog);
73
- dialog.fadeIn(400, function() {
74
- $(document).trigger("dom:update", dialog.attr('id'));
75
- });
44
+ var frame = $(document.createElement('div')), h1, title=null;
45
+ frame.attr({id: frame_id, 'class': 'dialog ajax-dialog', style: 'display:none;'});
46
+ $('body').append(frame);
47
+ frame.html(data);
48
+ frame.prop("dialogSettings", settings);
49
+ frame.dialog({
50
+ autoOpen: false,
51
+ show: 'fade',
52
+ modal: true,
53
+ width: (width > 1024 ? width *0.6 : width*0.9),
54
+ minWidth: (width*0.3 < 150 ? 150 : width*0.3)
55
+ });
56
+ $.ajaxDialogInitialize(frame);
57
+ frame.dialog("open");
76
58
  },
77
59
  error: function(jqXHR, textStatus, errorThrown) {
78
60
  alert("FAILURE (Error "+textStatus+"): "+errorThrown);
79
- Formize.Overlay.remove();
61
+ var frame = $("#" + frame_id);
62
+ frame.dialog("close");
63
+ frame.remove();
80
64
  }
81
65
  });
82
- },
83
-
84
- resize: function (dialog) {
85
- var width = $(window).width(), height = $(window).height();
86
- var ratio = parseFloat(dialog.attr('data-ratio'));
87
- var w = dialog.width();
88
- var h = dialog.height();
89
- if (ratio > 0) {
90
- w = ratio*width;
91
- h = ratio*height;
66
+ $.ajaxDialogCount += 1;
67
+ };
68
+
69
+ $.ajaxDialogInitialize = function(frame) {
70
+ var frame_id = frame.attr("id");
71
+ var title = frame.prop("dialogSettings")["title"];
72
+ if (title === null || title === undefined) {
73
+ var h1 = $("#" + frame_id + " h1");
74
+ if (h1[0] !== null && h1[0] !== undefined) {
75
+ title = h1.text()
76
+ h1.remove();
77
+ }
92
78
  }
93
- dialog.animate({left: ((width-w)/2)+'px', top: ((height-h)/2)+'px', width: w+'px', height: h+'px'});
94
- return true;
95
- },
96
-
97
- // Close a virtual popup
98
- close: function(dialog) {
99
- dialog = $('#'+dialog);
100
- dialog.fadeOut(400, function() { $(this).remove(); });
101
- Formize.Overlay.remove();
102
- return true;
103
- },
104
-
105
- submitForm: function(form) {
106
- var form = $(this);
107
- var dialog_id = form.attr('data-dialog');
108
- var dialog = $('#'+dialog_id);
109
-
79
+ frame.dialog("option", "title", title);
80
+
81
+ $("#" + frame_id + " form").each(function (index, form) {
82
+ $(form).attr('data-dialog', frame_id);
83
+ });
84
+ };
85
+
86
+ $.submitAjaxForm = function () {
87
+ var form = $(this);
88
+ var frame_id = form.attr('data-dialog');
89
+ var frame = $('#'+frame_id);
90
+ var settings = frame.prop("dialogSettings");
91
+
110
92
  var field = $(document.createElement('input'));
111
- field.attr({ type: 'hidden', name: 'dialog', value: dialog_id });
93
+ field.attr({ type: 'hidden', name: 'dialog', value: frame_id });
112
94
  form.append(field);
113
95
 
114
- $.ajax(form.attr('action'), {
115
- type: form.attr('method') || 'POST',
116
- data: form.serialize(),
117
- success: function(data, textStatus, request) {
118
- var record_id = request.getResponseHeader("X-Saved-Record-Id");
119
- if (record_id === null) {
120
- // No return => validation error
121
- dialog.html(request.responseText);
122
- $(document).trigger("dom:update", dialog.attr('id'));
123
- } else {
124
- // Refresh element with its refresh URL
125
- var updated_id = '#'+dialog.attr('data-dialog-update'), updated = $(updated_id);
126
- if (updated[0] !== undefined) {
127
- var url = updated.attr('data-refresh');
128
- $.ajax(url, {
129
- data: {selected: record_id},
130
- success: function(data2, textStatus2, request2) {
131
- updated.replaceWith(request2.responseText);
132
- $(document).trigger("dom:update");
133
- $(updated_id+' input').trigger("emulated:change");
134
- }
135
- });
96
+ $.ajax(form.attr('action'), {
97
+ type: form.attr('method') || 'POST',
98
+ data: form.serialize(),
99
+ success: function(data, textStatus, request) {
100
+ var returnCode = request.getResponseHeader(settings["header"])
101
+ var returns = settings["returns"], unknownReturnCode = true;
102
+ for (var code in returns) {
103
+ if (returnCode == code && $.isFunction(returns[code])) {
104
+ returns[code].call(form, frame, data, textStatus, request);
105
+ unknownReturnCode = false;
106
+ $.ajaxDialogInitialize(frame);
107
+ break;
136
108
  }
137
- // Close dialog
138
- Formize.Dialog.close(dialog_id);
139
109
  }
140
- }
141
- });
110
+ if (unknownReturnCode) {
111
+ if ($.isFunction(settings["defaultReturn"])) {
112
+ settings["defaultReturn"].call(form, frame);
113
+ } else {
114
+ alert("FAILURE (Unknown return code for header " + settings["header"] + "): " + returnCode);
115
+ }
116
+ }
117
+ },
118
+ error: function(jqXHR, textStatus, errorThrown) {
119
+ alert("FAILURE (Error "+textStatus+"): "+errorThrown);
120
+ var frame = $("#" + frame_id);
121
+ frame.dialog("close");
122
+ frame.remove();
123
+ // if ($.isFunction(settings["error"])) { settings["error"].call(form, frame, jqXHR, textStatus, errorThrown); }
124
+ }
125
+ });
142
126
  return false;
143
- }
127
+ };
128
+
129
+ // Submits dialog forms
130
+ $.behave(".ajax-dialog form[data-dialog]", "submit", $.submitAjaxForm);
144
131
 
145
- };
132
+ })(jQuery);
146
133
 
134
+ var Formize = {};
147
135
 
148
136
  Formize.refreshDependents = function (event) {
149
137
  var element = $(this);
@@ -158,12 +146,10 @@ Formize.refreshDependents = function (event) {
158
146
  $.ajax(url, {
159
147
  data: params,
160
148
  success: function(data, textStatus, response) {
161
- // alert("Success: "+response.responseText);
162
149
  $(item).replaceWith(response.responseText);
163
- $(document).trigger("dom:update");
164
150
  },
165
151
  error: function(jqXHR, textStatus, errorThrown) {
166
- alert("FAILURE (Error "+textStatus+"): "+errorThrown);
152
+ alert("FAILURE (Error "+textStatus+"): "+errorThrown);
167
153
  }
168
154
  });
169
155
  }
@@ -176,88 +162,55 @@ Formize.refreshDependents = function (event) {
176
162
  Formize.Toggles = {
177
163
 
178
164
  ifChecked: function () {
179
- if (this.checked) {
180
- $($(this).attr('data-show')).slideDown();
181
- $($(this).attr('data-hide')).slideUp();
182
- } else {
183
- $($(this).attr('data-show')).slideUp();
184
- $($(this).attr('data-hide')).slideDown();
185
- }
165
+ if (this.checked) {
166
+ $($(this).attr('data-show')).slideDown();
167
+ $($(this).attr('data-hide')).slideUp();
168
+ } else {
169
+ $($(this).attr('data-show')).slideUp();
170
+ $($(this).attr('data-hide')).slideDown();
171
+ }
186
172
  }
187
173
 
188
174
  }
189
175
 
190
176
 
191
- /**
192
- * Special method which is a sharthand to bind every element
193
- * concerned by the selector now and in the future. It correspond
194
- * to a lack of functionnality of jQuery on 'load' events.
195
- */
196
- $.rebindeds = [];
197
- function behave(selector, eventType, handler) {
198
- if (eventType == "load") {
199
- $(document).ready(function(event) {
200
- $(selector).each(handler);
201
- $(selector).attr('data-already-bound', 'true');
202
- });
203
- $.rebindeds.push({selector: selector, handler:handler});
204
- } else {
205
- $(selector).live(eventType, handler);
206
- }
207
- }
208
-
209
- // Rebinds unbound elements on DOM updates.
210
- $(document).bind('dom:update', function(event, element_id) {
211
- var rebinded;
212
- for (var i=0; i<$.rebindeds.length; i++) {
213
- rebinded = $.rebindeds[i];
214
- $(rebinded.selector).each(function(x, element){
215
- if ($(element).attr('data-already-bound') !== 'true') {
216
- rebinded.handler.call($(element));
217
- $(element).attr('data-already-bound', 'true');
218
- }
219
- });
220
- }
221
- });
222
-
223
177
 
224
178
  // Initializes unroll inputs
225
- behave('input[data-unroll]', 'load', function() {
179
+ $.behave('input[data-unroll]', 'load', function() {
226
180
  var element = $(this), choices, paramName;
227
181
 
228
182
  element.unrollCache = element.val();
229
183
  element.autocompleteType = "text";
230
184
  element.valueField = $('#'+element.attr('data-value-container'))[0];
231
185
  if ($.isEmptyObject(element.valueField)) {
232
- alert('An input '+element.id+' with a "data-unroll" attribute must contain a "data-value-container" attribute');
233
- element.autocompleteType = "id";
186
+ alert('An input '+element.id+' with a "data-unroll" attribute must contain a "data-value-container" attribute');
187
+ element.autocompleteType = "id";
234
188
  }
235
189
  element.maxResize = parseInt(element.attr('data-max-resize'));
236
190
  if (isNaN(element.maxResize) || element.maxResize === 0) { element.maxResize = 64; }
237
191
  element.size = (element.unrollCache.length < 32 ? 32 : element.unrollCache.length > element.maxResize ? element.maxResize : element.unrollCache.length);
238
192
 
239
193
  element.autocomplete({
240
- source: element.attr('data-unroll'),
241
- minLength: 1,
242
- select: function(event, ui) {
243
- var selected = ui.item;
244
- element.valueField.value = selected.id;
245
- element.unrollCache = selected.label;
246
- element.attr("size", (element.unrollCache.length < 32 ? 32 : element.unrollCache.length > element.maxResize ? element.maxResize : element.unrollCache.length));
247
- $(element.valueField).trigger("emulated:change");
248
- return true;
249
- }
194
+ source: element.attr('data-unroll'),
195
+ minLength: 1,
196
+ select: function(event, ui) {
197
+ var selected = ui.item;
198
+ element.valueField.value = selected.id;
199
+ element.unrollCache = selected.label;
200
+ element.attr("size", (element.unrollCache.length < 32 ? 32 : element.unrollCache.length > element.maxResize ? element.maxResize : element.unrollCache.length));
201
+ $(element.valueField).trigger("emulated:change");
202
+ return true;
203
+ }
250
204
  });
251
205
  });
252
206
 
253
-
254
207
  // Initializes date fields
255
- behave('input[data-datepicker]', "load", function() {
208
+ $.behave('input[data-datepicker]', "load", function() {
256
209
  var element = $(this);
257
210
  var locale = element.attr("data-locale");
258
211
  var options = $.datepicker.regional[locale];
259
212
  if (element.attr("data-date-format") !== null) {
260
- options['dateFormat'] = element.attr("data-date-format");
213
+ options['dateFormat'] = element.attr("data-date-format");
261
214
  }
262
215
  options['altField'] = '#'+element.attr("data-datepicker");
263
216
  options['altFormat'] = 'yy-mm-dd';
@@ -267,49 +220,51 @@ behave('input[data-datepicker]', "load", function() {
267
220
 
268
221
  // Initializes resizable text areas
269
222
  // Minimal size is defined on default size of the area
270
- behave('textarea[data-resizable]', "load", function() {
223
+ $.behave('textarea[data-resizable]', "load", function() {
271
224
  var element = $(this);
272
225
  element.resizable({
273
- handles: "se",
274
- minHeight: element.height(),
275
- minWidth: element.width(),
276
- create: function (event, ui) { $(this).css("padding-bottom", "0px"); },
277
- stop: function (event, ui) { $(this).css("padding-bottom", "0px"); }
226
+ handles: "se",
227
+ minHeight: element.height(),
228
+ minWidth: element.width(),
229
+ create: function (event, ui) { $(this).css("padding-bottom", "0px"); },
230
+ stop: function (event, ui) { $(this).css("padding-bottom", "0px"); }
278
231
  });
279
232
  });
280
233
 
281
- // Opens a dialog for a ressource creation
282
- behave("a[data-add-item]", "click", function() {
234
+ // Opens a dialog for a resource creation
235
+ $.behave("a[data-add-item]", "click", function() {
283
236
  var element = $(this);
284
- var list_id = element.attr('data-add-item');
237
+ var list_id = '#'+element.attr('data-add-item'), list = $(list_id);
285
238
  var url = element.attr('href');
286
- Formize.Dialog.open(url, list_id);
287
- return false;
288
- });
289
-
290
- // Closes a dialog
291
- behave("a[data-close-dialog]", "click", function() {
292
- var dialog_id = element.attr('data-close-dialog');
293
- Formize.Dialog.close(dialog_id);
239
+ $.ajaxDialog(url, {
240
+ returns: {
241
+ success: function (frame, data, textStatus, request) {
242
+ var record_id = request.getResponseHeader("X-Saved-Record-Id");
243
+ if (list[0] !== undefined) {
244
+ $.ajax(list.attr('data-refresh'), {
245
+ data: {selected: record_id},
246
+ success: function(data, textStatus, request) {
247
+ list.replaceWith(request.responseText);
248
+ $(list_id + ' input').trigger("emulated:change");
249
+ }
250
+ });
251
+ }
252
+ frame.dialog("close");
253
+ },
254
+ invalid: function (frame, data, textStatus, request) {
255
+ frame.html(request.responseText);
256
+ }
257
+ },
258
+ });
294
259
  return false;
295
260
  });
296
261
 
297
- // Submits dialog forms
298
- behave("form[data-dialog]", "submit", Formize.Dialog.submitForm);
299
-
300
262
  // Refresh dependents on changes
301
- behave("*[data-dependents]", "change", Formize.refreshDependents);
302
- behave("*[data-dependents]", "emulated:change", Formize.refreshDependents);
263
+ $.behave("*[data-dependents]", "change", Formize.refreshDependents);
264
+ $.behave("*[data-dependents]", "emulated:change", Formize.refreshDependents);
303
265
  // Compensate for changes made with keyboard
304
- behave("select[data-dependents]", "keypress", Formize.refreshDependents);
266
+ $.behave("select[data-dependents]", "keypress", Formize.refreshDependents);
305
267
 
306
- behave("input[data-show], input[data-hide]", "load", Formize.Toggles.ifChecked);
307
- behave("input[data-show], input[data-hide]", "change", Formize.Toggles.ifChecked);
308
-
309
- // Resizes the overlay automatically
310
- $(window).resize(function() {
311
- Formize.Overlay.resize();
312
- $('.dialog').each(function(i, dialog) {
313
- Formize.Dialog.resize($(dialog));
314
- });
315
- });
268
+ // Hide/show blocks depending on check boxes
269
+ $.behave("input[data-show], input[data-hide]", "load", Formize.Toggles.ifChecked);
270
+ $.behave("input[data-show], input[data-hide]", "change", Formize.Toggles.ifChecked);
@@ -5,6 +5,30 @@ module Formize
5
5
  def self.included(base)
6
6
  base.extend(ClassMethods)
7
7
  end
8
+
9
+
10
+ # Adds method to provides a default response for create/update actions
11
+ # It saves the record/resource and return response with good status and headers
12
+ def save_and_respond(resource, options={}, &block)
13
+ creation = resource.new_record?
14
+ resource.attributes = options[:attributes] unless options[:attributes].nil?
15
+ respond_to do |format|
16
+ # if ((block_given? and block.arity == 1) ? yield(resource) : (block_given? and block.arity == 2) ? yield(resource, format) : resource.save)
17
+ if (block_given? ? yield(resource, format) : resource.save)
18
+ status = (creation ? :created : :ok)
19
+ response.headers["X-Return-Code"] = "success"
20
+ response.headers["X-Saved-Record-Id"] = resource.id.to_s
21
+ format.html { params[:dialog] ? head(status) : redirect_to(options[:url] || resource) }
22
+ format.xml { render :xml => resource, :status => status, :location => resource }
23
+ else
24
+ response.headers["X-Return-Code"] = "invalid"
25
+ format.html { render :action => (resource.new_record? ? "new" : "edit")}
26
+ format.xml { render :xml => resource.errors, :status => :unprocessable_entity }
27
+ end
28
+ end
29
+ end
30
+
31
+
8
32
 
9
33
  module ClassMethods
10
34
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: formize
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.12
4
+ version: 0.0.13
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-09-12 00:00:00.000000000Z
12
+ date: 2011-09-14 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
16
- requirement: &8400960 !ruby/object:Gem::Requirement
16
+ requirement: &13456920 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '3'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *8400960
24
+ version_requirements: *13456920
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: jquery-rails
27
- requirement: &8376420 !ruby/object:Gem::Requirement
27
+ requirement: &13455880 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *8376420
35
+ version_requirements: *13455880
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: fastercsv
38
- requirement: &8373140 !ruby/object:Gem::Requirement
38
+ requirement: &13454620 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *8373140
46
+ version_requirements: *13454620
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: jeweler
49
- requirement: &8354560 !ruby/object:Gem::Requirement
49
+ requirement: &13453320 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 1.6.4
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *8354560
57
+ version_requirements: *13453320
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: rcov
60
- requirement: &8349740 !ruby/object:Gem::Requirement
60
+ requirement: &13398260 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *8349740
68
+ version_requirements: *13398260
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rdoc
71
- requirement: &8346560 !ruby/object:Gem::Requirement
71
+ requirement: &13396340 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,7 +76,7 @@ dependencies:
76
76
  version: 2.4.2
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *8346560
79
+ version_requirements: *13396340
80
80
  description: Like simple_form or formtastic, it aims to handle easily forms but taking
81
81
  in account AJAX and HTML5 on depending fields mainly.
82
82
  email: brice.texier@ekylibre.org