1 |
efrain |
1 |
/**
|
|
|
2 |
* JavaScript for form editing date conditions.
|
|
|
3 |
*
|
|
|
4 |
* @module moodle-availability_date-form
|
|
|
5 |
*/
|
|
|
6 |
M.availability_date = M.availability_date || {};
|
|
|
7 |
|
|
|
8 |
/**
|
|
|
9 |
* @class M.availability_date.form
|
|
|
10 |
* @extends M.core_availability.plugin
|
|
|
11 |
*/
|
|
|
12 |
M.availability_date.form = Y.Object(M.core_availability.plugin);
|
|
|
13 |
|
|
|
14 |
/**
|
|
|
15 |
* Initialises this plugin.
|
|
|
16 |
*
|
|
|
17 |
* Because the date fields are complex depending on Moodle calendar settings,
|
|
|
18 |
* we create the HTML for these fields in PHP and pass it to this method.
|
|
|
19 |
*
|
|
|
20 |
* @method initInner
|
|
|
21 |
* @param {String} html HTML to use for date fields
|
|
|
22 |
* @param {Number} defaultTime Time value that corresponds to initial fields
|
|
|
23 |
*/
|
|
|
24 |
M.availability_date.form.initInner = function(html, defaultTime) {
|
|
|
25 |
this.html = html;
|
|
|
26 |
this.defaultTime = defaultTime;
|
|
|
27 |
};
|
|
|
28 |
|
|
|
29 |
M.availability_date.form.getNode = function(json) {
|
|
|
30 |
var html = '<span class="col-form-label pr-3">' +
|
|
|
31 |
M.util.get_string('direction_before', 'availability_date') + '</span> <span class="availability-group">' +
|
|
|
32 |
'<label><span class="accesshide">' + M.util.get_string('direction_label', 'availability_date') + ' </span>' +
|
|
|
33 |
'<select name="direction" class="custom-select">' +
|
|
|
34 |
'<option value=">=">' + M.util.get_string('direction_from', 'availability_date') + '</option>' +
|
|
|
35 |
'<option value="<">' + M.util.get_string('direction_until', 'availability_date') + '</option>' +
|
|
|
36 |
'</select></label></span> ' + this.html;
|
|
|
37 |
var node = Y.Node.create('<span>' + html + '</span>');
|
|
|
38 |
|
|
|
39 |
// Set initial value if non-default.
|
|
|
40 |
if (json.t !== undefined) {
|
|
|
41 |
node.setData('time', json.t);
|
|
|
42 |
// Disable everything.
|
|
|
43 |
node.all('select:not([name=direction])').each(function(select) {
|
|
|
44 |
select.set('disabled', true);
|
|
|
45 |
});
|
|
|
46 |
|
|
|
47 |
var url = M.cfg.wwwroot + '/availability/condition/date/ajax.php?action=fromtime' +
|
|
|
48 |
'&time=' + json.t;
|
|
|
49 |
Y.io(url, {on: {
|
|
|
50 |
success: function(id, response) {
|
|
|
51 |
var fields = Y.JSON.parse(response.responseText);
|
|
|
52 |
for (var field in fields) {
|
|
|
53 |
var select = node.one('select[name=x\\[' + field + '\\]]');
|
|
|
54 |
select.set('value', '' + fields[field]);
|
|
|
55 |
select.set('disabled', false);
|
|
|
56 |
}
|
|
|
57 |
},
|
|
|
58 |
failure: function() {
|
|
|
59 |
window.alert(M.util.get_string('ajaxerror', 'availability_date'));
|
|
|
60 |
}
|
|
|
61 |
}});
|
|
|
62 |
} else {
|
|
|
63 |
// Set default time that corresponds to the HTML selectors.
|
|
|
64 |
node.setData('time', this.defaultTime);
|
|
|
65 |
}
|
|
|
66 |
if (json.nodeUID === undefined) {
|
|
|
67 |
var miliTime = new Date();
|
|
|
68 |
json.nodeUID = miliTime.getTime();
|
|
|
69 |
}
|
|
|
70 |
node.setData('nodeUID', json.nodeUID);
|
|
|
71 |
if (json.d !== undefined) {
|
|
|
72 |
node.one('select[name=direction]').set('value', json.d);
|
|
|
73 |
}
|
|
|
74 |
|
|
|
75 |
// Add event handlers (first time only).
|
|
|
76 |
if (!M.availability_date.form.addedEvents) {
|
|
|
77 |
M.availability_date.form.addedEvents = true;
|
|
|
78 |
|
|
|
79 |
var root = Y.one('.availability-field');
|
|
|
80 |
root.delegate('change', function() {
|
|
|
81 |
// For the direction, just update the form fields.
|
|
|
82 |
M.core_availability.form.update();
|
|
|
83 |
}, '.availability_date select[name=direction]');
|
|
|
84 |
|
|
|
85 |
root.delegate('change', function() {
|
|
|
86 |
// Update time using AJAX call from root node.
|
|
|
87 |
M.availability_date.form.updateTime(this.ancestor('span.availability_date'));
|
|
|
88 |
}, '.availability_date select:not([name=direction])');
|
|
|
89 |
}
|
|
|
90 |
|
|
|
91 |
if (node.one('a[href=#]')) {
|
|
|
92 |
// Add the date selector magic.
|
|
|
93 |
M.form.dateselector.init_single_date_selector(node);
|
|
|
94 |
|
|
|
95 |
// This special handler detects when the date selector changes the year.
|
|
|
96 |
var yearSelect = node.one('select[name=x\\[year\\]]');
|
|
|
97 |
var oldSet = yearSelect.set;
|
|
|
98 |
yearSelect.set = function(name, value) {
|
|
|
99 |
oldSet.call(yearSelect, name, value);
|
|
|
100 |
if (name === 'selectedIndex') {
|
|
|
101 |
// Do this after timeout or the other fields haven't been set yet.
|
|
|
102 |
setTimeout(function() {
|
|
|
103 |
M.availability_date.form.updateTime(node);
|
|
|
104 |
}, 0);
|
|
|
105 |
}
|
|
|
106 |
};
|
|
|
107 |
}
|
|
|
108 |
|
|
|
109 |
return node;
|
|
|
110 |
};
|
|
|
111 |
|
|
|
112 |
/**
|
|
|
113 |
* Updates time from AJAX. Whenever the field values change, we recompute the
|
|
|
114 |
* actual time via an AJAX request to Moodle.
|
|
|
115 |
*
|
|
|
116 |
* This will set the 'time' data on the node and then update the form, once it
|
|
|
117 |
* gets an AJAX response.
|
|
|
118 |
*
|
|
|
119 |
* @method updateTime
|
|
|
120 |
* @param {Y.Node} node Node for plugin controls
|
|
|
121 |
*/
|
|
|
122 |
M.availability_date.form.updateTime = function(node) {
|
|
|
123 |
// After a change to the date/time we need to recompute the
|
|
|
124 |
// actual time using AJAX because it depends on the user's
|
|
|
125 |
// time zone and calendar options.
|
|
|
126 |
var url = M.cfg.wwwroot + '/availability/condition/date/ajax.php?action=totime' +
|
|
|
127 |
'&year=' + node.one('select[name=x\\[year\\]]').get('value') +
|
|
|
128 |
'&month=' + node.one('select[name=x\\[month\\]]').get('value') +
|
|
|
129 |
'&day=' + node.one('select[name=x\\[day\\]]').get('value') +
|
|
|
130 |
'&hour=' + node.one('select[name=x\\[hour\\]]').get('value') +
|
|
|
131 |
'&minute=' + node.one('select[name=x\\[minute\\]]').get('value');
|
|
|
132 |
Y.io(url, {on: {
|
|
|
133 |
success: function(id, response) {
|
|
|
134 |
node.setData('time', response.responseText);
|
|
|
135 |
M.core_availability.form.update();
|
|
|
136 |
},
|
|
|
137 |
failure: function() {
|
|
|
138 |
window.alert(M.util.get_string('ajaxerror', 'availability_date'));
|
|
|
139 |
}
|
|
|
140 |
}});
|
|
|
141 |
};
|
|
|
142 |
|
|
|
143 |
M.availability_date.form.fillValue = function(value, node) {
|
|
|
144 |
value.d = node.one('select[name=direction]').get('value');
|
|
|
145 |
value.t = parseInt(node.getData('time'), 10);
|
|
|
146 |
value.nodeUID = node.getData('nodeUID');
|
|
|
147 |
};
|
|
|
148 |
|
|
|
149 |
/**
|
|
|
150 |
* List out Date node value in the same branch.
|
|
|
151 |
*
|
|
|
152 |
* This will go through all array node and list nodes that are sibling of the current node.
|
|
|
153 |
*
|
|
|
154 |
* @method findAllDateSiblings
|
|
|
155 |
* @param {Array} tree Tree items to convert
|
|
|
156 |
* @param {Number} nodeUIDToFind node UID to find.
|
|
|
157 |
* @return {Array|null} array of surrounding date avaiability values
|
|
|
158 |
*/
|
|
|
159 |
M.availability_date.form.findAllDateSiblings = function(tree, nodeUIDToFind) {
|
|
|
160 |
var itemValue = null;
|
|
|
161 |
var siblingsFinderRecursive = function(itemsTree) {
|
|
|
162 |
var dateSiblings = [];
|
|
|
163 |
var nodeFound = false;
|
|
|
164 |
var index;
|
|
|
165 |
var childDates;
|
|
|
166 |
var currentOp = itemsTree.op !== undefined ? itemsTree.op : null;
|
|
|
167 |
if (itemsTree.c !== undefined) {
|
|
|
168 |
var children = itemsTree.c;
|
|
|
169 |
for (index = 0; index < children.length; index++) {
|
|
|
170 |
itemValue = children.at(index);
|
|
|
171 |
if (itemValue.type === undefined) {
|
|
|
172 |
childDates = siblingsFinderRecursive(itemValue);
|
|
|
173 |
if (childDates) {
|
|
|
174 |
return childDates;
|
|
|
175 |
}
|
|
|
176 |
}
|
|
|
177 |
if (itemValue.type === 'date') {
|
|
|
178 |
// We go through all tree node, if we meet the current node then we add all nodes in the current branch.
|
|
|
179 |
if (nodeUIDToFind === itemValue.nodeUID) {
|
|
|
180 |
nodeFound = true;
|
|
|
181 |
} else if (currentOp === '&') {
|
|
|
182 |
dateSiblings.push(itemValue);
|
|
|
183 |
}
|
|
|
184 |
}
|
|
|
185 |
}
|
|
|
186 |
if (nodeFound) {
|
|
|
187 |
return dateSiblings;
|
|
|
188 |
}
|
|
|
189 |
}
|
|
|
190 |
return null;
|
|
|
191 |
};
|
|
|
192 |
return siblingsFinderRecursive(tree);
|
|
|
193 |
};
|
|
|
194 |
|
|
|
195 |
/**
|
|
|
196 |
* Check current node.
|
|
|
197 |
*
|
|
|
198 |
* This will check current date node with all date node in tree node.
|
|
|
199 |
*
|
|
|
200 |
* @method checkConditionDate
|
|
|
201 |
* @param {Y.Node} currentNode The curent node.
|
|
|
202 |
*
|
|
|
203 |
* @return {boolean} error Return true if the date is conflict.
|
|
|
204 |
*/
|
|
|
205 |
M.availability_date.form.checkConditionDate = function(currentNode) {
|
|
|
206 |
var error = false;
|
|
|
207 |
var currentNodeUID = currentNode.getData('nodeUID');
|
|
|
208 |
var currentNodeDirection = currentNode.one('select[name=direction]').get('value');
|
|
|
209 |
var currentNodeTime = parseInt(currentNode.getData('time'), 10);
|
|
|
210 |
var dateSiblings = M.availability_date.form.findAllDateSiblings(
|
|
|
211 |
M.core_availability.form.rootList.getValue(),
|
|
|
212 |
currentNodeUID);
|
|
|
213 |
if (dateSiblings) {
|
|
|
214 |
dateSiblings.forEach(function(dateSibling) {
|
|
|
215 |
// Validate if the date is conflict.
|
|
|
216 |
if (dateSibling.d === '<') {
|
|
|
217 |
if (currentNodeDirection === '>=' && currentNodeTime >= dateSibling.t) {
|
|
|
218 |
error = true;
|
|
|
219 |
}
|
|
|
220 |
} else {
|
|
|
221 |
if (currentNodeDirection === '<' && currentNodeTime <= dateSibling.t) {
|
|
|
222 |
error = true;
|
|
|
223 |
}
|
|
|
224 |
}
|
|
|
225 |
return error;
|
|
|
226 |
});
|
|
|
227 |
}
|
|
|
228 |
return error;
|
|
|
229 |
};
|
|
|
230 |
|
|
|
231 |
M.availability_date.form.fillErrors = function(errors, node) {
|
|
|
232 |
var error = M.availability_date.form.checkConditionDate(node);
|
|
|
233 |
if (error) {
|
|
|
234 |
errors.push('availability_date:error_dateconflict');
|
|
|
235 |
}
|
|
|
236 |
};
|