cck_forms 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
+ });