8d6923d408729ca8fda1bd8c61a8dcaf4804b96a
[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         Y.one('div.ddarea .grid')
98             .setXY(this.doc.bg_img().getXY())
99             .setStyle('width', this.doc.bg_img().get('width'))
100             .setStyle('height', this.doc.bg_img().get('height'));
101         M.util.js_complete(pendingid);
102     },
104     get_coords : function (dropzoneno) {
105         var coords = this.form.get_form_value('drops', [dropzoneno, 'coords']);
106         return coords.replace(new RegExp("\\s*", 'g'), '');
107     },
108     get_marker_text : function (markerno) {
109         if (Number(markerno) !== 0) {
110             var label = this.form.get_form_value('drags', [markerno - 1, 'label']);
111             return label.replace(new RegExp("^\\s*(.*)\\s*$"), "$1");
112         } else {
113             return '';
114         }
115     },
116     set_options_for_drag_item_selectors : function () {
117         var dragitemsoptions = {0: ''};
118         for (var i = 1; i <= this.form.get_form_value('noitems', []); i++) {
119             var label = this.get_marker_text(i);
120             if (label !== "") {
121                 dragitemsoptions[i] = Y.Escape.html(label);
122             }
123         }
124         // Get all the currently selected drags for each drop.
125         var selectedvalues = [];
126         var selector;
127         for (i = 0; i < this.form.get_form_value('nodropzone', []); i++) {
128             selector = Y.one('#id_drops_' + i + '_choice');
129             selectedvalues[i] = Number(selector.get('value'));
130         }
131         for (i = 0; i < this.form.get_form_value('nodropzone', []); i++) {
132             selector = Y.one('#id_drops_' + i + '_choice');
133             // Remove all options for drag choice.
134             selector.all('option').remove(true);
135             // And recreate the options.
136             for (var value in dragitemsoptions) {
137                 value = Number(value);
138                 var option = '<option value="' + value + '">' + dragitemsoptions[value] + '</option>';
139                 selector.append(option);
140                 var optionnode = selector.one('option[value="' + value + '"]');
141                 // Is this the currently selected value?
142                 if (value === selectedvalues[i]) {
143                     optionnode.set('selected', true);
144                 } else {
145                     // It is not the currently selected value, is it selectable?
146                     if (value !== 0) { // The 'no item' option is always selectable.
147                         // Variables to hold form values about this drag item.
148                         var noofdrags = this.form.get_form_value('drags', [value - 1, 'noofdrags']);
149                         if (Number(noofdrags) !== 0) { // 'noofdrags == 0' means infinite.
150                             // Go through all selected values in drop downs.
151                             for (var k in selectedvalues) {
152                                 // Count down 'noofdrags' and if reach zero then set disabled option for this drag item.
153                                 if (Number(selectedvalues[k]) === value) {
154                                     if (Number(noofdrags) === 1) {
155                                         optionnode.set('disabled', true);
156                                         break;
157                                     } else {
158                                         noofdrags--;
159                                     }
160                                 }
161                             }
162                         }
163                     }
164                 }
165             }
166         }
167     },
169     stop_selector_events : function () {
170         Y.all('fieldset#id_dropzoneheader select').detachAll();
171     },
173     setup_form_events : function () {
174         //events triggered by changes to form data
176         // Changes to labels.
177         Y.all('fieldset#id_draggableitemheader input').on('change', function () {
178             this.set_options_for_drag_item_selectors();
179         }, this);
181         // Changes to selected drag item.
182         Y.all('fieldset#id_draggableitemheader select').on('change', function () {
183             this.set_options_for_drag_item_selectors();
184         }, this);
186         // Change in selected item.
187         Y.all('fieldset#id_dropzoneheader select').on('change', function () {
188             this.set_options_for_drag_item_selectors();
189         }, this);
190     },
192     /**
193      * Low level operations on form.
194      */
195     form : {
196         to_name_with_index : function(name, indexes) {
197             var indexstring = name;
198             for (var i = 0; i < indexes.length; i++) {
199                 indexstring = indexstring + '[' + indexes[i] + ']';
200             }
201             return indexstring;
202         },
203         get_el : function (name, indexes) {
204             var form = document.getElementById('mform1');
205             return form.elements[this.to_name_with_index(name, indexes)];
206         },
207         get_form_value : function(name, indexes) {
208             var el = this.get_el(name, indexes);
209             if (el.type === 'checkbox') {
210                 return el.checked;
211             } else {
212                 return el.value;
213             }
214         },
215         set_form_value : function(name, indexes, value) {
216             var el = this.get_el(name, indexes);
217             if (el.type === 'checkbox') {
218                 el.checked = value;
219             } else {
220                 el.value = value;
221             }
222         },
223         from_name_with_index : function(name) {
224             var toreturn = {};
225             toreturn.indexes = [];
226             var bracket = name.indexOf('[');
227             toreturn.name = name.substring(0, bracket);
228             while (bracket !== -1) {
229                 var end = name.indexOf(']', bracket + 1);
230                 toreturn.indexes.push(name.substring(bracket + 1, end));
231                 bracket = name.indexOf('[', end + 1);
232             }
233             return toreturn;
234         }
235     },
237     file_pickers : function () {
238         var draftitemidstoname;
239         var nametoparentnode;
240         if (draftitemidstoname === undefined) {
241             draftitemidstoname = {};
242             nametoparentnode = {};
243             var filepickers = Y.all('form.mform input.filepickerhidden');
244             filepickers.each(function(filepicker) {
245                 draftitemidstoname[filepicker.get('value')] = filepicker.get('name');
246                 nametoparentnode[filepicker.get('name')] = filepicker.get('parentNode');
247             }, this);
248         }
249         var toreturn = {
250             file : function (name) {
251                 var parentnode = nametoparentnode[name];
252                 var fileanchor = parentnode.one('div.filepicker-filelist a');
253                 if (fileanchor) {
254                     return {href : fileanchor.get('href'), name : fileanchor.get('innerHTML')};
255                 } else {
256                     return {href : null, name : null};
257                 }
258             },
259             name : function (draftitemid) {
260                 return draftitemidstoname[draftitemid];
261             }
262         };
263         return toreturn;
264     }
265 },{NAME : DDMARKERFORMNAME, ATTRS : {maxsizes:{value:null}}});
267 M.qtype_ddmarker = M.qtype_ddmarker || {};
268 M.qtype_ddmarker.init_form = function(config) {
269     return new DDMARKER_FORM(config);
270 };