MDL-47494 ddmarker: Fix editing form javascript.
[moodle.git] / question / type / ddmarker / yui / src / form / js / form.js
1 // This file is part of Moodle - http://moodle.org/
2 //
3 // Moodle is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // Moodle is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16 /**
17  * This is the question editing form code.
18  */
19 var DDMARKERFORMNAME = 'moodle-qtype_ddmarker-form';
20 var DDMARKER_FORM = function() {
21     DDMARKER_FORM.superclass.constructor.apply(this, arguments);
22 };
23 Y.extend(DDMARKER_FORM, M.qtype_ddmarker.dd_base_class, {
24     fp : null,
26     initializer : function() {
27         var pendingid = 'qtype_ddmarker-form-' + Math.random().toString(36).slice(2); // Random string.
28         M.util.js_pending(pendingid);
29         this.fp = this.file_pickers();
30         var tn = Y.one(this.get('topnode'));
31         tn.one('div.fcontainer').append(
32                 '<div class="ddarea">' +
33                     '<div class="markertexts"></div>' +
34                     '<div class="droparea"></div>' +
35                     '<div class="dropzones"></div>' +
36                     '<div class="grid"></div>' +
37                 '</div>');
38         this.doc = this.doc_structure(this);
39         this.stop_selector_events();
40         this.set_options_for_drag_item_selectors();
41         this.setup_form_events();
42         Y.later(500, this, this.update_drop_zones, [pendingid], true);
43         Y.after(this.load_bg_image, M.form_filepicker, 'callback', this);
44         this.load_bg_image();
45     },
47     load_bg_image : function() {
48         var bgimageurl = this.fp.file('bgimage').href;
49         if (bgimageurl !== null) {
50             this.doc.load_bg_img(bgimageurl);
52             var drop = new Y.DD.Drop({
53                 node: this.doc.bg_img()
54             });
56             // Listen for a drop:hit on the background image.
57             drop.on('drop:hit', function(e) {
58                 e.drag.get('node').setData('gooddrop', true);
59             });
61             this.afterimageloaddone = false;
62             this.doc.bg_img().on('load', this.constrain_image_size, this);
63         }
64     },
66     constrain_image_size : function (e) {
67         var maxsize = this.get('maxsizes').bgimage;
68         var reduceby = Math.max(e.target.get('width') / maxsize.width,
69                                 e.target.get('height') / maxsize.height);
70         if (reduceby > 1) {
71             e.target.set('width', Math.floor(e.target.get('width') / reduceby));
72         }
73         e.target.addClass('constrained');
74         e.target.detach('load', this.constrain_image_size);
75     },
77     update_drop_zones : function (pendingid) {
79         // Set up drop zones.
80         if (this.graphics !== null) {
81             this.graphics.destroy();
82         }
83         this.restart_colours();
84         this.graphics = new Y.Graphic({render:"div.ddarea div.dropzones"});
85         var noofdropzones = this.form.get_form_value('nodropzone', []);
86         for (var dropzoneno = 0; dropzoneno < noofdropzones; dropzoneno++) {
87             var dragitemno = this.form.get_form_value('drops', [dropzoneno, 'choice']);
88             var markertext = this.get_marker_text(dragitemno);
89             var shape = this.form.get_form_value('drops', [dropzoneno, 'shape']);
90             var coords = this.get_coords(dropzoneno);
91             var colourfordropzone = this.get_next_colour();
92             Y.one('input#id_drops_' + dropzoneno + '_coords')
93                     .setStyle('background-color', colourfordropzone);
94             this.draw_drop_zone(dropzoneno, markertext,
95                     shape, coords, colourfordropzone, false);
96         }
97         if (this.doc.bg_img()) {
98             Y.one('div.ddarea .grid')
99                     .setXY(this.doc.bg_img().getXY())
100                     .setStyle('width', this.doc.bg_img().get('width'))
101                     .setStyle('height', this.doc.bg_img().get('height'));
102         }
103         M.util.js_complete(pendingid);
104     },
106     get_coords : function (dropzoneno) {
107         var coords = this.form.get_form_value('drops', [dropzoneno, 'coords']);
108         return coords.replace(new RegExp("\\s*", 'g'), '');
109     },
110     get_marker_text : function (markerno) {
111         if (Number(markerno) !== 0) {
112             var label = this.form.get_form_value('drags', [markerno - 1, 'label']);
113             return label.replace(new RegExp("^\\s*(.*)\\s*$"), "$1");
114         } else {
115             return '';
116         }
117     },
118     set_options_for_drag_item_selectors : function () {
119         var dragitemsoptions = {0: ''};
120         for (var i = 1; i <= this.form.get_form_value('noitems', []); i++) {
121             var label = this.get_marker_text(i);
122             if (label !== "") {
123                 dragitemsoptions[i] = Y.Escape.html(label);
124             }
125         }
126         // Get all the currently selected drags for each drop.
127         var selectedvalues = [];
128         var selector;
129         for (i = 0; i < this.form.get_form_value('nodropzone', []); i++) {
130             selector = Y.one('#id_drops_' + i + '_choice');
131             selectedvalues[i] = Number(selector.get('value'));
132         }
133         for (i = 0; i < this.form.get_form_value('nodropzone', []); i++) {
134             selector = Y.one('#id_drops_' + i + '_choice');
135             // Remove all options for drag choice.
136             selector.all('option').remove(true);
137             // And recreate the options.
138             for (var value in dragitemsoptions) {
139                 value = Number(value);
140                 var option = '<option value="' + value + '">' + dragitemsoptions[value] + '</option>';
141                 selector.append(option);
142                 var optionnode = selector.one('option[value="' + value + '"]');
143                 // Is this the currently selected value?
144                 if (value === selectedvalues[i]) {
145                     optionnode.set('selected', true);
146                 } else {
147                     // It is not the currently selected value, is it selectable?
148                     if (value !== 0) { // The 'no item' option is always selectable.
149                         // Variables to hold form values about this drag item.
150                         var noofdrags = this.form.get_form_value('drags', [value - 1, 'noofdrags']);
151                         if (Number(noofdrags) !== 0) { // 'noofdrags == 0' means infinite.
152                             // Go through all selected values in drop downs.
153                             for (var k in selectedvalues) {
154                                 // Count down 'noofdrags' and if reach zero then set disabled option for this drag item.
155                                 if (Number(selectedvalues[k]) === value) {
156                                     if (Number(noofdrags) === 1) {
157                                         optionnode.set('disabled', true);
158                                         break;
159                                     } else {
160                                         noofdrags--;
161                                     }
162                                 }
163                             }
164                         }
165                     }
166                 }
167             }
168         }
169     },
171     stop_selector_events : function () {
172         Y.all('fieldset#id_dropzoneheader select').detachAll();
173     },
175     setup_form_events : function () {
176         //events triggered by changes to form data
178         // Changes to labels.
179         Y.all('fieldset#id_draggableitemheader input').on('change', function () {
180             this.set_options_for_drag_item_selectors();
181         }, this);
183         // Changes to selected drag item.
184         Y.all('fieldset#id_draggableitemheader select').on('change', function () {
185             this.set_options_for_drag_item_selectors();
186         }, this);
188         // Change in selected item.
189         Y.all('fieldset#id_dropzoneheader select').on('change', function () {
190             this.set_options_for_drag_item_selectors();
191         }, this);
192     },
194     /**
195      * Low level operations on form.
196      */
197     form : {
198         to_name_with_index : function(name, indexes) {
199             var indexstring = name;
200             for (var i = 0; i < indexes.length; i++) {
201                 indexstring = indexstring + '[' + indexes[i] + ']';
202             }
203             return indexstring;
204         },
205         get_el : function (name, indexes) {
206             var form = document.getElementById('mform1');
207             return form.elements[this.to_name_with_index(name, indexes)];
208         },
209         get_form_value : function(name, indexes) {
210             var el = this.get_el(name, indexes);
211             if (el.type === 'checkbox') {
212                 return el.checked;
213             } else {
214                 return el.value;
215             }
216         },
217         set_form_value : function(name, indexes, value) {
218             var el = this.get_el(name, indexes);
219             if (el.type === 'checkbox') {
220                 el.checked = value;
221             } else {
222                 el.value = value;
223             }
224         },
225         from_name_with_index : function(name) {
226             var toreturn = {};
227             toreturn.indexes = [];
228             var bracket = name.indexOf('[');
229             toreturn.name = name.substring(0, bracket);
230             while (bracket !== -1) {
231                 var end = name.indexOf(']', bracket + 1);
232                 toreturn.indexes.push(name.substring(bracket + 1, end));
233                 bracket = name.indexOf('[', end + 1);
234             }
235             return toreturn;
236         }
237     },
239     file_pickers : function () {
240         var draftitemidstoname;
241         var nametoparentnode;
242         if (draftitemidstoname === undefined) {
243             draftitemidstoname = {};
244             nametoparentnode = {};
245             var filepickers = Y.all('form.mform input.filepickerhidden');
246             filepickers.each(function(filepicker) {
247                 draftitemidstoname[filepicker.get('value')] = filepicker.get('name');
248                 nametoparentnode[filepicker.get('name')] = filepicker.get('parentNode');
249             }, this);
250         }
251         var toreturn = {
252             file : function (name) {
253                 var parentnode = nametoparentnode[name];
254                 var fileanchor = parentnode.one('div.filepicker-filelist a');
255                 if (fileanchor) {
256                     return {href : fileanchor.get('href'), name : fileanchor.get('innerHTML')};
257                 } else {
258                     return {href : null, name : null};
259                 }
260             },
261             name : function (draftitemid) {
262                 return draftitemidstoname[draftitemid];
263             }
264         };
265         return toreturn;
266     }
267 },{NAME : DDMARKERFORMNAME, ATTRS : {maxsizes:{value:null}}});
269 M.qtype_ddmarker = M.qtype_ddmarker || {};
270 M.qtype_ddmarker.init_form = function(config) {
271     return new DDMARKER_FORM(config);
272 };