cck_forms 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -0
  3. data/README.md +1 -0
  4. data/Rakefile +40 -0
  5. data/lib/cck_forms/date_time.rb +58 -0
  6. data/lib/cck_forms/engine.rb +23 -0
  7. data/lib/cck_forms/form_builder_extensions.rb +18 -0
  8. data/lib/cck_forms/parameter_type_class/album.rb +100 -0
  9. data/lib/cck_forms/parameter_type_class/base.rb +275 -0
  10. data/lib/cck_forms/parameter_type_class/boolean.rb +24 -0
  11. data/lib/cck_forms/parameter_type_class/checkboxes.rb +202 -0
  12. data/lib/cck_forms/parameter_type_class/date.rb +35 -0
  13. data/lib/cck_forms/parameter_type_class/date_range.rb +53 -0
  14. data/lib/cck_forms/parameter_type_class/date_time.rb +76 -0
  15. data/lib/cck_forms/parameter_type_class/enum.rb +53 -0
  16. data/lib/cck_forms/parameter_type_class/file.rb +80 -0
  17. data/lib/cck_forms/parameter_type_class/float.rb +20 -0
  18. data/lib/cck_forms/parameter_type_class/image.rb +20 -0
  19. data/lib/cck_forms/parameter_type_class/integer.rb +77 -0
  20. data/lib/cck_forms/parameter_type_class/integer_range.rb +150 -0
  21. data/lib/cck_forms/parameter_type_class/map.rb +259 -0
  22. data/lib/cck_forms/parameter_type_class/phones.rb +204 -0
  23. data/lib/cck_forms/parameter_type_class/string.rb +13 -0
  24. data/lib/cck_forms/parameter_type_class/string_collection.rb +23 -0
  25. data/lib/cck_forms/parameter_type_class/text.rb +18 -0
  26. data/lib/cck_forms/parameter_type_class/time.rb +30 -0
  27. data/lib/cck_forms/parameter_type_class/work_hours.rb +369 -0
  28. data/lib/cck_forms/version.rb +3 -0
  29. data/lib/cck_forms.rb +4 -0
  30. data/vendor/assets/javascripts/cck_forms/jquery.workhours.js +399 -0
  31. data/vendor/assets/javascripts/cck_forms/map.js.coffee +322 -0
  32. metadata +116 -0
@@ -0,0 +1,399 @@
1
+ $(function() {
2
+
3
+ /**
4
+ * Виджет-помогайка для установки и получения значения из ХТМЛ-объекта 1 дня или группы (поля у низ почти одинаковые).
5
+ * При установке значения событи change не вызывается!
6
+ */
7
+ $.widget("cck.workhoursday", {
8
+
9
+ options: {
10
+
11
+ },
12
+
13
+ _$day: null,
14
+ _$open24Hours: null,
15
+ _$openUntilLastClient: null,
16
+ _$openTimeH: null,
17
+ _$openTimeM: null,
18
+ _$closeTimeH: null,
19
+ _$closeTimeM: null,
20
+
21
+ _create: function() {
22
+ var $form = this.element;
23
+ this._$day = $form.find('input[name$="[day]"]');
24
+ this._$open24Hours = $form.find('input:checkbox[name$="[open_24_hours]"]');
25
+ this._$openUntilLastClient = $form.find('input:checkbox[name$="[open_until_last_client]"]');
26
+ this._$openTimeH = $form.find('select[name$="[open_time][hours]"]');
27
+ this._$openTimeM = $form.find('select[name$="[open_time][minutes]"]');
28
+ this._$closeTimeH = $form.find('select[name$="[close_time][hours]"]');
29
+ this._$closeTimeM = $form.find('select[name$="[close_time][minutes]"]');
30
+ },
31
+
32
+ /**
33
+ * Получить название дня (mod, tue).
34
+ */
35
+ day: function() {
36
+ return this._$day.val();
37
+ },
38
+
39
+ /**
40
+ * Получить или установить галку "круглосуточно".
41
+ */
42
+ open24Hours: function(value) {
43
+ if(value !== undefined) {
44
+ this._$open24Hours.prop("checked", value);
45
+ } else {
46
+ return !!this._$open24Hours.prop("checked");
47
+ }
48
+ },
49
+
50
+ /**
51
+ * Получить или установить галку "до последнего клиента".
52
+ */
53
+ openUntilLastClient: function(value) {
54
+ if(value !== undefined) {
55
+ this._$openUntilLastClient.prop("checked", value);
56
+ } else {
57
+ return !!this._$openUntilLastClient.prop("checked");
58
+ }
59
+ },
60
+
61
+ /**
62
+ * Преобразовать значения переданных инпутов в строку вида "20:35". Если не получится, или одно из значений пустое,
63
+ * вернет null.
64
+ */
65
+ inputsToTime: function($hoursInput, $minutesInput) {
66
+ var h = $hoursInput.val() == '' ? null : $hoursInput.val() * 1;
67
+ var m = $minutesInput.val() == '' ? null : $minutesInput.val() * 1;
68
+
69
+ if(h === null || m === null) {
70
+ return null;
71
+ }
72
+
73
+ return h + ":" + m;
74
+ },
75
+
76
+ /**
77
+ * Преобразовать хэш вида {hours: 20, minutes: 5} в значения переданных инпутов. Можно по одному.
78
+ */
79
+ timeToInputs: function(value, $hoursInput, $minutesInput) {
80
+ if(value.hours !== undefined) {
81
+ $hoursInput.val(value.hours);
82
+ }
83
+
84
+ if(value.minutes !== undefined) {
85
+ $minutesInput.val(value.minutes);
86
+ }
87
+ },
88
+
89
+ /**
90
+ * Вернуть (строка) или установить (из хэша) значения времени открытия.
91
+ */
92
+ openTime: function(value) {
93
+ if(value !== undefined) {
94
+ this.timeToInputs(value, this._$openTimeH, this._$openTimeM);
95
+ } else {
96
+ return this.inputsToTime(this._$openTimeH, this._$openTimeM);
97
+ }
98
+ },
99
+
100
+ /**
101
+ * Вернуть (строка) или установить (из хэша) значения времени закрытия.
102
+ */
103
+ closeTime: function(value) {
104
+ if(value !== undefined) {
105
+ this.timeToInputs(value, this._$closeTimeH, this._$closeTimeM);
106
+ } else {
107
+ return this.inputsToTime(this._$closeTimeH, this._$closeTimeM);
108
+ }
109
+ },
110
+
111
+ /**
112
+ * Пустой ли день? Пустой тогда, когда не выбрано времени и чекбоксы не включены.
113
+ */
114
+ isEmpty: function() {
115
+ return !this.open24Hours() && !this.openUntilLastClient() && !this.openTime() && !this.closeTime();
116
+ },
117
+
118
+ equalTo: function($other) {
119
+ return (
120
+ this.open24Hours() == $other.workhoursday("open24Hours") &&
121
+ this.openUntilLastClient() == $other.workhoursday("openUntilLastClient") &&
122
+ this.openTime() == $other.workhoursday("openTime") &&
123
+ this.closeTime() == $other.workhoursday("closeTime")
124
+ );
125
+ },
126
+
127
+ /**
128
+ * Вернет или установит текущее значение скопом. Через массив, порядок элементов: "круглосуточно", "до послед.
129
+ * клиента", "время открытия", "время закрытия". Время открытия и закрытия устанавливается через строку "ЧЧ:ММ",
130
+ * в отличие от метода closeTime({hours: ..., minutes: ...}).
131
+ */
132
+ value: function(value) {
133
+ if(value) {
134
+
135
+ this._$open24Hours.prop("checked", !!value[0]);
136
+ this._$openUntilLastClient.prop("checked", !!value[1]);
137
+ this._$openTimeH.val(value[2] ? value[2].split(":")[0].replace(/^0(.)$/, '$1') : '');
138
+ this._$openTimeM.val(value[2] ? value[2].split(":")[1].replace(/^0(.)$/, '$1') : '');
139
+ this._$closeTimeH.val(value[3] ? value[3].split(":")[0].replace(/^0(.)$/, '$1') : '');
140
+ this._$closeTimeM.val(value[3] ? value[3].split(":")[1].replace(/^0(.)$/, '$1') : '');
141
+
142
+ return this.element;
143
+
144
+ } else {
145
+
146
+ return [
147
+ this.open24Hours(),
148
+ this.openUntilLastClient(),
149
+ this.openTime(),
150
+ this.closeTime()
151
+ ];
152
+ }
153
+ },
154
+
155
+ slashzero: null
156
+
157
+ });
158
+
159
+
160
+
161
+ // последний использованный ID группы
162
+ var cckWorkhoursGroupId = 0;
163
+
164
+ /**
165
+ * Виджет блока "режим работы". Навешивается на весь ДИВ со строками для каждого дня недели, прячет их и подменяет
166
+ * сгруппированными строками, когда на несколько дней (определяется включенными галочками) идет один режим работы:
167
+ *
168
+ * > [^] Пн [^] Вт [^] Ср [^] Чт [^] Пт [_] Сб [_] Вс
169
+ * > c [09]:[00] по [20]:[00] [_] круглосуточно [_] до последнего клиента
170
+ *
171
+ * > [_] Пн [_] Вт [_] Ср [_] Чт [_] Пт [^] Сб [^] Вс
172
+ * > c [10]:[00] по [__]:[__] [_] круглосуточно [^] до последнего клиента
173
+ *
174
+ * [ Добавить дней ]
175
+ *
176
+ * Ссылка "Добавить дней" добавляет еще одну пусту группу. При указании в группе дня, который уже есть в другой,
177
+ * оттуда он удаляется. Если группа опустела (нет дней), она удаляется.
178
+ *
179
+ * Реально данные из группы при всех манипуляциях переносятся в спрятанные строки, так что для бэкенда ничего
180
+ * не меняется - профит!
181
+ */
182
+ $.widget("cck.workhours", {
183
+
184
+ options: {
185
+
186
+ },
187
+
188
+ _days: [],
189
+ _$template: null,
190
+
191
+ /**
192
+ * Инициализируем виджет.
193
+ */
194
+ _create: function() {
195
+ var $form = this.element;
196
+ var $widget = this;
197
+
198
+ var $addGroupLink = $('<a href="#add-group">Добавить дней</a>').click(function() {
199
+ $widget.createGroup([], []);
200
+ });
201
+
202
+ this._lastP = $('<p></p>').appendTo($form).append($addGroupLink);
203
+
204
+ this._$template = $form.find(".form_work_hours_day_template");
205
+
206
+ var groups = [];
207
+ $widget._days = [];
208
+
209
+ // существующие дни распихаем по группам и спрячем
210
+ $form.children(".form_work_hours_day:not(.form_work_hours_day_template)").each(function() {
211
+ var $day = $(this).workhoursday().hide();
212
+ var dayName = $day.workhoursday("day");
213
+ var value = $day.workhoursday("value");
214
+ var valueHash = value.join("/");
215
+
216
+ $widget._days[dayName] = $day;
217
+
218
+ if(!groups[valueHash]) {
219
+ groups[valueHash] = {days: [], value: value};
220
+ }
221
+
222
+ groups[valueHash].days.push(dayName);
223
+ });
224
+
225
+ for(var i in groups) {
226
+ if (groups.hasOwnProperty(i)) {
227
+ $widget.createGroup(groups[i].days, groups[i].value);
228
+ }
229
+ }
230
+ },
231
+
232
+ /**
233
+ * Создаем новый ДОМ-элемент "группы" дней из шаблона. Навешиваем нужных слушателей.
234
+ */
235
+ createGroup: function(days, value) {
236
+
237
+ // сделаем ХТМЛ новой группы, заменив id|name ~= template на какой-то новый ID
238
+ var newHtml = this._$template[0].outerHTML.replace(/((?:id|name)="[^"]*)template([^"]*")/g, "$1" + this.newGroupId() + "$2");
239
+ var $newGroup = $(newHtml);
240
+
241
+ // отметим чекбоксы, соотв. выбранным дням
242
+ for(var i = 0, ic = days.length; i < ic; ++ i) {
243
+ $newGroup.find("input[name$=\"[days]\"][value=" + days[i] + "]").prop("checked", true).closest("li").addClass("active");
244
+ }
245
+
246
+ // спрячем чекбоксы, вместо них будут ссылко-кнопки
247
+ $newGroup.find("input[name$=\"[days]\"]").hide();
248
+ $newGroup.find(".nav-pills").on("click", "a", function(event) {
249
+ // событие могло произойти на чекбоксе внутри этой ссылки, это нам не нужно
250
+ if(event.target == this) {
251
+ $(this).children("input:checkbox").click();
252
+ return false;
253
+ }
254
+ });
255
+
256
+ // будем слушать изменения в формах, чтобы транслировать в соотв. группе скрытые поля дней
257
+ var $widget = this;
258
+ $newGroup.on("change", "input, select", function() {
259
+ $widget.groupChangeListener(this);
260
+ });
261
+
262
+ // добавим ХТМЛ и его виджет workhoursday
263
+ $newGroup.workhoursday().workhoursday("value", value).insertBefore(this._lastP).show();
264
+ },
265
+
266
+ /**
267
+ * Слушаем изменения полей группы.
268
+ */
269
+ groupChangeListener: function(input) {
270
+
271
+ var $group = $(input).closest(".form_work_hours_day");
272
+
273
+ // если событие произошло на чекбоксах "Пн", "Вт"
274
+ if(input.getAttribute("name").substr(-6) == '[days]') {
275
+
276
+ if(input.checked) {
277
+
278
+ // "включили" этот день, значит, "выключим" его в другой группе (группах)
279
+ // сделаем себе временную метку, чтобы себя не выключить тоже
280
+ var dayName = input.value;
281
+
282
+ if (!$group.data("multiDays")) {
283
+ input.setAttribute("data-hold-check", "1");
284
+ this.element.find("input:checked[name$='[days]'][value=" + dayName + "][data-hold-check!=1]").prop("checked", false).change();
285
+ input.removeAttribute("data-hold-check");
286
+ }
287
+
288
+ // теперь обновим все поля из нашей группы для нового (включенного) дня
289
+ this._days[dayName].workhoursday("value", $group.workhoursday("value"));
290
+
291
+ // выделим метку этого инпута
292
+ input.parentNode.parentNode.className += " active";
293
+
294
+ } else {
295
+
296
+ // "выключили" этот день в нашей группе, значит, обнулим его поля
297
+ this._days[input.value].workhoursday("value", []);
298
+
299
+ // удалим группу, если в ней не осталось включенных дней, иначе уберем метку активного дня
300
+ if($group.find("input:checked[name$='[days]']").size() == 0) {
301
+ $group.remove();
302
+ } else {
303
+ input.parentNode.parentNode.className = input.parentNode.parentNode.className.replace(/(^|\s)active($|\s)/, "$1");
304
+ }
305
+ }
306
+
307
+ // если событие НЕ на чекбоксах "Пн", "Вт" и т. п., то транслируем в соотв. скрытые поля
308
+ } else {
309
+
310
+ var match = input.getAttribute("name").match(/(?:\[(open_time|close_time)\])?\[([^\]]*)\]$/);
311
+ var fieldName = (match[1] ? match[1] + '_' : '') + match[2];
312
+ var inputNodeName = input.nodeName.toLowerCase();
313
+
314
+ // сначала получим текущие отмеченные дни в группе
315
+ var days = [];
316
+ $group.find("input:checked[name$='[days]']").each(function() {
317
+ days.push(this.value);
318
+ });
319
+
320
+ // сделаем несколько простых проверок и отреагируем, меняя значения полей для удобства пользователя
321
+ // если текущее поле выбрано, то...
322
+ if(inputNodeName == "input" && input.checked || inputNodeName == "select" && input.value != '') {
323
+ switch(fieldName) {
324
+
325
+ // открыто 24 часа? обнулим время работы
326
+ case "open_24_hours":
327
+ $group.workhoursday("openTime", {hours: '', minutes: ''}).workhoursday("closeTime", {hours: '', minutes: ''});
328
+ break;
329
+
330
+ // открыто до последнего клиента? обнулим время закрытия
331
+ case "open_until_last_client":
332
+ $group.workhoursday("closeTime", {hours: '', minutes: ''});
333
+ break;
334
+
335
+ // выбрано время закрытия? уберем галку "открыто до последнего клиента"
336
+ case "close_time_hours":
337
+ case "close_time_minutes":
338
+ $group.workhoursday("openUntilLastClient", false);
339
+ // break пропущен намеренно
340
+
341
+ // выбрано к. л. время? уберем галку "открыто 24 часа"
342
+ case "open_time_hours":
343
+ case "open_time_minutes":
344
+ $group.workhoursday("open24Hours", false);
345
+ break;
346
+ }
347
+
348
+ // указан час, но не указаны минуты? установим :00
349
+ var camelCasedTime = this.camelCaseTime(fieldName, 6);
350
+ if((fieldName == "open_time_hours" || fieldName == "close_time_hours") && !$group.workhoursday(camelCasedTime)) {
351
+ $group.workhoursday(camelCasedTime, {minutes: 0})
352
+ }
353
+
354
+ // если текущее поле не выбрано...
355
+ } else {
356
+
357
+ // устанавливаем часы или минуты? обнулим минуты или часы соотв.
358
+ if(fieldName == "open_time_hours" || fieldName == "close_time_hours") {
359
+ $group.workhoursday(this.camelCaseTime(fieldName, 6), {minutes: ''});
360
+ } else if(fieldName == "open_time_minutes" || fieldName == "close_time_minutes") {
361
+ $group.workhoursday(this.camelCaseTime(fieldName, 8), {hours: ''});
362
+ }
363
+ }
364
+
365
+ // теперь найдем соотв. скрытые дни и установим у них значение группы
366
+ var value = $group.workhoursday("value");
367
+ for(var i = 0, ic = days.length; i < ic; ++ i) {
368
+ this._days[days[i]].workhoursday("value", value);
369
+ }
370
+
371
+ }
372
+ },
373
+
374
+ /**
375
+ * "(open|close)_time_(hours|minutes)" -> "(open|close)Time"
376
+ */
377
+ camelCaseTime: function(under_scored, stripTrailingSymbols) {
378
+ if(stripTrailingSymbols > 0) {
379
+ under_scored = under_scored.substr(0, under_scored.length - stripTrailingSymbols);
380
+ }
381
+
382
+ var tokens = under_scored.split('_');
383
+ return tokens[0] + tokens[1].substr(0, 1).toUpperCase() + tokens[1].substr(1)
384
+ },
385
+
386
+ /**
387
+ * Сгенерируем новый уникальный ID группы.
388
+ */
389
+ newGroupId: function() {
390
+ return 'group' + (cckWorkhoursGroupId ++);
391
+ },
392
+
393
+ /**
394
+ * Затычка, чтобы у предыдущих полей объекта всегда ставить в конце запятую.
395
+ */
396
+ slashzero: null
397
+
398
+ });
399
+ });