mirror of
https://github.com/moodle/moodle.git
synced 2025-08-06 09:26:35 +02:00
MDL-47494 ddmarker: Convert the JavaScript to use Shifter. #13314
This commit is contained in:
parent
a366c8b950
commit
24c6f2fc8c
14 changed files with 2546 additions and 804 deletions
565
question/type/ddmarker/yui/build/moodle-qtype_ddmarker-dd/moodle-qtype_ddmarker-dd-debug.js
vendored
Normal file
565
question/type/ddmarker/yui/build/moodle-qtype_ddmarker-dd/moodle-qtype_ddmarker-dd-debug.js
vendored
Normal file
|
@ -0,0 +1,565 @@
|
|||
YUI.add('moodle-qtype_ddmarker-dd', function (Y, NAME) {
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
var DDMARKERDDNAME = 'moodle-qtype_ddmarker-dd';
|
||||
var DDMARKER_DD = function() {
|
||||
DDMARKER_DD.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
/**
|
||||
* This is the base class for the question rendering and question editing form code.
|
||||
*/
|
||||
Y.extend(DDMARKER_DD, Y.Base, {
|
||||
doc : null,
|
||||
polltimer : null,
|
||||
afterimageloaddone : false,
|
||||
graphics : null,
|
||||
poll_for_image_load : function (e, waitforimageconstrain, pause, doafterwords) {
|
||||
if (this.afterimageloaddone) {
|
||||
return;
|
||||
}
|
||||
var bgdone = this.doc.bg_img().get('complete');
|
||||
if (waitforimageconstrain) {
|
||||
bgdone = bgdone && this.doc.bg_img().hasClass('constrained');
|
||||
}
|
||||
if (bgdone) {
|
||||
if (this.polltimer !== null) {
|
||||
this.polltimer.cancel();
|
||||
this.polltimer = null;
|
||||
}
|
||||
this.doc.bg_img().detach('load', this.poll_for_image_load);
|
||||
if (pause !== 0) {
|
||||
Y.later(pause, this, doafterwords);
|
||||
} else {
|
||||
doafterwords.call(this);
|
||||
}
|
||||
this.afterimageloaddone = true;
|
||||
} else if (this.polltimer === null) {
|
||||
var pollarguments = [null, waitforimageconstrain, pause, doafterwords];
|
||||
this.polltimer =
|
||||
Y.later(1000, this, this.poll_for_image_load, pollarguments, true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Object to encapsulate operations on dd area.
|
||||
*/
|
||||
doc_structure : function () {
|
||||
var topnode = Y.one(this.get('topnode'));
|
||||
var dragitemsarea = topnode.one('div.dragitems');
|
||||
var dropbgarea = topnode.one('div.droparea');
|
||||
return {
|
||||
top_node : function() {
|
||||
return topnode;
|
||||
},
|
||||
bg_img : function() {
|
||||
return topnode.one('.dropbackground');
|
||||
},
|
||||
load_bg_img : function (url) {
|
||||
dropbgarea.setContent('<img class="dropbackground" src="' + url + '"/>');
|
||||
this.bg_img().on('load', this.on_image_load, this, 'bg_image');
|
||||
},
|
||||
drag_items : function() {
|
||||
return dragitemsarea.all('.dragitem');
|
||||
},
|
||||
drag_items_for_choice : function(choiceno) {
|
||||
return dragitemsarea.all('span.dragitem.choice' + choiceno);
|
||||
},
|
||||
drag_item_for_choice : function(choiceno, itemno) {
|
||||
return dragitemsarea.one('span.dragitem.choice' + choiceno +
|
||||
'.item' + itemno);
|
||||
},
|
||||
drag_item_being_dragged : function(choiceno) {
|
||||
return dragitemsarea.one('span.dragitem.beingdragged.choice' + choiceno);
|
||||
},
|
||||
drag_item_home : function (choiceno) {
|
||||
return dragitemsarea.one('span.draghome.choice' + choiceno);
|
||||
},
|
||||
drag_item_homes : function() {
|
||||
return dragitemsarea.all('span.draghome');
|
||||
},
|
||||
get_classname_numeric_suffix : function(node, prefix) {
|
||||
var classes = node.getAttribute('class');
|
||||
if (classes !== '') {
|
||||
var classesarr = classes.split(' ');
|
||||
for (var index = 0; index < classesarr.length; index++) {
|
||||
var patt1 = new RegExp('^' + prefix + '([0-9])+$');
|
||||
if (patt1.test(classesarr[index])) {
|
||||
var patt2 = new RegExp('([0-9])+$');
|
||||
var match = patt2.exec(classesarr[index]);
|
||||
return Number(match[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
inputs_for_choices : function () {
|
||||
return topnode.all('input.choices');
|
||||
},
|
||||
input_for_choice : function (choiceno) {
|
||||
return topnode.one('input.choice' + choiceno);
|
||||
},
|
||||
marker_texts : function () {
|
||||
return topnode.one('div.markertexts');
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
colours : ['#FFFFFF', '#B0C4DE', '#DCDCDC', '#D8BFD8',
|
||||
'#87CEFA','#DAA520', '#FFD700', '#F0E68C'],
|
||||
nextcolourindex : 0,
|
||||
restart_colours : function () {
|
||||
this.nextcolourindex = 0;
|
||||
},
|
||||
get_next_colour : function () {
|
||||
var colour = this.colours[this.nextcolourindex];
|
||||
this.nextcolourindex++;
|
||||
if (this.nextcolourindex === this.colours.length) {
|
||||
this.nextcolourindex = 0;
|
||||
}
|
||||
return colour;
|
||||
},
|
||||
convert_to_window_xy : function (bgimgxy) {
|
||||
return [Number(bgimgxy[0]) + this.doc.bg_img().getX() + 1,
|
||||
Number(bgimgxy[1]) + this.doc.bg_img().getY() + 1];
|
||||
},
|
||||
shapes : [],
|
||||
draw_drop_zone : function (dropzoneno, markertext, shape, coords, colour, link) {
|
||||
var existingmarkertext;
|
||||
if (link) {
|
||||
existingmarkertext = this.doc.marker_texts().one('span.markertext' + dropzoneno + ' a');
|
||||
} else {
|
||||
existingmarkertext = this.doc.marker_texts().one('span.markertext' + dropzoneno);
|
||||
}
|
||||
|
||||
if (existingmarkertext) {
|
||||
if (markertext !== '') {
|
||||
existingmarkertext.setContent(markertext);
|
||||
} else {
|
||||
existingmarkertext.remove(true);
|
||||
}
|
||||
} else if (markertext !== '') {
|
||||
var classnames = 'markertext markertext' + dropzoneno;
|
||||
if (link) {
|
||||
this.doc.marker_texts().append('<span class="' + classnames + '"><a href="#">' +
|
||||
markertext + '</a></span>');
|
||||
} else {
|
||||
this.doc.marker_texts().append('<span class="' + classnames + '">' +
|
||||
markertext + '</span>');
|
||||
}
|
||||
}
|
||||
var drawfunc = 'draw_shape_' + shape;
|
||||
if (this[drawfunc] instanceof Function){
|
||||
var xyfortext = this[drawfunc](dropzoneno, coords, colour);
|
||||
if (xyfortext !== null) {
|
||||
var markerspan = this.doc.top_node().one('div.ddarea div.markertexts span.markertext' + dropzoneno);
|
||||
if (markerspan !== null) {
|
||||
markerspan.setStyle('opacity', '0.6');
|
||||
xyfortext[0] -= markerspan.get('offsetWidth') / 2;
|
||||
xyfortext[1] -= markerspan.get('offsetHeight') / 2;
|
||||
markerspan.setXY(this.convert_to_window_xy(xyfortext));
|
||||
var markerspananchor = markerspan.one('a');
|
||||
if (markerspananchor !== null) {
|
||||
markerspananchor.once('click', function (e, dropzoneno) {
|
||||
var fill = this.shapes[dropzoneno].get('fill');
|
||||
fill.opacity = 1;
|
||||
this.shapes[dropzoneno].set('fill', fill);
|
||||
},
|
||||
this,
|
||||
dropzoneno
|
||||
);
|
||||
markerspananchor.set('tabIndex', 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
draw_shape_circle : function (dropzoneno, coords, colour) {
|
||||
var coordsparts = coords.match(/(\d+),(\d+);(\d+)/);
|
||||
if (coordsparts && coordsparts.length === 4) {
|
||||
var xy = [Number(coordsparts[1]) - coordsparts[3], Number(coordsparts[2]) - coordsparts[3]];
|
||||
if (this.coords_in_img(xy)) {
|
||||
var widthheight = [Number(coordsparts[3]) * 2, Number(coordsparts[3]) * 2];
|
||||
var shape = this.graphics.addShape({
|
||||
type: 'circle',
|
||||
width: widthheight[0],
|
||||
height: widthheight[1],
|
||||
fill: {
|
||||
color: colour,
|
||||
opacity: "0.5"
|
||||
},
|
||||
stroke: {
|
||||
weight: 1,
|
||||
color: "black"
|
||||
}
|
||||
});
|
||||
shape.setXY(this.convert_to_window_xy(xy));
|
||||
this.shapes[dropzoneno] = shape;
|
||||
return [Number(coordsparts[1]), Number(coordsparts[2])];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
draw_shape_rectangle : function (dropzoneno, coords, colour) {
|
||||
var coordsparts = coords.match(/(\d+),(\d+);(\d+),(\d+)/);
|
||||
if (coordsparts && coordsparts.length === 5) {
|
||||
var xy = [Number(coordsparts[1]), Number(coordsparts[2])];
|
||||
var widthheight = [Number(coordsparts[3]), Number(coordsparts[4])];
|
||||
if (this.coords_in_img([xy[0] + widthheight[0], xy[1] + widthheight[1]])) {
|
||||
var shape = this.graphics.addShape({
|
||||
type: 'rect',
|
||||
width: widthheight[0],
|
||||
height: widthheight[1],
|
||||
fill: {
|
||||
color: colour,
|
||||
opacity: "0.5"
|
||||
},
|
||||
stroke: {
|
||||
weight: 1,
|
||||
color: "black"
|
||||
}
|
||||
});
|
||||
shape.setXY(this.convert_to_window_xy(xy));
|
||||
this.shapes[dropzoneno] = shape;
|
||||
return [Number(xy[0]) + widthheight[0] / 2, Number(xy[1]) + widthheight[1] / 2];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
},
|
||||
draw_shape_polygon : function (dropzoneno, coords, colour) {
|
||||
var coordsparts = coords.split(';');
|
||||
var xy = [];
|
||||
for (var i in coordsparts) {
|
||||
var parts = coordsparts[i].match(/^(\d+),(\d+)$/);
|
||||
if (parts !== null && this.coords_in_img([parts[1], parts[2]])) {
|
||||
xy[xy.length] = [parts[1], parts[2]];
|
||||
}
|
||||
}
|
||||
if (xy.length > 2) {
|
||||
var polygon = this.graphics.addShape({
|
||||
type: "path",
|
||||
stroke: {
|
||||
weight: 1,
|
||||
color: "black"
|
||||
},
|
||||
fill: {
|
||||
color: colour,
|
||||
opacity : "0.5"
|
||||
}
|
||||
});
|
||||
var maxxy = [0,0];
|
||||
var minxy = [this.doc.bg_img().get('width'), this.doc.bg_img().get('height')];
|
||||
for (i = 0; i < xy.length; i++) {
|
||||
//calculate min and max points to find center to show marker on
|
||||
minxy[0] = Math.min(xy[i][0], minxy[0]);
|
||||
minxy[1] = Math.min(xy[i][1], minxy[1]);
|
||||
maxxy[0] = Math.max(xy[i][0], maxxy[0]);
|
||||
maxxy[1] = Math.max(xy[i][1], maxxy[1]);
|
||||
if (i === 0) {
|
||||
polygon.moveTo(xy[i][0], xy[i][1]);
|
||||
} else {
|
||||
polygon.lineTo(xy[i][0], xy[i][1]);
|
||||
}
|
||||
}
|
||||
if (Number(xy[0][0]) !== Number(xy[xy.length - 1][0]) || Number(xy[0][1]) !== Number(xy[xy.length - 1][1])) {
|
||||
polygon.lineTo(xy[0][0], xy[0][1]); // Close polygon if not already closed.
|
||||
}
|
||||
polygon.end();
|
||||
polygon.setXY(this.doc.bg_img().getXY());
|
||||
this.shapes[dropzoneno] = polygon;
|
||||
return [(minxy[0] + maxxy[0]) / 2, (minxy[1] + maxxy[1]) / 2];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
coords_in_img : function (coords) {
|
||||
return (coords[0] <= this.doc.bg_img().get('width') &&
|
||||
coords[1] <= this.doc.bg_img().get('height'));
|
||||
}
|
||||
}, {
|
||||
NAME : DDMARKERDDNAME,
|
||||
ATTRS : {
|
||||
drops : {value : null},
|
||||
readonly : {value : false},
|
||||
topnode : {value : null}
|
||||
}
|
||||
});
|
||||
M.qtype_ddmarker = M.qtype_ddmarker || {};
|
||||
M.qtype_ddmarker.dd_base_class = DDMARKER_DD;
|
||||
|
||||
var DDMARKERQUESTIONNAME = 'ddmarker_question';
|
||||
var DDMARKER_QUESTION = function() {
|
||||
DDMARKER_QUESTION.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
/**
|
||||
* This is the code for question rendering.
|
||||
*/
|
||||
Y.extend(DDMARKER_QUESTION, M.qtype_ddmarker.dd_base_class, {
|
||||
initializer : function() {
|
||||
this.doc = this.doc_structure(this);
|
||||
this.poll_for_image_load(null, false, 0, this.after_image_load);
|
||||
this.doc.bg_img().after('load', this.poll_for_image_load, this,
|
||||
false, 0, this.after_image_load);
|
||||
},
|
||||
after_image_load : function () {
|
||||
this.redraw_drags_and_drops();
|
||||
Y.later(2000, this, this.redraw_drags_and_drops, [], true);
|
||||
},
|
||||
clone_new_drag_item : function (draghome, itemno) {
|
||||
var drag = draghome.cloneNode(true);
|
||||
drag.removeClass('draghome');
|
||||
drag.addClass('dragitem');
|
||||
drag.addClass('item' + itemno);
|
||||
drag.one('span.markertext').setStyle('opacity', 0.6);
|
||||
draghome.insert(drag, 'after');
|
||||
if (!this.get('readonly')) {
|
||||
this.draggable(drag);
|
||||
}
|
||||
return drag;
|
||||
},
|
||||
draggable : function (drag) {
|
||||
var dd = new Y.DD.Drag({
|
||||
node: drag,
|
||||
dragMode: 'intersect'
|
||||
}).plug(Y.Plugin.DDConstrained, {constrain2node: this.doc.top_node()});
|
||||
dd.after('drag:start', function(e){
|
||||
var dragnode = e.target.get('node');
|
||||
dragnode.addClass('beingdragged');
|
||||
var choiceno = this.get_choiceno_for_node(dragnode);
|
||||
var itemno = this.get_itemno_for_node(dragnode);
|
||||
if (itemno !== null) {
|
||||
dragnode.removeClass('item' + dragnode);
|
||||
}
|
||||
this.save_all_xy_for_choice(choiceno, null);
|
||||
this.redraw_drags_and_drops();
|
||||
}, this);
|
||||
dd.after('drag:end', function(e) {
|
||||
var dragnode = e.target.get('node');
|
||||
dragnode.removeClass('beingdragged');
|
||||
var choiceno = this.get_choiceno_for_node(dragnode);
|
||||
this.save_all_xy_for_choice(choiceno, dragnode);
|
||||
this.redraw_drags_and_drops();
|
||||
}, this);
|
||||
//--- keyboard accessibility
|
||||
drag.set('tabIndex', 0);
|
||||
drag.on('dragchange', this.drop_zone_key_press, this);
|
||||
},
|
||||
save_all_xy_for_choice: function (choiceno, dropped) {
|
||||
var coords = [];
|
||||
var bgimgxy;
|
||||
for (var i = 0; i <= this.doc.drag_items_for_choice(choiceno).size(); i++) {
|
||||
var dragitem = this.doc.drag_item_for_choice(choiceno, i);
|
||||
if (dragitem) {
|
||||
dragitem.removeClass('item' + i);
|
||||
if (!dragitem.hasClass('beingdragged')) {
|
||||
bgimgxy = this.convert_to_bg_img_xy(dragitem.getXY());
|
||||
if (this.xy_in_bgimg(bgimgxy)) {
|
||||
dragitem.removeClass('item' + i);
|
||||
dragitem.addClass('item' + coords.length);
|
||||
coords[coords.length] = bgimgxy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dropped !== null){
|
||||
bgimgxy = this.convert_to_bg_img_xy(dropped.getXY());
|
||||
dropped.addClass('item' + coords.length);
|
||||
if (this.xy_in_bgimg(bgimgxy)) {
|
||||
coords[coords.length] = bgimgxy;
|
||||
}
|
||||
}
|
||||
this.set_form_value(choiceno, coords.join(';'));
|
||||
},
|
||||
reset_drag_xy : function (choiceno) {
|
||||
this.set_form_value(choiceno, '');
|
||||
},
|
||||
set_form_value : function (choiceno, value) {
|
||||
this.doc.input_for_choice(choiceno).set('value', value);
|
||||
},
|
||||
//make sure xy value is not out of bounds of bg image
|
||||
xy_in_bgimg : function (bgimgxy) {
|
||||
if ((bgimgxy[0] < 0) ||
|
||||
(bgimgxy[1] < 0) ||
|
||||
(bgimgxy[0] > this.doc.bg_img().get('width')) ||
|
||||
(bgimgxy[1] > this.doc.bg_img().get('height'))){
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
constrain_to_bgimg : function (windowxy) {
|
||||
var bgimgxy = this.convert_to_bg_img_xy(windowxy);
|
||||
bgimgxy[0] = Math.max(0, bgimgxy[0]);
|
||||
bgimgxy[1] = Math.max(0, bgimgxy[1]);
|
||||
bgimgxy[0] = Math.min(this.doc.bg_img().get('width'), bgimgxy[0]);
|
||||
bgimgxy[1] = Math.min(this.doc.bg_img().get('height'), bgimgxy[1]);
|
||||
return this.convert_to_window_xy(bgimgxy);
|
||||
},
|
||||
convert_to_bg_img_xy : function (windowxy) {
|
||||
return [Number(windowxy[0]) - this.doc.bg_img().getX() - 1,
|
||||
Number(windowxy[1]) - this.doc.bg_img().getY() - 1];
|
||||
},
|
||||
redraw_drags_and_drops : function() {
|
||||
this.doc.drag_items().each(function(item) {
|
||||
//if (!item.hasClass('beingdragged')){
|
||||
item.addClass('unneeded');
|
||||
//}
|
||||
}, this);
|
||||
this.doc.inputs_for_choices().each(function (input) {
|
||||
var choiceno = this.get_choiceno_for_node(input);
|
||||
var coords = this.get_coords(input);
|
||||
var dragitemhome = this.doc.drag_item_home(choiceno);
|
||||
for (var i = 0; i < coords.length; i++) {
|
||||
var dragitem = this.doc.drag_item_for_choice(choiceno, i);
|
||||
if (!dragitem || dragitem.hasClass('beingdragged')) {
|
||||
dragitem = this.clone_new_drag_item(dragitemhome, i);
|
||||
} else {
|
||||
dragitem.removeClass('unneeded');
|
||||
}
|
||||
dragitem.setXY(coords[i]);
|
||||
}
|
||||
}, this);
|
||||
this.doc.drag_items().each(function(item) {
|
||||
if (item.hasClass('unneeded') && !item.hasClass('beingdragged')) {
|
||||
item.remove(true);
|
||||
}
|
||||
}, this);
|
||||
if (this.graphics !== null) {
|
||||
this.graphics.clear();
|
||||
} else {
|
||||
this.graphics = new Y.Graphic(
|
||||
{render:this.doc.top_node().one("div.ddarea div.dropzones")}
|
||||
);
|
||||
}
|
||||
if (this.get('dropzones').length !== 0) {
|
||||
this.restart_colours();
|
||||
for (var dropzoneno in this.get('dropzones')) {
|
||||
var colourfordropzone = this.get_next_colour();
|
||||
var d = this.get('dropzones')[dropzoneno];
|
||||
this.draw_drop_zone(dropzoneno, d.markertext,
|
||||
d.shape, d.coords, colourfordropzone, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Determine what drag items need to be shown and
|
||||
* return coords of all drag items except any that are currently being dragged
|
||||
* based on contents of hidden inputs and whether drags are 'infinite' or how many drags should be shown.
|
||||
*/
|
||||
get_coords : function (input) {
|
||||
var choiceno = this.get_choiceno_for_node(input);
|
||||
var fv = input.get('value');
|
||||
var infinite = input.hasClass('infinite');
|
||||
var noofdrags = this.get_noofdrags_for_node(input);
|
||||
var dragging = (null !== this.doc.drag_item_being_dragged(choiceno));
|
||||
var coords = [];
|
||||
if (fv !== '') {
|
||||
var coordsstrings = fv.split(';');
|
||||
for (var i = 0; i < coordsstrings.length; i++) {
|
||||
coords[coords.length] = this.convert_to_window_xy(coordsstrings[i].split(','));
|
||||
}
|
||||
}
|
||||
var displayeddrags = coords.length + (dragging ? 1 : 0);
|
||||
if (infinite || (displayeddrags < noofdrags)) {
|
||||
coords[coords.length] = this.drag_home_xy(choiceno);
|
||||
}
|
||||
return coords;
|
||||
},
|
||||
drag_home_xy : function (choiceno) {
|
||||
var dragitemhome = this.doc.drag_item_home(choiceno);
|
||||
return [dragitemhome.getX(), dragitemhome.getY() - 12];
|
||||
},
|
||||
get_choiceno_for_node : function(node) {
|
||||
return Number(this.doc.get_classname_numeric_suffix(node, 'choice'));
|
||||
},
|
||||
get_itemno_for_node : function(node) {
|
||||
return Number(this.doc.get_classname_numeric_suffix(node, 'item'));
|
||||
},
|
||||
get_noofdrags_for_node : function(node) {
|
||||
return Number(this.doc.get_classname_numeric_suffix(node, 'noofdrags'));
|
||||
},
|
||||
|
||||
// Keyboard accessibility stuff below here.
|
||||
drop_zone_key_press : function (e) {
|
||||
var dragitem = e.target;
|
||||
var xy = dragitem.getXY();
|
||||
switch (e.direction) {
|
||||
case 'left' :
|
||||
xy[0] -= 1;
|
||||
break;
|
||||
case 'right' :
|
||||
xy[0] += 1;
|
||||
break;
|
||||
case 'down' :
|
||||
xy[1] += 1;
|
||||
break;
|
||||
case 'up' :
|
||||
xy[1] -= 1;
|
||||
break;
|
||||
case 'remove' :
|
||||
xy = null;
|
||||
break;
|
||||
}
|
||||
var choiceno = this.get_choiceno_for_node(dragitem);
|
||||
if (xy !== null) {
|
||||
xy = this.constrain_to_bgimg(xy);
|
||||
} else {
|
||||
xy = this.drag_home_xy(choiceno);
|
||||
}
|
||||
e.preventDefault();
|
||||
dragitem.setXY(xy);
|
||||
this.save_all_xy_for_choice(choiceno, null);
|
||||
}
|
||||
}, {NAME : DDMARKERQUESTIONNAME, ATTRS : {dropzones:{value:[]}}});
|
||||
|
||||
Y.Event.define('dragchange', {
|
||||
// Webkit and IE repeat keydown when you hold down arrow keys.
|
||||
// Opera links keypress to page scroll; others keydown.
|
||||
// Firefox prevents page scroll via preventDefault() on either
|
||||
// keydown or keypress.
|
||||
_event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress',
|
||||
|
||||
_keys: {
|
||||
'32': 'remove', // Space
|
||||
'37': 'left', // Left arrow
|
||||
'38': 'up', // Up arrow
|
||||
'39': 'right', // Right arrow
|
||||
'40': 'down', // Down arrow
|
||||
'65': 'left', // a
|
||||
'87': 'up', // w
|
||||
'68': 'right', // d
|
||||
'83': 'down', // s
|
||||
'27': 'remove' // Escape
|
||||
},
|
||||
|
||||
_keyHandler: function (e, notifier) {
|
||||
if (this._keys[e.keyCode]) {
|
||||
e.direction = this._keys[e.keyCode];
|
||||
notifier.fire(e);
|
||||
}
|
||||
},
|
||||
|
||||
on: function (node, sub, notifier) {
|
||||
sub._detacher = node.on(this._event, this._keyHandler,
|
||||
this, notifier);
|
||||
}
|
||||
});
|
||||
M.qtype_ddmarker.init_question = function(config) {
|
||||
return new DDMARKER_QUESTION(config);
|
||||
};
|
||||
|
||||
|
||||
}, '@VERSION@', {"requires": ["node", "event-resize", "dd", "dd-drop", "dd-constrain", "graphics"]});
|
2
question/type/ddmarker/yui/build/moodle-qtype_ddmarker-dd/moodle-qtype_ddmarker-dd-min.js
vendored
Normal file
2
question/type/ddmarker/yui/build/moodle-qtype_ddmarker-dd/moodle-qtype_ddmarker-dd-min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
565
question/type/ddmarker/yui/build/moodle-qtype_ddmarker-dd/moodle-qtype_ddmarker-dd.js
vendored
Normal file
565
question/type/ddmarker/yui/build/moodle-qtype_ddmarker-dd/moodle-qtype_ddmarker-dd.js
vendored
Normal file
|
@ -0,0 +1,565 @@
|
|||
YUI.add('moodle-qtype_ddmarker-dd', function (Y, NAME) {
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
var DDMARKERDDNAME = 'moodle-qtype_ddmarker-dd';
|
||||
var DDMARKER_DD = function() {
|
||||
DDMARKER_DD.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
/**
|
||||
* This is the base class for the question rendering and question editing form code.
|
||||
*/
|
||||
Y.extend(DDMARKER_DD, Y.Base, {
|
||||
doc : null,
|
||||
polltimer : null,
|
||||
afterimageloaddone : false,
|
||||
graphics : null,
|
||||
poll_for_image_load : function (e, waitforimageconstrain, pause, doafterwords) {
|
||||
if (this.afterimageloaddone) {
|
||||
return;
|
||||
}
|
||||
var bgdone = this.doc.bg_img().get('complete');
|
||||
if (waitforimageconstrain) {
|
||||
bgdone = bgdone && this.doc.bg_img().hasClass('constrained');
|
||||
}
|
||||
if (bgdone) {
|
||||
if (this.polltimer !== null) {
|
||||
this.polltimer.cancel();
|
||||
this.polltimer = null;
|
||||
}
|
||||
this.doc.bg_img().detach('load', this.poll_for_image_load);
|
||||
if (pause !== 0) {
|
||||
Y.later(pause, this, doafterwords);
|
||||
} else {
|
||||
doafterwords.call(this);
|
||||
}
|
||||
this.afterimageloaddone = true;
|
||||
} else if (this.polltimer === null) {
|
||||
var pollarguments = [null, waitforimageconstrain, pause, doafterwords];
|
||||
this.polltimer =
|
||||
Y.later(1000, this, this.poll_for_image_load, pollarguments, true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Object to encapsulate operations on dd area.
|
||||
*/
|
||||
doc_structure : function () {
|
||||
var topnode = Y.one(this.get('topnode'));
|
||||
var dragitemsarea = topnode.one('div.dragitems');
|
||||
var dropbgarea = topnode.one('div.droparea');
|
||||
return {
|
||||
top_node : function() {
|
||||
return topnode;
|
||||
},
|
||||
bg_img : function() {
|
||||
return topnode.one('.dropbackground');
|
||||
},
|
||||
load_bg_img : function (url) {
|
||||
dropbgarea.setContent('<img class="dropbackground" src="' + url + '"/>');
|
||||
this.bg_img().on('load', this.on_image_load, this, 'bg_image');
|
||||
},
|
||||
drag_items : function() {
|
||||
return dragitemsarea.all('.dragitem');
|
||||
},
|
||||
drag_items_for_choice : function(choiceno) {
|
||||
return dragitemsarea.all('span.dragitem.choice' + choiceno);
|
||||
},
|
||||
drag_item_for_choice : function(choiceno, itemno) {
|
||||
return dragitemsarea.one('span.dragitem.choice' + choiceno +
|
||||
'.item' + itemno);
|
||||
},
|
||||
drag_item_being_dragged : function(choiceno) {
|
||||
return dragitemsarea.one('span.dragitem.beingdragged.choice' + choiceno);
|
||||
},
|
||||
drag_item_home : function (choiceno) {
|
||||
return dragitemsarea.one('span.draghome.choice' + choiceno);
|
||||
},
|
||||
drag_item_homes : function() {
|
||||
return dragitemsarea.all('span.draghome');
|
||||
},
|
||||
get_classname_numeric_suffix : function(node, prefix) {
|
||||
var classes = node.getAttribute('class');
|
||||
if (classes !== '') {
|
||||
var classesarr = classes.split(' ');
|
||||
for (var index = 0; index < classesarr.length; index++) {
|
||||
var patt1 = new RegExp('^' + prefix + '([0-9])+$');
|
||||
if (patt1.test(classesarr[index])) {
|
||||
var patt2 = new RegExp('([0-9])+$');
|
||||
var match = patt2.exec(classesarr[index]);
|
||||
return Number(match[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
inputs_for_choices : function () {
|
||||
return topnode.all('input.choices');
|
||||
},
|
||||
input_for_choice : function (choiceno) {
|
||||
return topnode.one('input.choice' + choiceno);
|
||||
},
|
||||
marker_texts : function () {
|
||||
return topnode.one('div.markertexts');
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
colours : ['#FFFFFF', '#B0C4DE', '#DCDCDC', '#D8BFD8',
|
||||
'#87CEFA','#DAA520', '#FFD700', '#F0E68C'],
|
||||
nextcolourindex : 0,
|
||||
restart_colours : function () {
|
||||
this.nextcolourindex = 0;
|
||||
},
|
||||
get_next_colour : function () {
|
||||
var colour = this.colours[this.nextcolourindex];
|
||||
this.nextcolourindex++;
|
||||
if (this.nextcolourindex === this.colours.length) {
|
||||
this.nextcolourindex = 0;
|
||||
}
|
||||
return colour;
|
||||
},
|
||||
convert_to_window_xy : function (bgimgxy) {
|
||||
return [Number(bgimgxy[0]) + this.doc.bg_img().getX() + 1,
|
||||
Number(bgimgxy[1]) + this.doc.bg_img().getY() + 1];
|
||||
},
|
||||
shapes : [],
|
||||
draw_drop_zone : function (dropzoneno, markertext, shape, coords, colour, link) {
|
||||
var existingmarkertext;
|
||||
if (link) {
|
||||
existingmarkertext = this.doc.marker_texts().one('span.markertext' + dropzoneno + ' a');
|
||||
} else {
|
||||
existingmarkertext = this.doc.marker_texts().one('span.markertext' + dropzoneno);
|
||||
}
|
||||
|
||||
if (existingmarkertext) {
|
||||
if (markertext !== '') {
|
||||
existingmarkertext.setContent(markertext);
|
||||
} else {
|
||||
existingmarkertext.remove(true);
|
||||
}
|
||||
} else if (markertext !== '') {
|
||||
var classnames = 'markertext markertext' + dropzoneno;
|
||||
if (link) {
|
||||
this.doc.marker_texts().append('<span class="' + classnames + '"><a href="#">' +
|
||||
markertext + '</a></span>');
|
||||
} else {
|
||||
this.doc.marker_texts().append('<span class="' + classnames + '">' +
|
||||
markertext + '</span>');
|
||||
}
|
||||
}
|
||||
var drawfunc = 'draw_shape_' + shape;
|
||||
if (this[drawfunc] instanceof Function){
|
||||
var xyfortext = this[drawfunc](dropzoneno, coords, colour);
|
||||
if (xyfortext !== null) {
|
||||
var markerspan = this.doc.top_node().one('div.ddarea div.markertexts span.markertext' + dropzoneno);
|
||||
if (markerspan !== null) {
|
||||
markerspan.setStyle('opacity', '0.6');
|
||||
xyfortext[0] -= markerspan.get('offsetWidth') / 2;
|
||||
xyfortext[1] -= markerspan.get('offsetHeight') / 2;
|
||||
markerspan.setXY(this.convert_to_window_xy(xyfortext));
|
||||
var markerspananchor = markerspan.one('a');
|
||||
if (markerspananchor !== null) {
|
||||
markerspananchor.once('click', function (e, dropzoneno) {
|
||||
var fill = this.shapes[dropzoneno].get('fill');
|
||||
fill.opacity = 1;
|
||||
this.shapes[dropzoneno].set('fill', fill);
|
||||
},
|
||||
this,
|
||||
dropzoneno
|
||||
);
|
||||
markerspananchor.set('tabIndex', 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
draw_shape_circle : function (dropzoneno, coords, colour) {
|
||||
var coordsparts = coords.match(/(\d+),(\d+);(\d+)/);
|
||||
if (coordsparts && coordsparts.length === 4) {
|
||||
var xy = [Number(coordsparts[1]) - coordsparts[3], Number(coordsparts[2]) - coordsparts[3]];
|
||||
if (this.coords_in_img(xy)) {
|
||||
var widthheight = [Number(coordsparts[3]) * 2, Number(coordsparts[3]) * 2];
|
||||
var shape = this.graphics.addShape({
|
||||
type: 'circle',
|
||||
width: widthheight[0],
|
||||
height: widthheight[1],
|
||||
fill: {
|
||||
color: colour,
|
||||
opacity: "0.5"
|
||||
},
|
||||
stroke: {
|
||||
weight: 1,
|
||||
color: "black"
|
||||
}
|
||||
});
|
||||
shape.setXY(this.convert_to_window_xy(xy));
|
||||
this.shapes[dropzoneno] = shape;
|
||||
return [Number(coordsparts[1]), Number(coordsparts[2])];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
draw_shape_rectangle : function (dropzoneno, coords, colour) {
|
||||
var coordsparts = coords.match(/(\d+),(\d+);(\d+),(\d+)/);
|
||||
if (coordsparts && coordsparts.length === 5) {
|
||||
var xy = [Number(coordsparts[1]), Number(coordsparts[2])];
|
||||
var widthheight = [Number(coordsparts[3]), Number(coordsparts[4])];
|
||||
if (this.coords_in_img([xy[0] + widthheight[0], xy[1] + widthheight[1]])) {
|
||||
var shape = this.graphics.addShape({
|
||||
type: 'rect',
|
||||
width: widthheight[0],
|
||||
height: widthheight[1],
|
||||
fill: {
|
||||
color: colour,
|
||||
opacity: "0.5"
|
||||
},
|
||||
stroke: {
|
||||
weight: 1,
|
||||
color: "black"
|
||||
}
|
||||
});
|
||||
shape.setXY(this.convert_to_window_xy(xy));
|
||||
this.shapes[dropzoneno] = shape;
|
||||
return [Number(xy[0]) + widthheight[0] / 2, Number(xy[1]) + widthheight[1] / 2];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
},
|
||||
draw_shape_polygon : function (dropzoneno, coords, colour) {
|
||||
var coordsparts = coords.split(';');
|
||||
var xy = [];
|
||||
for (var i in coordsparts) {
|
||||
var parts = coordsparts[i].match(/^(\d+),(\d+)$/);
|
||||
if (parts !== null && this.coords_in_img([parts[1], parts[2]])) {
|
||||
xy[xy.length] = [parts[1], parts[2]];
|
||||
}
|
||||
}
|
||||
if (xy.length > 2) {
|
||||
var polygon = this.graphics.addShape({
|
||||
type: "path",
|
||||
stroke: {
|
||||
weight: 1,
|
||||
color: "black"
|
||||
},
|
||||
fill: {
|
||||
color: colour,
|
||||
opacity : "0.5"
|
||||
}
|
||||
});
|
||||
var maxxy = [0,0];
|
||||
var minxy = [this.doc.bg_img().get('width'), this.doc.bg_img().get('height')];
|
||||
for (i = 0; i < xy.length; i++) {
|
||||
//calculate min and max points to find center to show marker on
|
||||
minxy[0] = Math.min(xy[i][0], minxy[0]);
|
||||
minxy[1] = Math.min(xy[i][1], minxy[1]);
|
||||
maxxy[0] = Math.max(xy[i][0], maxxy[0]);
|
||||
maxxy[1] = Math.max(xy[i][1], maxxy[1]);
|
||||
if (i === 0) {
|
||||
polygon.moveTo(xy[i][0], xy[i][1]);
|
||||
} else {
|
||||
polygon.lineTo(xy[i][0], xy[i][1]);
|
||||
}
|
||||
}
|
||||
if (Number(xy[0][0]) !== Number(xy[xy.length - 1][0]) || Number(xy[0][1]) !== Number(xy[xy.length - 1][1])) {
|
||||
polygon.lineTo(xy[0][0], xy[0][1]); // Close polygon if not already closed.
|
||||
}
|
||||
polygon.end();
|
||||
polygon.setXY(this.doc.bg_img().getXY());
|
||||
this.shapes[dropzoneno] = polygon;
|
||||
return [(minxy[0] + maxxy[0]) / 2, (minxy[1] + maxxy[1]) / 2];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
coords_in_img : function (coords) {
|
||||
return (coords[0] <= this.doc.bg_img().get('width') &&
|
||||
coords[1] <= this.doc.bg_img().get('height'));
|
||||
}
|
||||
}, {
|
||||
NAME : DDMARKERDDNAME,
|
||||
ATTRS : {
|
||||
drops : {value : null},
|
||||
readonly : {value : false},
|
||||
topnode : {value : null}
|
||||
}
|
||||
});
|
||||
M.qtype_ddmarker = M.qtype_ddmarker || {};
|
||||
M.qtype_ddmarker.dd_base_class = DDMARKER_DD;
|
||||
|
||||
var DDMARKERQUESTIONNAME = 'ddmarker_question';
|
||||
var DDMARKER_QUESTION = function() {
|
||||
DDMARKER_QUESTION.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
/**
|
||||
* This is the code for question rendering.
|
||||
*/
|
||||
Y.extend(DDMARKER_QUESTION, M.qtype_ddmarker.dd_base_class, {
|
||||
initializer : function() {
|
||||
this.doc = this.doc_structure(this);
|
||||
this.poll_for_image_load(null, false, 0, this.after_image_load);
|
||||
this.doc.bg_img().after('load', this.poll_for_image_load, this,
|
||||
false, 0, this.after_image_load);
|
||||
},
|
||||
after_image_load : function () {
|
||||
this.redraw_drags_and_drops();
|
||||
Y.later(2000, this, this.redraw_drags_and_drops, [], true);
|
||||
},
|
||||
clone_new_drag_item : function (draghome, itemno) {
|
||||
var drag = draghome.cloneNode(true);
|
||||
drag.removeClass('draghome');
|
||||
drag.addClass('dragitem');
|
||||
drag.addClass('item' + itemno);
|
||||
drag.one('span.markertext').setStyle('opacity', 0.6);
|
||||
draghome.insert(drag, 'after');
|
||||
if (!this.get('readonly')) {
|
||||
this.draggable(drag);
|
||||
}
|
||||
return drag;
|
||||
},
|
||||
draggable : function (drag) {
|
||||
var dd = new Y.DD.Drag({
|
||||
node: drag,
|
||||
dragMode: 'intersect'
|
||||
}).plug(Y.Plugin.DDConstrained, {constrain2node: this.doc.top_node()});
|
||||
dd.after('drag:start', function(e){
|
||||
var dragnode = e.target.get('node');
|
||||
dragnode.addClass('beingdragged');
|
||||
var choiceno = this.get_choiceno_for_node(dragnode);
|
||||
var itemno = this.get_itemno_for_node(dragnode);
|
||||
if (itemno !== null) {
|
||||
dragnode.removeClass('item' + dragnode);
|
||||
}
|
||||
this.save_all_xy_for_choice(choiceno, null);
|
||||
this.redraw_drags_and_drops();
|
||||
}, this);
|
||||
dd.after('drag:end', function(e) {
|
||||
var dragnode = e.target.get('node');
|
||||
dragnode.removeClass('beingdragged');
|
||||
var choiceno = this.get_choiceno_for_node(dragnode);
|
||||
this.save_all_xy_for_choice(choiceno, dragnode);
|
||||
this.redraw_drags_and_drops();
|
||||
}, this);
|
||||
//--- keyboard accessibility
|
||||
drag.set('tabIndex', 0);
|
||||
drag.on('dragchange', this.drop_zone_key_press, this);
|
||||
},
|
||||
save_all_xy_for_choice: function (choiceno, dropped) {
|
||||
var coords = [];
|
||||
var bgimgxy;
|
||||
for (var i = 0; i <= this.doc.drag_items_for_choice(choiceno).size(); i++) {
|
||||
var dragitem = this.doc.drag_item_for_choice(choiceno, i);
|
||||
if (dragitem) {
|
||||
dragitem.removeClass('item' + i);
|
||||
if (!dragitem.hasClass('beingdragged')) {
|
||||
bgimgxy = this.convert_to_bg_img_xy(dragitem.getXY());
|
||||
if (this.xy_in_bgimg(bgimgxy)) {
|
||||
dragitem.removeClass('item' + i);
|
||||
dragitem.addClass('item' + coords.length);
|
||||
coords[coords.length] = bgimgxy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dropped !== null){
|
||||
bgimgxy = this.convert_to_bg_img_xy(dropped.getXY());
|
||||
dropped.addClass('item' + coords.length);
|
||||
if (this.xy_in_bgimg(bgimgxy)) {
|
||||
coords[coords.length] = bgimgxy;
|
||||
}
|
||||
}
|
||||
this.set_form_value(choiceno, coords.join(';'));
|
||||
},
|
||||
reset_drag_xy : function (choiceno) {
|
||||
this.set_form_value(choiceno, '');
|
||||
},
|
||||
set_form_value : function (choiceno, value) {
|
||||
this.doc.input_for_choice(choiceno).set('value', value);
|
||||
},
|
||||
//make sure xy value is not out of bounds of bg image
|
||||
xy_in_bgimg : function (bgimgxy) {
|
||||
if ((bgimgxy[0] < 0) ||
|
||||
(bgimgxy[1] < 0) ||
|
||||
(bgimgxy[0] > this.doc.bg_img().get('width')) ||
|
||||
(bgimgxy[1] > this.doc.bg_img().get('height'))){
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
constrain_to_bgimg : function (windowxy) {
|
||||
var bgimgxy = this.convert_to_bg_img_xy(windowxy);
|
||||
bgimgxy[0] = Math.max(0, bgimgxy[0]);
|
||||
bgimgxy[1] = Math.max(0, bgimgxy[1]);
|
||||
bgimgxy[0] = Math.min(this.doc.bg_img().get('width'), bgimgxy[0]);
|
||||
bgimgxy[1] = Math.min(this.doc.bg_img().get('height'), bgimgxy[1]);
|
||||
return this.convert_to_window_xy(bgimgxy);
|
||||
},
|
||||
convert_to_bg_img_xy : function (windowxy) {
|
||||
return [Number(windowxy[0]) - this.doc.bg_img().getX() - 1,
|
||||
Number(windowxy[1]) - this.doc.bg_img().getY() - 1];
|
||||
},
|
||||
redraw_drags_and_drops : function() {
|
||||
this.doc.drag_items().each(function(item) {
|
||||
//if (!item.hasClass('beingdragged')){
|
||||
item.addClass('unneeded');
|
||||
//}
|
||||
}, this);
|
||||
this.doc.inputs_for_choices().each(function (input) {
|
||||
var choiceno = this.get_choiceno_for_node(input);
|
||||
var coords = this.get_coords(input);
|
||||
var dragitemhome = this.doc.drag_item_home(choiceno);
|
||||
for (var i = 0; i < coords.length; i++) {
|
||||
var dragitem = this.doc.drag_item_for_choice(choiceno, i);
|
||||
if (!dragitem || dragitem.hasClass('beingdragged')) {
|
||||
dragitem = this.clone_new_drag_item(dragitemhome, i);
|
||||
} else {
|
||||
dragitem.removeClass('unneeded');
|
||||
}
|
||||
dragitem.setXY(coords[i]);
|
||||
}
|
||||
}, this);
|
||||
this.doc.drag_items().each(function(item) {
|
||||
if (item.hasClass('unneeded') && !item.hasClass('beingdragged')) {
|
||||
item.remove(true);
|
||||
}
|
||||
}, this);
|
||||
if (this.graphics !== null) {
|
||||
this.graphics.clear();
|
||||
} else {
|
||||
this.graphics = new Y.Graphic(
|
||||
{render:this.doc.top_node().one("div.ddarea div.dropzones")}
|
||||
);
|
||||
}
|
||||
if (this.get('dropzones').length !== 0) {
|
||||
this.restart_colours();
|
||||
for (var dropzoneno in this.get('dropzones')) {
|
||||
var colourfordropzone = this.get_next_colour();
|
||||
var d = this.get('dropzones')[dropzoneno];
|
||||
this.draw_drop_zone(dropzoneno, d.markertext,
|
||||
d.shape, d.coords, colourfordropzone, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Determine what drag items need to be shown and
|
||||
* return coords of all drag items except any that are currently being dragged
|
||||
* based on contents of hidden inputs and whether drags are 'infinite' or how many drags should be shown.
|
||||
*/
|
||||
get_coords : function (input) {
|
||||
var choiceno = this.get_choiceno_for_node(input);
|
||||
var fv = input.get('value');
|
||||
var infinite = input.hasClass('infinite');
|
||||
var noofdrags = this.get_noofdrags_for_node(input);
|
||||
var dragging = (null !== this.doc.drag_item_being_dragged(choiceno));
|
||||
var coords = [];
|
||||
if (fv !== '') {
|
||||
var coordsstrings = fv.split(';');
|
||||
for (var i = 0; i < coordsstrings.length; i++) {
|
||||
coords[coords.length] = this.convert_to_window_xy(coordsstrings[i].split(','));
|
||||
}
|
||||
}
|
||||
var displayeddrags = coords.length + (dragging ? 1 : 0);
|
||||
if (infinite || (displayeddrags < noofdrags)) {
|
||||
coords[coords.length] = this.drag_home_xy(choiceno);
|
||||
}
|
||||
return coords;
|
||||
},
|
||||
drag_home_xy : function (choiceno) {
|
||||
var dragitemhome = this.doc.drag_item_home(choiceno);
|
||||
return [dragitemhome.getX(), dragitemhome.getY() - 12];
|
||||
},
|
||||
get_choiceno_for_node : function(node) {
|
||||
return Number(this.doc.get_classname_numeric_suffix(node, 'choice'));
|
||||
},
|
||||
get_itemno_for_node : function(node) {
|
||||
return Number(this.doc.get_classname_numeric_suffix(node, 'item'));
|
||||
},
|
||||
get_noofdrags_for_node : function(node) {
|
||||
return Number(this.doc.get_classname_numeric_suffix(node, 'noofdrags'));
|
||||
},
|
||||
|
||||
// Keyboard accessibility stuff below here.
|
||||
drop_zone_key_press : function (e) {
|
||||
var dragitem = e.target;
|
||||
var xy = dragitem.getXY();
|
||||
switch (e.direction) {
|
||||
case 'left' :
|
||||
xy[0] -= 1;
|
||||
break;
|
||||
case 'right' :
|
||||
xy[0] += 1;
|
||||
break;
|
||||
case 'down' :
|
||||
xy[1] += 1;
|
||||
break;
|
||||
case 'up' :
|
||||
xy[1] -= 1;
|
||||
break;
|
||||
case 'remove' :
|
||||
xy = null;
|
||||
break;
|
||||
}
|
||||
var choiceno = this.get_choiceno_for_node(dragitem);
|
||||
if (xy !== null) {
|
||||
xy = this.constrain_to_bgimg(xy);
|
||||
} else {
|
||||
xy = this.drag_home_xy(choiceno);
|
||||
}
|
||||
e.preventDefault();
|
||||
dragitem.setXY(xy);
|
||||
this.save_all_xy_for_choice(choiceno, null);
|
||||
}
|
||||
}, {NAME : DDMARKERQUESTIONNAME, ATTRS : {dropzones:{value:[]}}});
|
||||
|
||||
Y.Event.define('dragchange', {
|
||||
// Webkit and IE repeat keydown when you hold down arrow keys.
|
||||
// Opera links keypress to page scroll; others keydown.
|
||||
// Firefox prevents page scroll via preventDefault() on either
|
||||
// keydown or keypress.
|
||||
_event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress',
|
||||
|
||||
_keys: {
|
||||
'32': 'remove', // Space
|
||||
'37': 'left', // Left arrow
|
||||
'38': 'up', // Up arrow
|
||||
'39': 'right', // Right arrow
|
||||
'40': 'down', // Down arrow
|
||||
'65': 'left', // a
|
||||
'87': 'up', // w
|
||||
'68': 'right', // d
|
||||
'83': 'down', // s
|
||||
'27': 'remove' // Escape
|
||||
},
|
||||
|
||||
_keyHandler: function (e, notifier) {
|
||||
if (this._keys[e.keyCode]) {
|
||||
e.direction = this._keys[e.keyCode];
|
||||
notifier.fire(e);
|
||||
}
|
||||
},
|
||||
|
||||
on: function (node, sub, notifier) {
|
||||
sub._detacher = node.on(this._event, this._keyHandler,
|
||||
this, notifier);
|
||||
}
|
||||
});
|
||||
M.qtype_ddmarker.init_question = function(config) {
|
||||
return new DDMARKER_QUESTION(config);
|
||||
};
|
||||
|
||||
|
||||
}, '@VERSION@', {"requires": ["node", "event-resize", "dd", "dd-drop", "dd-constrain", "graphics"]});
|
272
question/type/ddmarker/yui/build/moodle-qtype_ddmarker-form/moodle-qtype_ddmarker-form-debug.js
vendored
Normal file
272
question/type/ddmarker/yui/build/moodle-qtype_ddmarker-form/moodle-qtype_ddmarker-form-debug.js
vendored
Normal file
|
@ -0,0 +1,272 @@
|
|||
YUI.add('moodle-qtype_ddmarker-form', function (Y, NAME) {
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This is the question editing form code.
|
||||
*/
|
||||
var DDMARKERFORMNAME = 'moodle-qtype_ddmarker-form';
|
||||
var DDMARKER_FORM = function() {
|
||||
DDMARKER_FORM.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
Y.extend(DDMARKER_FORM, M.qtype_ddmarker.dd_base_class, {
|
||||
fp : null,
|
||||
|
||||
initializer : function() {
|
||||
this.fp = this.file_pickers();
|
||||
var tn = Y.one(this.get('topnode'));
|
||||
tn.one('div.fcontainer').append(
|
||||
'<div class="ddarea">' +
|
||||
'<div class="markertexts"></div>' +
|
||||
'<div class="droparea"></div>' +
|
||||
'<div class="dropzones"></div>' +
|
||||
'<div class="grid"></div>' +
|
||||
'</div>');
|
||||
this.doc = this.doc_structure(this);
|
||||
this.stop_selector_events();
|
||||
this.set_options_for_drag_item_selectors();
|
||||
this.setup_form_events();
|
||||
Y.later(500, this, this.update_drop_zones, [], true);
|
||||
Y.after(this.load_bg_image, M.form_filepicker, 'callback', this);
|
||||
this.load_bg_image();
|
||||
},
|
||||
|
||||
load_bg_image : function() {
|
||||
var bgimageurl = this.fp.file('bgimage').href;
|
||||
if (bgimageurl !== null) {
|
||||
this.doc.load_bg_img(bgimageurl);
|
||||
|
||||
var drop = new Y.DD.Drop({
|
||||
node: this.doc.bg_img()
|
||||
});
|
||||
|
||||
// Listen for a drop:hit on the background image.
|
||||
drop.on('drop:hit', function(e) {
|
||||
e.drag.get('node').setData('gooddrop', true);
|
||||
});
|
||||
|
||||
this.afterimageloaddone = false;
|
||||
this.doc.bg_img().on('load', this.constrain_image_size, this);
|
||||
}
|
||||
},
|
||||
|
||||
constrain_image_size : function (e) {
|
||||
var maxsize = this.get('maxsizes').bgimage;
|
||||
var reduceby = Math.max(e.target.get('width') / maxsize.width,
|
||||
e.target.get('height') / maxsize.height);
|
||||
if (reduceby > 1) {
|
||||
e.target.set('width', Math.floor(e.target.get('width') / reduceby));
|
||||
}
|
||||
e.target.addClass('constrained');
|
||||
e.target.detach('load', this.constrain_image_size);
|
||||
},
|
||||
|
||||
update_drop_zones : function () {
|
||||
|
||||
// Set up drop zones.
|
||||
if (this.graphics !== null) {
|
||||
this.graphics.destroy();
|
||||
}
|
||||
this.restart_colours();
|
||||
this.graphics = new Y.Graphic({render:"div.ddarea div.dropzones"});
|
||||
var noofdropzones = this.form.get_form_value('nodropzone', []);
|
||||
for (var dropzoneno = 0; dropzoneno < noofdropzones; dropzoneno++) {
|
||||
var dragitemno = this.form.get_form_value('drops', [dropzoneno, 'choice']);
|
||||
var markertext = this.get_marker_text(dragitemno);
|
||||
var shape = this.form.get_form_value('drops', [dropzoneno, 'shape']);
|
||||
var coords = this.get_coords(dropzoneno);
|
||||
var colourfordropzone = this.get_next_colour();
|
||||
Y.one('input#id_drops_' + dropzoneno + '_coords')
|
||||
.setStyle('background-color', colourfordropzone);
|
||||
this.draw_drop_zone(dropzoneno, markertext,
|
||||
shape, coords, colourfordropzone, false);
|
||||
}
|
||||
Y.one('div.ddarea .grid')
|
||||
.setXY(this.doc.bg_img().getXY())
|
||||
.setStyle('width', this.doc.bg_img().get('width'))
|
||||
.setStyle('height', this.doc.bg_img().get('height'));
|
||||
},
|
||||
|
||||
get_coords : function (dropzoneno) {
|
||||
var coords = this.form.get_form_value('drops', [dropzoneno, 'coords']);
|
||||
return coords.replace(new RegExp("\\s*", 'g'), '');
|
||||
},
|
||||
get_marker_text : function (markerno) {
|
||||
if (Number(markerno) !== 0) {
|
||||
var label = this.form.get_form_value('drags', [markerno - 1, 'label']);
|
||||
return label.replace(new RegExp("^\\s*(.*)\\s*$"), "$1");
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
set_options_for_drag_item_selectors : function () {
|
||||
var dragitemsoptions = {0: ''};
|
||||
for (var i = 1; i <= this.form.get_form_value('noitems', []); i++) {
|
||||
var label = this.get_marker_text(i);
|
||||
if (label !== "") {
|
||||
dragitemsoptions[i] = Y.Escape.html(label);
|
||||
}
|
||||
}
|
||||
// Get all the currently selected drags for each drop.
|
||||
var selectedvalues = [];
|
||||
var selector;
|
||||
for (i = 0; i < this.form.get_form_value('nodropzone', []); i++) {
|
||||
selector = Y.one('#id_drops_' + i + '_choice');
|
||||
selectedvalues[i] = Number(selector.get('value'));
|
||||
}
|
||||
for (i = 0; i < this.form.get_form_value('nodropzone', []); i++) {
|
||||
selector = Y.one('#id_drops_' + i + '_choice');
|
||||
// Remove all options for drag choice.
|
||||
selector.all('option').remove(true);
|
||||
// And recreate the options.
|
||||
for (var value in dragitemsoptions) {
|
||||
value = Number(value);
|
||||
var option = '<option value="' + value + '">' + dragitemsoptions[value] + '</option>';
|
||||
selector.append(option);
|
||||
var optionnode = selector.one('option[value="' + value + '"]');
|
||||
// Is this the currently selected value?
|
||||
if (value === selectedvalues[i]) {
|
||||
optionnode.set('selected', true);
|
||||
} else {
|
||||
// It is not the currently selected value, is it selectable?
|
||||
if (value !== 0) { // The 'no item' option is always selectable.
|
||||
// Variables to hold form values about this drag item.
|
||||
var noofdrags = this.form.get_form_value('drags', [value - 1, 'noofdrags']);
|
||||
if (Number(noofdrags) !== 0) { // 'noofdrags == 0' means infinite.
|
||||
// Go through all selected values in drop downs.
|
||||
for (var k in selectedvalues) {
|
||||
// Count down 'noofdrags' and if reach zero then set disabled option for this drag item.
|
||||
if (Number(selectedvalues[k]) === value) {
|
||||
if (Number(noofdrags) === 1) {
|
||||
optionnode.set('disabled', true);
|
||||
break;
|
||||
} else {
|
||||
noofdrags--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
stop_selector_events : function () {
|
||||
Y.all('fieldset#id_dropzoneheader select').detachAll();
|
||||
},
|
||||
|
||||
setup_form_events : function () {
|
||||
//events triggered by changes to form data
|
||||
|
||||
// Changes to labels.
|
||||
Y.all('fieldset#id_draggableitemheader input').on('change', function () {
|
||||
this.set_options_for_drag_item_selectors();
|
||||
}, this);
|
||||
|
||||
// Changes to selected drag item.
|
||||
Y.all('fieldset#id_draggableitemheader select').on('change', function () {
|
||||
this.set_options_for_drag_item_selectors();
|
||||
}, this);
|
||||
|
||||
// Change in selected item.
|
||||
Y.all('fieldset#id_dropzoneheader select').on('change', function () {
|
||||
this.set_options_for_drag_item_selectors();
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Low level operations on form.
|
||||
*/
|
||||
form : {
|
||||
to_name_with_index : function(name, indexes) {
|
||||
var indexstring = name;
|
||||
for (var i = 0; i < indexes.length; i++) {
|
||||
indexstring = indexstring + '[' + indexes[i] + ']';
|
||||
}
|
||||
return indexstring;
|
||||
},
|
||||
get_el : function (name, indexes) {
|
||||
var form = document.getElementById('mform1');
|
||||
return form.elements[this.to_name_with_index(name, indexes)];
|
||||
},
|
||||
get_form_value : function(name, indexes) {
|
||||
var el = this.get_el(name, indexes);
|
||||
if (el.type === 'checkbox') {
|
||||
return el.checked;
|
||||
} else {
|
||||
return el.value;
|
||||
}
|
||||
},
|
||||
set_form_value : function(name, indexes, value) {
|
||||
var el = this.get_el(name, indexes);
|
||||
if (el.type === 'checkbox') {
|
||||
el.checked = value;
|
||||
} else {
|
||||
el.value = value;
|
||||
}
|
||||
},
|
||||
from_name_with_index : function(name) {
|
||||
var toreturn = {};
|
||||
toreturn.indexes = [];
|
||||
var bracket = name.indexOf('[');
|
||||
toreturn.name = name.substring(0, bracket);
|
||||
while (bracket !== -1) {
|
||||
var end = name.indexOf(']', bracket + 1);
|
||||
toreturn.indexes.push(name.substring(bracket + 1, end));
|
||||
bracket = name.indexOf('[', end + 1);
|
||||
}
|
||||
return toreturn;
|
||||
}
|
||||
},
|
||||
|
||||
file_pickers : function () {
|
||||
var draftitemidstoname;
|
||||
var nametoparentnode;
|
||||
if (draftitemidstoname === undefined) {
|
||||
draftitemidstoname = {};
|
||||
nametoparentnode = {};
|
||||
var filepickers = Y.all('form.mform input.filepickerhidden');
|
||||
filepickers.each(function(filepicker) {
|
||||
draftitemidstoname[filepicker.get('value')] = filepicker.get('name');
|
||||
nametoparentnode[filepicker.get('name')] = filepicker.get('parentNode');
|
||||
}, this);
|
||||
}
|
||||
var toreturn = {
|
||||
file : function (name) {
|
||||
var parentnode = nametoparentnode[name];
|
||||
var fileanchor = parentnode.one('div.filepicker-filelist a');
|
||||
if (fileanchor) {
|
||||
return {href : fileanchor.get('href'), name : fileanchor.get('innerHTML')};
|
||||
} else {
|
||||
return {href : null, name : null};
|
||||
}
|
||||
},
|
||||
name : function (draftitemid) {
|
||||
return draftitemidstoname[draftitemid];
|
||||
}
|
||||
};
|
||||
return toreturn;
|
||||
}
|
||||
},{NAME : DDMARKERFORMNAME, ATTRS : {maxsizes:{value:null}}});
|
||||
|
||||
M.qtype_ddmarker = M.qtype_ddmarker || {};
|
||||
M.qtype_ddmarker.init_form = function(config) {
|
||||
return new DDMARKER_FORM(config);
|
||||
};
|
||||
|
||||
|
||||
}, '@VERSION@', {"requires": ["moodle-qtype_ddmarker-dd", "form_filepicker", "graphics", "escape"]});
|
|
@ -0,0 +1 @@
|
|||
YUI.add("moodle-qtype_ddmarker-form",function(e,t){var n="moodle-qtype_ddmarker-form",r=function(){r.superclass.constructor.apply(this,arguments)};e.extend(r,M.qtype_ddmarker.dd_base_class,{fp:null,initializer:function(){this.fp=this.file_pickers();var t=e.one(this.get("topnode"));t.one("div.fcontainer").append('<div class="ddarea"><div class="markertexts"></div><div class="droparea"></div><div class="dropzones"></div><div class="grid"></div></div>'),this.doc=this.doc_structure(this),this.stop_selector_events(),this.set_options_for_drag_item_selectors(),this.setup_form_events(),e.later(500,this,this.update_drop_zones,[],!0),e.after(this.load_bg_image,M.form_filepicker,"callback",this),this.load_bg_image()},load_bg_image:function(){var t=this.fp.file("bgimage").href;if(t!==null){this.doc.load_bg_img(t);var n=new e.DD.Drop({node:this.doc.bg_img()});n.on("drop:hit",function(e){e.drag.get("node").setData("gooddrop",!0)}),this.afterimageloaddone=!1,this.doc.bg_img().on("load",this.constrain_image_size,this)}},constrain_image_size:function(e){var t=this.get("maxsizes").bgimage,n=Math.max(e.target.get("width")/t.width,e.target.get("height")/t.height);n>1&&e.target.set("width",Math.floor(e.target.get("width")/n)),e.target.addClass("constrained"),e.target.detach("load",this.constrain_image_size)},update_drop_zones:function(){this.graphics!==null&&this.graphics.destroy(),this.restart_colours(),this.graphics=new e.Graphic({render:"div.ddarea div.dropzones"});var t=this.form.get_form_value("nodropzone",[]);for(var n=0;n<t;n++){var r=this.form.get_form_value("drops",[n,"choice"]),i=this.get_marker_text(r),s=this.form.get_form_value("drops",[n,"shape"]),o=this.get_coords(n),u=this.get_next_colour();e.one("input#id_drops_"+n+"_coords").setStyle("background-color",u),this.draw_drop_zone(n,i,s,o,u,!1)}e.one("div.ddarea .grid").setXY(this.doc.bg_img().getXY()).setStyle("width",this.doc.bg_img().get("width")).setStyle("height",this.doc.bg_img().get("height"))},get_coords:function(e){var t=this.form.get_form_value("drops",[e,"coords"]);return t.replace(new RegExp("\\s*","g"),"")},get_marker_text:function(e){if(Number(e)!==0){var t=this.form.get_form_value("drags",[e-1,"label"]);return t.replace(new RegExp("^\\s*(.*)\\s*$"),"$1")}return""},set_options_for_drag_item_selectors:function(){var t={0:""};for(var n=1;n<=this.form.get_form_value("noitems",[]);n++){var r=this.get_marker_text(n);r!==""&&(t[n]=e.Escape.html(r))}var i=[],s;for(n=0;n<this.form.get_form_value("nodropzone",[]);n++)s=e.one("#id_drops_"+n+"_choice"),i[n]=Number(s.get("value"));for(n=0;n<this.form.get_form_value("nodropzone",[]);n++){s=e.one("#id_drops_"+n+"_choice"),s.all("option").remove(!0);for(var o in t){o=Number(o);var u='<option value="'+o+'">'+t[o]+"</option>";s.append(u);var a=s.one('option[value="'+o+'"]');if(o===i[n])a.set("selected",!0);else if(o!==0){var f=this.form.get_form_value("drags",[o-1,"noofdrags"]);if(Number(f)!==0)for(var l in i)if(Number(i[l])===o){if(Number(f)===1){a.set("disabled",!0);break}f--}}}}},stop_selector_events:function(){e.all("fieldset#id_dropzoneheader select").detachAll()},setup_form_events:function(){e.all("fieldset#id_draggableitemheader input").on("change",function(){this.set_options_for_drag_item_selectors()},this),e.all("fieldset#id_draggableitemheader select").on("change",function(){this.set_options_for_drag_item_selectors()},this),e.all("fieldset#id_dropzoneheader select").on("change",function(){this.set_options_for_drag_item_selectors()},this)},form:{to_name_with_index:function(e,t){var n=e;for(var r=0;r<t.length;r++)n=n+"["+t[r]+"]";return n},get_el:function(e,t){var n=document.getElementById("mform1");return n.elements[this.to_name_with_index(e,t)]},get_form_value:function(e,t){var n=this.get_el(e,t);return n.type==="checkbox"?n.checked:n.value},set_form_value:function(e,t,n){var r=this.get_el(e,t);r.type==="checkbox"?r.checked=n:r.value=n},from_name_with_index:function(e){var t={};t.indexes=[];var n=e.indexOf("[");t.name=e.substring(0,n);while(n!==-1){var r=e.indexOf("]",n+1);t.indexes.push(e.substring(n+1,r)),n=e.indexOf("[",r+1)}return t}},file_pickers:function(){var t,n;if(t===undefined){t={},n={};var r=e.all("form.mform input.filepickerhidden");r.each(function(e){t[e.get("value")]=e.get("name"),n[e.get("name")]=e.get("parentNode")},this)}var i={file:function(e){var t=n[e],r=t.one("div.filepicker-filelist a");return r?{href:r.get("href"),name:r.get("innerHTML")}:{href:null,name:null}},name:function(e){return t[e]}};return i}},{NAME:n,ATTRS:{maxsizes:{value:null}}}),M.qtype_ddmarker=M.qtype_ddmarker||{},M.qtype_ddmarker.init_form=function(e){return new r(e)}},"@VERSION@",{requires:["moodle-qtype_ddmarker-dd","form_filepicker","graphics","escape"]});
|
272
question/type/ddmarker/yui/build/moodle-qtype_ddmarker-form/moodle-qtype_ddmarker-form.js
vendored
Normal file
272
question/type/ddmarker/yui/build/moodle-qtype_ddmarker-form/moodle-qtype_ddmarker-form.js
vendored
Normal file
|
@ -0,0 +1,272 @@
|
|||
YUI.add('moodle-qtype_ddmarker-form', function (Y, NAME) {
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This is the question editing form code.
|
||||
*/
|
||||
var DDMARKERFORMNAME = 'moodle-qtype_ddmarker-form';
|
||||
var DDMARKER_FORM = function() {
|
||||
DDMARKER_FORM.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
Y.extend(DDMARKER_FORM, M.qtype_ddmarker.dd_base_class, {
|
||||
fp : null,
|
||||
|
||||
initializer : function() {
|
||||
this.fp = this.file_pickers();
|
||||
var tn = Y.one(this.get('topnode'));
|
||||
tn.one('div.fcontainer').append(
|
||||
'<div class="ddarea">' +
|
||||
'<div class="markertexts"></div>' +
|
||||
'<div class="droparea"></div>' +
|
||||
'<div class="dropzones"></div>' +
|
||||
'<div class="grid"></div>' +
|
||||
'</div>');
|
||||
this.doc = this.doc_structure(this);
|
||||
this.stop_selector_events();
|
||||
this.set_options_for_drag_item_selectors();
|
||||
this.setup_form_events();
|
||||
Y.later(500, this, this.update_drop_zones, [], true);
|
||||
Y.after(this.load_bg_image, M.form_filepicker, 'callback', this);
|
||||
this.load_bg_image();
|
||||
},
|
||||
|
||||
load_bg_image : function() {
|
||||
var bgimageurl = this.fp.file('bgimage').href;
|
||||
if (bgimageurl !== null) {
|
||||
this.doc.load_bg_img(bgimageurl);
|
||||
|
||||
var drop = new Y.DD.Drop({
|
||||
node: this.doc.bg_img()
|
||||
});
|
||||
|
||||
// Listen for a drop:hit on the background image.
|
||||
drop.on('drop:hit', function(e) {
|
||||
e.drag.get('node').setData('gooddrop', true);
|
||||
});
|
||||
|
||||
this.afterimageloaddone = false;
|
||||
this.doc.bg_img().on('load', this.constrain_image_size, this);
|
||||
}
|
||||
},
|
||||
|
||||
constrain_image_size : function (e) {
|
||||
var maxsize = this.get('maxsizes').bgimage;
|
||||
var reduceby = Math.max(e.target.get('width') / maxsize.width,
|
||||
e.target.get('height') / maxsize.height);
|
||||
if (reduceby > 1) {
|
||||
e.target.set('width', Math.floor(e.target.get('width') / reduceby));
|
||||
}
|
||||
e.target.addClass('constrained');
|
||||
e.target.detach('load', this.constrain_image_size);
|
||||
},
|
||||
|
||||
update_drop_zones : function () {
|
||||
|
||||
// Set up drop zones.
|
||||
if (this.graphics !== null) {
|
||||
this.graphics.destroy();
|
||||
}
|
||||
this.restart_colours();
|
||||
this.graphics = new Y.Graphic({render:"div.ddarea div.dropzones"});
|
||||
var noofdropzones = this.form.get_form_value('nodropzone', []);
|
||||
for (var dropzoneno = 0; dropzoneno < noofdropzones; dropzoneno++) {
|
||||
var dragitemno = this.form.get_form_value('drops', [dropzoneno, 'choice']);
|
||||
var markertext = this.get_marker_text(dragitemno);
|
||||
var shape = this.form.get_form_value('drops', [dropzoneno, 'shape']);
|
||||
var coords = this.get_coords(dropzoneno);
|
||||
var colourfordropzone = this.get_next_colour();
|
||||
Y.one('input#id_drops_' + dropzoneno + '_coords')
|
||||
.setStyle('background-color', colourfordropzone);
|
||||
this.draw_drop_zone(dropzoneno, markertext,
|
||||
shape, coords, colourfordropzone, false);
|
||||
}
|
||||
Y.one('div.ddarea .grid')
|
||||
.setXY(this.doc.bg_img().getXY())
|
||||
.setStyle('width', this.doc.bg_img().get('width'))
|
||||
.setStyle('height', this.doc.bg_img().get('height'));
|
||||
},
|
||||
|
||||
get_coords : function (dropzoneno) {
|
||||
var coords = this.form.get_form_value('drops', [dropzoneno, 'coords']);
|
||||
return coords.replace(new RegExp("\\s*", 'g'), '');
|
||||
},
|
||||
get_marker_text : function (markerno) {
|
||||
if (Number(markerno) !== 0) {
|
||||
var label = this.form.get_form_value('drags', [markerno - 1, 'label']);
|
||||
return label.replace(new RegExp("^\\s*(.*)\\s*$"), "$1");
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
set_options_for_drag_item_selectors : function () {
|
||||
var dragitemsoptions = {0: ''};
|
||||
for (var i = 1; i <= this.form.get_form_value('noitems', []); i++) {
|
||||
var label = this.get_marker_text(i);
|
||||
if (label !== "") {
|
||||
dragitemsoptions[i] = Y.Escape.html(label);
|
||||
}
|
||||
}
|
||||
// Get all the currently selected drags for each drop.
|
||||
var selectedvalues = [];
|
||||
var selector;
|
||||
for (i = 0; i < this.form.get_form_value('nodropzone', []); i++) {
|
||||
selector = Y.one('#id_drops_' + i + '_choice');
|
||||
selectedvalues[i] = Number(selector.get('value'));
|
||||
}
|
||||
for (i = 0; i < this.form.get_form_value('nodropzone', []); i++) {
|
||||
selector = Y.one('#id_drops_' + i + '_choice');
|
||||
// Remove all options for drag choice.
|
||||
selector.all('option').remove(true);
|
||||
// And recreate the options.
|
||||
for (var value in dragitemsoptions) {
|
||||
value = Number(value);
|
||||
var option = '<option value="' + value + '">' + dragitemsoptions[value] + '</option>';
|
||||
selector.append(option);
|
||||
var optionnode = selector.one('option[value="' + value + '"]');
|
||||
// Is this the currently selected value?
|
||||
if (value === selectedvalues[i]) {
|
||||
optionnode.set('selected', true);
|
||||
} else {
|
||||
// It is not the currently selected value, is it selectable?
|
||||
if (value !== 0) { // The 'no item' option is always selectable.
|
||||
// Variables to hold form values about this drag item.
|
||||
var noofdrags = this.form.get_form_value('drags', [value - 1, 'noofdrags']);
|
||||
if (Number(noofdrags) !== 0) { // 'noofdrags == 0' means infinite.
|
||||
// Go through all selected values in drop downs.
|
||||
for (var k in selectedvalues) {
|
||||
// Count down 'noofdrags' and if reach zero then set disabled option for this drag item.
|
||||
if (Number(selectedvalues[k]) === value) {
|
||||
if (Number(noofdrags) === 1) {
|
||||
optionnode.set('disabled', true);
|
||||
break;
|
||||
} else {
|
||||
noofdrags--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
stop_selector_events : function () {
|
||||
Y.all('fieldset#id_dropzoneheader select').detachAll();
|
||||
},
|
||||
|
||||
setup_form_events : function () {
|
||||
//events triggered by changes to form data
|
||||
|
||||
// Changes to labels.
|
||||
Y.all('fieldset#id_draggableitemheader input').on('change', function () {
|
||||
this.set_options_for_drag_item_selectors();
|
||||
}, this);
|
||||
|
||||
// Changes to selected drag item.
|
||||
Y.all('fieldset#id_draggableitemheader select').on('change', function () {
|
||||
this.set_options_for_drag_item_selectors();
|
||||
}, this);
|
||||
|
||||
// Change in selected item.
|
||||
Y.all('fieldset#id_dropzoneheader select').on('change', function () {
|
||||
this.set_options_for_drag_item_selectors();
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Low level operations on form.
|
||||
*/
|
||||
form : {
|
||||
to_name_with_index : function(name, indexes) {
|
||||
var indexstring = name;
|
||||
for (var i = 0; i < indexes.length; i++) {
|
||||
indexstring = indexstring + '[' + indexes[i] + ']';
|
||||
}
|
||||
return indexstring;
|
||||
},
|
||||
get_el : function (name, indexes) {
|
||||
var form = document.getElementById('mform1');
|
||||
return form.elements[this.to_name_with_index(name, indexes)];
|
||||
},
|
||||
get_form_value : function(name, indexes) {
|
||||
var el = this.get_el(name, indexes);
|
||||
if (el.type === 'checkbox') {
|
||||
return el.checked;
|
||||
} else {
|
||||
return el.value;
|
||||
}
|
||||
},
|
||||
set_form_value : function(name, indexes, value) {
|
||||
var el = this.get_el(name, indexes);
|
||||
if (el.type === 'checkbox') {
|
||||
el.checked = value;
|
||||
} else {
|
||||
el.value = value;
|
||||
}
|
||||
},
|
||||
from_name_with_index : function(name) {
|
||||
var toreturn = {};
|
||||
toreturn.indexes = [];
|
||||
var bracket = name.indexOf('[');
|
||||
toreturn.name = name.substring(0, bracket);
|
||||
while (bracket !== -1) {
|
||||
var end = name.indexOf(']', bracket + 1);
|
||||
toreturn.indexes.push(name.substring(bracket + 1, end));
|
||||
bracket = name.indexOf('[', end + 1);
|
||||
}
|
||||
return toreturn;
|
||||
}
|
||||
},
|
||||
|
||||
file_pickers : function () {
|
||||
var draftitemidstoname;
|
||||
var nametoparentnode;
|
||||
if (draftitemidstoname === undefined) {
|
||||
draftitemidstoname = {};
|
||||
nametoparentnode = {};
|
||||
var filepickers = Y.all('form.mform input.filepickerhidden');
|
||||
filepickers.each(function(filepicker) {
|
||||
draftitemidstoname[filepicker.get('value')] = filepicker.get('name');
|
||||
nametoparentnode[filepicker.get('name')] = filepicker.get('parentNode');
|
||||
}, this);
|
||||
}
|
||||
var toreturn = {
|
||||
file : function (name) {
|
||||
var parentnode = nametoparentnode[name];
|
||||
var fileanchor = parentnode.one('div.filepicker-filelist a');
|
||||
if (fileanchor) {
|
||||
return {href : fileanchor.get('href'), name : fileanchor.get('innerHTML')};
|
||||
} else {
|
||||
return {href : null, name : null};
|
||||
}
|
||||
},
|
||||
name : function (draftitemid) {
|
||||
return draftitemidstoname[draftitemid];
|
||||
}
|
||||
};
|
||||
return toreturn;
|
||||
}
|
||||
},{NAME : DDMARKERFORMNAME, ATTRS : {maxsizes:{value:null}}});
|
||||
|
||||
M.qtype_ddmarker = M.qtype_ddmarker || {};
|
||||
M.qtype_ddmarker.init_form = function(config) {
|
||||
return new DDMARKER_FORM(config);
|
||||
};
|
||||
|
||||
|
||||
}, '@VERSION@', {"requires": ["moodle-qtype_ddmarker-dd", "form_filepicker", "graphics", "escape"]});
|
549
question/type/ddmarker/yui/dd/dd.js
vendored
549
question/type/ddmarker/yui/dd/dd.js
vendored
|
@ -1,549 +0,0 @@
|
|||
YUI.add('moodle-qtype_ddmarker-dd', function(Y) {
|
||||
var DDMARKERDDNAME = 'ddmarker_dd';
|
||||
var DDMARKER_DD = function() {
|
||||
DDMARKER_DD.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
/**
|
||||
* This is the base class for the question rendering and question editing form code.
|
||||
*/
|
||||
Y.extend(DDMARKER_DD, Y.Base, {
|
||||
doc : null,
|
||||
polltimer : null,
|
||||
afterimageloaddone : false,
|
||||
graphics : null,
|
||||
poll_for_image_load : function (e, waitforimageconstrain, pause, doafterwords) {
|
||||
if (this.afterimageloaddone) {
|
||||
return;
|
||||
}
|
||||
var bgdone = this.doc.bg_img().get('complete');
|
||||
if (waitforimageconstrain) {
|
||||
bgdone = bgdone && this.doc.bg_img().hasClass('constrained');
|
||||
}
|
||||
if (bgdone) {
|
||||
if (this.polltimer !== null) {
|
||||
this.polltimer.cancel();
|
||||
this.polltimer = null;
|
||||
}
|
||||
this.doc.bg_img().detach('load', this.poll_for_image_load);
|
||||
if (pause !== 0) {
|
||||
Y.later(pause, this, doafterwords);
|
||||
} else {
|
||||
doafterwords.call(this);
|
||||
}
|
||||
this.afterimageloaddone = true;
|
||||
} else if (this.polltimer === null) {
|
||||
var pollarguments = [null, waitforimageconstrain, pause, doafterwords];
|
||||
this.polltimer =
|
||||
Y.later(1000, this, this.poll_for_image_load, pollarguments, true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Object to encapsulate operations on dd area.
|
||||
*/
|
||||
doc_structure : function () {
|
||||
var topnode = Y.one(this.get('topnode'));
|
||||
var dragitemsarea = topnode.one('div.dragitems');
|
||||
var dropbgarea = topnode.one('div.droparea');
|
||||
return {
|
||||
top_node : function() {
|
||||
return topnode;
|
||||
},
|
||||
bg_img : function() {
|
||||
return topnode.one('.dropbackground');
|
||||
},
|
||||
load_bg_img : function (url) {
|
||||
dropbgarea.setContent('<img class="dropbackground" src="' + url + '"/>');
|
||||
this.bg_img().on('load', this.on_image_load, this, 'bg_image');
|
||||
},
|
||||
drag_items : function() {
|
||||
return dragitemsarea.all('.dragitem');
|
||||
},
|
||||
drag_items_for_choice : function(choiceno) {
|
||||
return dragitemsarea.all('span.dragitem.choice' + choiceno);
|
||||
},
|
||||
drag_item_for_choice : function(choiceno, itemno) {
|
||||
return dragitemsarea.one('span.dragitem.choice' + choiceno +
|
||||
'.item' + itemno);
|
||||
},
|
||||
drag_item_being_dragged : function(choiceno) {
|
||||
return dragitemsarea.one('span.dragitem.beingdragged.choice' + choiceno);
|
||||
},
|
||||
drag_item_home : function (choiceno) {
|
||||
return dragitemsarea.one('span.draghome.choice' + choiceno);
|
||||
},
|
||||
drag_item_homes : function() {
|
||||
return dragitemsarea.all('span.draghome');
|
||||
},
|
||||
get_classname_numeric_suffix : function(node, prefix) {
|
||||
var classes = node.getAttribute('class');
|
||||
if (classes !== '') {
|
||||
var classesarr = classes.split(' ');
|
||||
for (var index = 0; index < classesarr.length; index++) {
|
||||
var patt1 = new RegExp('^' + prefix + '([0-9])+$');
|
||||
if (patt1.test(classesarr[index])) {
|
||||
var patt2 = new RegExp('([0-9])+$');
|
||||
var match = patt2.exec(classesarr[index]);
|
||||
return Number(match[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
inputs_for_choices : function () {
|
||||
return topnode.all('input.choices');
|
||||
},
|
||||
input_for_choice : function (choiceno) {
|
||||
return topnode.one('input.choice' + choiceno);
|
||||
},
|
||||
marker_texts : function () {
|
||||
return topnode.one('div.markertexts');
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
colours : ['#FFFFFF', '#B0C4DE', '#DCDCDC', '#D8BFD8',
|
||||
'#87CEFA','#DAA520', '#FFD700', '#F0E68C'],
|
||||
nextcolourindex : 0,
|
||||
restart_colours : function () {
|
||||
this.nextcolourindex = 0;
|
||||
},
|
||||
get_next_colour : function () {
|
||||
var colour = this.colours[this.nextcolourindex];
|
||||
this.nextcolourindex++;
|
||||
if (this.nextcolourindex === this.colours.length) {
|
||||
this.nextcolourindex = 0;
|
||||
}
|
||||
return colour;
|
||||
},
|
||||
convert_to_window_xy : function (bgimgxy) {
|
||||
return [Number(bgimgxy[0]) + this.doc.bg_img().getX() + 1,
|
||||
Number(bgimgxy[1]) + this.doc.bg_img().getY() + 1];
|
||||
},
|
||||
shapes : [],
|
||||
draw_drop_zone : function (dropzoneno, markertext, shape, coords, colour, link) {
|
||||
var existingmarkertext;
|
||||
if (link) {
|
||||
existingmarkertext = this.doc.marker_texts().one('span.markertext' + dropzoneno + ' a');
|
||||
} else {
|
||||
existingmarkertext = this.doc.marker_texts().one('span.markertext' + dropzoneno);
|
||||
}
|
||||
|
||||
if (existingmarkertext) {
|
||||
if (markertext !== '') {
|
||||
existingmarkertext.setContent(markertext);
|
||||
} else {
|
||||
existingmarkertext.remove(true);
|
||||
}
|
||||
} else if (markertext !== '') {
|
||||
var classnames = 'markertext markertext' + dropzoneno;
|
||||
if (link) {
|
||||
this.doc.marker_texts().append('<span class="' + classnames + '"><a href="#">' +
|
||||
markertext + '</a></span>');
|
||||
} else {
|
||||
this.doc.marker_texts().append('<span class="' + classnames + '">' +
|
||||
markertext + '</span>');
|
||||
}
|
||||
}
|
||||
var drawfunc = 'draw_shape_' + shape;
|
||||
if (this[drawfunc] instanceof Function){
|
||||
var xyfortext = this[drawfunc](dropzoneno, coords, colour);
|
||||
if (xyfortext !== null) {
|
||||
var markerspan = this.doc.top_node().one('div.ddarea div.markertexts span.markertext' + dropzoneno);
|
||||
if (markerspan !== null) {
|
||||
markerspan.setStyle('opacity', '0.6');
|
||||
xyfortext[0] -= markerspan.get('offsetWidth') / 2;
|
||||
xyfortext[1] -= markerspan.get('offsetHeight') / 2;
|
||||
markerspan.setXY(this.convert_to_window_xy(xyfortext));
|
||||
var markerspananchor = markerspan.one('a');
|
||||
if (markerspananchor !== null) {
|
||||
markerspananchor.once('click', function (e, dropzoneno) {
|
||||
var fill = this.shapes[dropzoneno].get('fill');
|
||||
fill.opacity = 1;
|
||||
this.shapes[dropzoneno].set('fill', fill);
|
||||
},
|
||||
this,
|
||||
dropzoneno
|
||||
);
|
||||
markerspananchor.set('tabIndex', 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
draw_shape_circle : function (dropzoneno, coords, colour) {
|
||||
var coordsparts = coords.match(/(\d+),(\d+);(\d+)/);
|
||||
if (coordsparts && coordsparts.length === 4) {
|
||||
var xy = [Number(coordsparts[1]) - coordsparts[3], Number(coordsparts[2]) - coordsparts[3]];
|
||||
if (this.coords_in_img(xy)) {
|
||||
var widthheight = [Number(coordsparts[3]) * 2, Number(coordsparts[3]) * 2];
|
||||
var shape = this.graphics.addShape({
|
||||
type: 'circle',
|
||||
width: widthheight[0],
|
||||
height: widthheight[1],
|
||||
fill: {
|
||||
color: colour,
|
||||
opacity: "0.5"
|
||||
},
|
||||
stroke: {
|
||||
weight: 1,
|
||||
color: "black"
|
||||
}
|
||||
});
|
||||
shape.setXY(this.convert_to_window_xy(xy));
|
||||
this.shapes[dropzoneno] = shape;
|
||||
return [Number(coordsparts[1]), Number(coordsparts[2])];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
draw_shape_rectangle : function (dropzoneno, coords, colour) {
|
||||
var coordsparts = coords.match(/(\d+),(\d+);(\d+),(\d+)/);
|
||||
if (coordsparts && coordsparts.length === 5) {
|
||||
var xy = [Number(coordsparts[1]), Number(coordsparts[2])];
|
||||
var widthheight = [Number(coordsparts[3]), Number(coordsparts[4])];
|
||||
if (this.coords_in_img([xy[0] + widthheight[0], xy[1] + widthheight[1]])) {
|
||||
var shape = this.graphics.addShape({
|
||||
type: 'rect',
|
||||
width: widthheight[0],
|
||||
height: widthheight[1],
|
||||
fill: {
|
||||
color: colour,
|
||||
opacity: "0.5"
|
||||
},
|
||||
stroke: {
|
||||
weight: 1,
|
||||
color: "black"
|
||||
}
|
||||
});
|
||||
shape.setXY(this.convert_to_window_xy(xy));
|
||||
this.shapes[dropzoneno] = shape;
|
||||
return [Number(xy[0]) + widthheight[0] / 2, Number(xy[1]) + widthheight[1] / 2];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
},
|
||||
draw_shape_polygon : function (dropzoneno, coords, colour) {
|
||||
var coordsparts = coords.split(';');
|
||||
var xy = [];
|
||||
for (var i in coordsparts) {
|
||||
var parts = coordsparts[i].match(/^(\d+),(\d+)$/);
|
||||
if (parts !== null && this.coords_in_img([parts[1], parts[2]])) {
|
||||
xy[xy.length] = [parts[1], parts[2]];
|
||||
}
|
||||
}
|
||||
if (xy.length > 2) {
|
||||
var polygon = this.graphics.addShape({
|
||||
type: "path",
|
||||
stroke: {
|
||||
weight: 1,
|
||||
color: "black"
|
||||
},
|
||||
fill: {
|
||||
color: colour,
|
||||
opacity : "0.5"
|
||||
}
|
||||
});
|
||||
var maxxy = [0,0];
|
||||
var minxy = [this.doc.bg_img().get('width'), this.doc.bg_img().get('height')];
|
||||
for (i = 0; i < xy.length; i++) {
|
||||
//calculate min and max points to find center to show marker on
|
||||
minxy[0] = Math.min(xy[i][0], minxy[0]);
|
||||
minxy[1] = Math.min(xy[i][1], minxy[1]);
|
||||
maxxy[0] = Math.max(xy[i][0], maxxy[0]);
|
||||
maxxy[1] = Math.max(xy[i][1], maxxy[1]);
|
||||
if (i === 0) {
|
||||
polygon.moveTo(xy[i][0], xy[i][1]);
|
||||
} else {
|
||||
polygon.lineTo(xy[i][0], xy[i][1]);
|
||||
}
|
||||
}
|
||||
if (Number(xy[0][0]) !== Number(xy[xy.length - 1][0]) || Number(xy[0][1]) !== Number(xy[xy.length - 1][1])) {
|
||||
polygon.lineTo(xy[0][0], xy[0][1]); // Close polygon if not already closed.
|
||||
}
|
||||
polygon.end();
|
||||
polygon.setXY(this.doc.bg_img().getXY());
|
||||
this.shapes[dropzoneno] = polygon;
|
||||
return [(minxy[0] + maxxy[0]) / 2, (minxy[1] + maxxy[1]) / 2];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
coords_in_img : function (coords) {
|
||||
return (coords[0] <= this.doc.bg_img().get('width') &&
|
||||
coords[1] <= this.doc.bg_img().get('height'));
|
||||
}
|
||||
}, {
|
||||
NAME : DDMARKERDDNAME,
|
||||
ATTRS : {
|
||||
drops : {value : null},
|
||||
readonly : {value : false},
|
||||
topnode : {value : null}
|
||||
}
|
||||
});
|
||||
M.qtype_ddmarker = M.qtype_ddmarker || {};
|
||||
M.qtype_ddmarker.dd_base_class = DDMARKER_DD;
|
||||
|
||||
var DDMARKERQUESTIONNAME = 'ddmarker_question';
|
||||
var DDMARKER_QUESTION = function() {
|
||||
DDMARKER_QUESTION.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
/**
|
||||
* This is the code for question rendering.
|
||||
*/
|
||||
Y.extend(DDMARKER_QUESTION, M.qtype_ddmarker.dd_base_class, {
|
||||
initializer : function() {
|
||||
this.doc = this.doc_structure(this);
|
||||
this.poll_for_image_load(null, false, 0, this.after_image_load);
|
||||
this.doc.bg_img().after('load', this.poll_for_image_load, this,
|
||||
false, 0, this.after_image_load);
|
||||
},
|
||||
after_image_load : function () {
|
||||
this.redraw_drags_and_drops();
|
||||
Y.later(2000, this, this.redraw_drags_and_drops, [], true);
|
||||
},
|
||||
clone_new_drag_item : function (draghome, itemno) {
|
||||
var drag = draghome.cloneNode(true);
|
||||
drag.removeClass('draghome');
|
||||
drag.addClass('dragitem');
|
||||
drag.addClass('item' + itemno);
|
||||
drag.one('span.markertext').setStyle('opacity', 0.6);
|
||||
draghome.insert(drag, 'after');
|
||||
if (!this.get('readonly')) {
|
||||
this.draggable(drag);
|
||||
}
|
||||
return drag;
|
||||
},
|
||||
draggable : function (drag) {
|
||||
var dd = new Y.DD.Drag({
|
||||
node: drag,
|
||||
dragMode: 'intersect'
|
||||
}).plug(Y.Plugin.DDConstrained, {constrain2node: this.doc.top_node()});
|
||||
dd.after('drag:start', function(e){
|
||||
var dragnode = e.target.get('node');
|
||||
dragnode.addClass('beingdragged');
|
||||
var choiceno = this.get_choiceno_for_node(dragnode);
|
||||
var itemno = this.get_itemno_for_node(dragnode);
|
||||
if (itemno !== null) {
|
||||
dragnode.removeClass('item' + dragnode);
|
||||
}
|
||||
this.save_all_xy_for_choice(choiceno, null);
|
||||
this.redraw_drags_and_drops();
|
||||
}, this);
|
||||
dd.after('drag:end', function(e) {
|
||||
var dragnode = e.target.get('node');
|
||||
dragnode.removeClass('beingdragged');
|
||||
var choiceno = this.get_choiceno_for_node(dragnode);
|
||||
this.save_all_xy_for_choice(choiceno, dragnode);
|
||||
this.redraw_drags_and_drops();
|
||||
}, this);
|
||||
//--- keyboard accessibility
|
||||
drag.set('tabIndex', 0);
|
||||
drag.on('dragchange', this.drop_zone_key_press, this);
|
||||
},
|
||||
save_all_xy_for_choice: function (choiceno, dropped) {
|
||||
var coords = [];
|
||||
var bgimgxy;
|
||||
for (var i = 0; i <= this.doc.drag_items_for_choice(choiceno).size(); i++) {
|
||||
var dragitem = this.doc.drag_item_for_choice(choiceno, i);
|
||||
if (dragitem) {
|
||||
dragitem.removeClass('item' + i);
|
||||
if (!dragitem.hasClass('beingdragged')) {
|
||||
bgimgxy = this.convert_to_bg_img_xy(dragitem.getXY());
|
||||
if (this.xy_in_bgimg(bgimgxy)) {
|
||||
dragitem.removeClass('item' + i);
|
||||
dragitem.addClass('item' + coords.length);
|
||||
coords[coords.length] = bgimgxy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dropped !== null){
|
||||
bgimgxy = this.convert_to_bg_img_xy(dropped.getXY());
|
||||
dropped.addClass('item' + coords.length);
|
||||
if (this.xy_in_bgimg(bgimgxy)) {
|
||||
coords[coords.length] = bgimgxy;
|
||||
}
|
||||
}
|
||||
this.set_form_value(choiceno, coords.join(';'));
|
||||
},
|
||||
reset_drag_xy : function (choiceno) {
|
||||
this.set_form_value(choiceno, '');
|
||||
},
|
||||
set_form_value : function (choiceno, value) {
|
||||
this.doc.input_for_choice(choiceno).set('value', value);
|
||||
},
|
||||
//make sure xy value is not out of bounds of bg image
|
||||
xy_in_bgimg : function (bgimgxy) {
|
||||
if ((bgimgxy[0] < 0) ||
|
||||
(bgimgxy[1] < 0) ||
|
||||
(bgimgxy[0] > this.doc.bg_img().get('width')) ||
|
||||
(bgimgxy[1] > this.doc.bg_img().get('height'))){
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
constrain_to_bgimg : function (windowxy) {
|
||||
var bgimgxy = this.convert_to_bg_img_xy(windowxy);
|
||||
bgimgxy[0] = Math.max(0, bgimgxy[0]);
|
||||
bgimgxy[1] = Math.max(0, bgimgxy[1]);
|
||||
bgimgxy[0] = Math.min(this.doc.bg_img().get('width'), bgimgxy[0]);
|
||||
bgimgxy[1] = Math.min(this.doc.bg_img().get('height'), bgimgxy[1]);
|
||||
return this.convert_to_window_xy(bgimgxy);
|
||||
},
|
||||
convert_to_bg_img_xy : function (windowxy) {
|
||||
return [Number(windowxy[0]) - this.doc.bg_img().getX() - 1,
|
||||
Number(windowxy[1]) - this.doc.bg_img().getY() - 1];
|
||||
},
|
||||
redraw_drags_and_drops : function() {
|
||||
this.doc.drag_items().each(function(item) {
|
||||
//if (!item.hasClass('beingdragged')){
|
||||
item.addClass('unneeded');
|
||||
//}
|
||||
}, this);
|
||||
this.doc.inputs_for_choices().each(function (input) {
|
||||
var choiceno = this.get_choiceno_for_node(input);
|
||||
var coords = this.get_coords(input);
|
||||
var dragitemhome = this.doc.drag_item_home(choiceno);
|
||||
for (var i = 0; i < coords.length; i++) {
|
||||
var dragitem = this.doc.drag_item_for_choice(choiceno, i);
|
||||
if (!dragitem || dragitem.hasClass('beingdragged')) {
|
||||
dragitem = this.clone_new_drag_item(dragitemhome, i);
|
||||
} else {
|
||||
dragitem.removeClass('unneeded');
|
||||
}
|
||||
dragitem.setXY(coords[i]);
|
||||
}
|
||||
}, this);
|
||||
this.doc.drag_items().each(function(item) {
|
||||
if (item.hasClass('unneeded') && !item.hasClass('beingdragged')) {
|
||||
item.remove(true);
|
||||
}
|
||||
}, this);
|
||||
if (this.graphics !== null) {
|
||||
this.graphics.clear();
|
||||
} else {
|
||||
this.graphics = new Y.Graphic(
|
||||
{render:this.doc.top_node().one("div.ddarea div.dropzones")}
|
||||
);
|
||||
}
|
||||
if (this.get('dropzones').length !== 0) {
|
||||
this.restart_colours();
|
||||
for (var dropzoneno in this.get('dropzones')) {
|
||||
var colourfordropzone = this.get_next_colour();
|
||||
var d = this.get('dropzones')[dropzoneno];
|
||||
this.draw_drop_zone(dropzoneno, d.markertext,
|
||||
d.shape, d.coords, colourfordropzone, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Determine what drag items need to be shown and
|
||||
* return coords of all drag items except any that are currently being dragged
|
||||
* based on contents of hidden inputs and whether drags are 'infinite' or how many drags should be shown.
|
||||
*/
|
||||
get_coords : function (input) {
|
||||
var choiceno = this.get_choiceno_for_node(input);
|
||||
var fv = input.get('value');
|
||||
var infinite = input.hasClass('infinite');
|
||||
var noofdrags = this.get_noofdrags_for_node(input);
|
||||
var dragging = (null !== this.doc.drag_item_being_dragged(choiceno));
|
||||
var coords = [];
|
||||
if (fv !== '') {
|
||||
var coordsstrings = fv.split(';');
|
||||
for (var i = 0; i < coordsstrings.length; i++) {
|
||||
coords[coords.length] = this.convert_to_window_xy(coordsstrings[i].split(','));
|
||||
}
|
||||
}
|
||||
var displayeddrags = coords.length + (dragging ? 1 : 0);
|
||||
if (infinite || (displayeddrags < noofdrags)) {
|
||||
coords[coords.length] = this.drag_home_xy(choiceno);
|
||||
}
|
||||
return coords;
|
||||
},
|
||||
drag_home_xy : function (choiceno) {
|
||||
var dragitemhome = this.doc.drag_item_home(choiceno);
|
||||
return [dragitemhome.getX(), dragitemhome.getY() - 12];
|
||||
},
|
||||
get_choiceno_for_node : function(node) {
|
||||
return Number(this.doc.get_classname_numeric_suffix(node, 'choice'));
|
||||
},
|
||||
get_itemno_for_node : function(node) {
|
||||
return Number(this.doc.get_classname_numeric_suffix(node, 'item'));
|
||||
},
|
||||
get_noofdrags_for_node : function(node) {
|
||||
return Number(this.doc.get_classname_numeric_suffix(node, 'noofdrags'));
|
||||
},
|
||||
|
||||
// Keyboard accessibility stuff below here.
|
||||
drop_zone_key_press : function (e) {
|
||||
var dragitem = e.target;
|
||||
var xy = dragitem.getXY();
|
||||
switch (e.direction) {
|
||||
case 'left' :
|
||||
xy[0] -= 1;
|
||||
break;
|
||||
case 'right' :
|
||||
xy[0] += 1;
|
||||
break;
|
||||
case 'down' :
|
||||
xy[1] += 1;
|
||||
break;
|
||||
case 'up' :
|
||||
xy[1] -= 1;
|
||||
break;
|
||||
case 'remove' :
|
||||
xy = null;
|
||||
break;
|
||||
}
|
||||
var choiceno = this.get_choiceno_for_node(dragitem);
|
||||
if (xy !== null) {
|
||||
xy = this.constrain_to_bgimg(xy);
|
||||
} else {
|
||||
xy = this.drag_home_xy(choiceno);
|
||||
}
|
||||
e.preventDefault();
|
||||
dragitem.setXY(xy);
|
||||
this.save_all_xy_for_choice(choiceno, null);
|
||||
}
|
||||
}, {NAME : DDMARKERQUESTIONNAME, ATTRS : {dropzones:{value:[]}}});
|
||||
|
||||
Y.Event.define('dragchange', {
|
||||
// Webkit and IE repeat keydown when you hold down arrow keys.
|
||||
// Opera links keypress to page scroll; others keydown.
|
||||
// Firefox prevents page scroll via preventDefault() on either
|
||||
// keydown or keypress.
|
||||
_event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress',
|
||||
|
||||
_keys: {
|
||||
'32': 'remove', // Space
|
||||
'37': 'left', // Left arrow
|
||||
'38': 'up', // Up arrow
|
||||
'39': 'right', // Right arrow
|
||||
'40': 'down', // Down arrow
|
||||
'65': 'left', // a
|
||||
'87': 'up', // w
|
||||
'68': 'right', // d
|
||||
'83': 'down', // s
|
||||
'27': 'remove' // Escape
|
||||
},
|
||||
|
||||
_keyHandler: function (e, notifier) {
|
||||
if (this._keys[e.keyCode]) {
|
||||
e.direction = this._keys[e.keyCode];
|
||||
notifier.fire(e);
|
||||
}
|
||||
},
|
||||
|
||||
on: function (node, sub, notifier) {
|
||||
sub._detacher = node.on(this._event, this._keyHandler,
|
||||
this, notifier);
|
||||
}
|
||||
});
|
||||
M.qtype_ddmarker.init_question = function(config) {
|
||||
return new DDMARKER_QUESTION(config);
|
||||
};
|
||||
}, '@VERSION@', {
|
||||
requires:['node', 'event-resize', 'dd', 'dd-drop', 'dd-constrain', 'graphics']
|
||||
});
|
255
question/type/ddmarker/yui/form/form.js
vendored
255
question/type/ddmarker/yui/form/form.js
vendored
|
@ -1,255 +0,0 @@
|
|||
/**
|
||||
* This is the question editing form code.
|
||||
*/
|
||||
YUI.add('moodle-qtype_ddmarker-form', function(Y) {
|
||||
var DDMARKERFORMNAME = 'ddmarker_form';
|
||||
var DDMARKER_FORM = function() {
|
||||
DDMARKER_FORM.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
Y.extend(DDMARKER_FORM, M.qtype_ddmarker.dd_base_class, {
|
||||
fp : null,
|
||||
|
||||
initializer : function() {
|
||||
this.fp = this.file_pickers();
|
||||
var tn = Y.one(this.get('topnode'));
|
||||
tn.one('div.fcontainer').append(
|
||||
'<div class="ddarea">' +
|
||||
'<div class="markertexts"></div>' +
|
||||
'<div class="droparea"></div>' +
|
||||
'<div class="dropzones"></div>' +
|
||||
'<div class="grid"></div>' +
|
||||
'</div>');
|
||||
this.doc = this.doc_structure(this);
|
||||
this.stop_selector_events();
|
||||
this.set_options_for_drag_item_selectors();
|
||||
this.setup_form_events();
|
||||
Y.later(500, this, this.update_drop_zones, [], true);
|
||||
Y.after(this.load_bg_image, M.form_filepicker, 'callback', this);
|
||||
this.load_bg_image();
|
||||
},
|
||||
|
||||
load_bg_image : function() {
|
||||
var bgimageurl = this.fp.file('bgimage').href;
|
||||
if (bgimageurl !== null) {
|
||||
this.doc.load_bg_img(bgimageurl);
|
||||
|
||||
var drop = new Y.DD.Drop({
|
||||
node: this.doc.bg_img()
|
||||
});
|
||||
|
||||
// Listen for a drop:hit on the background image.
|
||||
drop.on('drop:hit', function(e) {
|
||||
e.drag.get('node').setData('gooddrop', true);
|
||||
});
|
||||
|
||||
this.afterimageloaddone = false;
|
||||
this.doc.bg_img().on('load', this.constrain_image_size, this);
|
||||
}
|
||||
},
|
||||
|
||||
constrain_image_size : function (e) {
|
||||
var maxsize = this.get('maxsizes').bgimage;
|
||||
var reduceby = Math.max(e.target.get('width') / maxsize.width,
|
||||
e.target.get('height') / maxsize.height);
|
||||
if (reduceby > 1) {
|
||||
e.target.set('width', Math.floor(e.target.get('width') / reduceby));
|
||||
}
|
||||
e.target.addClass('constrained');
|
||||
e.target.detach('load', this.constrain_image_size);
|
||||
},
|
||||
|
||||
update_drop_zones : function () {
|
||||
|
||||
// Set up drop zones.
|
||||
if (this.graphics !== null) {
|
||||
this.graphics.destroy();
|
||||
}
|
||||
this.restart_colours();
|
||||
this.graphics = new Y.Graphic({render:"div.ddarea div.dropzones"});
|
||||
var noofdropzones = this.form.get_form_value('nodropzone', []);
|
||||
for (var dropzoneno = 0; dropzoneno < noofdropzones; dropzoneno++) {
|
||||
var dragitemno = this.form.get_form_value('drops', [dropzoneno, 'choice']);
|
||||
var markertext = this.get_marker_text(dragitemno);
|
||||
var shape = this.form.get_form_value('drops', [dropzoneno, 'shape']);
|
||||
var coords = this.get_coords(dropzoneno);
|
||||
var colourfordropzone = this.get_next_colour();
|
||||
Y.one('input#id_drops_' + dropzoneno + '_coords')
|
||||
.setStyle('background-color', colourfordropzone);
|
||||
this.draw_drop_zone(dropzoneno, markertext,
|
||||
shape, coords, colourfordropzone, false);
|
||||
}
|
||||
Y.one('div.ddarea .grid')
|
||||
.setXY(this.doc.bg_img().getXY())
|
||||
.setStyle('width', this.doc.bg_img().get('width'))
|
||||
.setStyle('height', this.doc.bg_img().get('height'));
|
||||
},
|
||||
|
||||
get_coords : function (dropzoneno) {
|
||||
var coords = this.form.get_form_value('drops', [dropzoneno, 'coords']);
|
||||
return coords.replace(new RegExp("\\s*", 'g'), '');
|
||||
},
|
||||
get_marker_text : function (markerno) {
|
||||
if (Number(markerno) !== 0) {
|
||||
var label = this.form.get_form_value('drags', [markerno - 1, 'label']);
|
||||
return label.replace(new RegExp("^\\s*(.*)\\s*$"), "$1");
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
set_options_for_drag_item_selectors : function () {
|
||||
var dragitemsoptions = {0: ''};
|
||||
for (var i = 1; i <= this.form.get_form_value('noitems', []); i++) {
|
||||
var label = this.get_marker_text(i);
|
||||
if (label !== "") {
|
||||
dragitemsoptions[i] = Y.Escape.html(label);
|
||||
}
|
||||
}
|
||||
// Get all the currently selected drags for each drop.
|
||||
var selectedvalues = [];
|
||||
var selector;
|
||||
for (i = 0; i < this.form.get_form_value('nodropzone', []); i++) {
|
||||
selector = Y.one('#id_drops_' + i + '_choice');
|
||||
selectedvalues[i] = Number(selector.get('value'));
|
||||
}
|
||||
for (i = 0; i < this.form.get_form_value('nodropzone', []); i++) {
|
||||
selector = Y.one('#id_drops_' + i + '_choice');
|
||||
// Remove all options for drag choice.
|
||||
selector.all('option').remove(true);
|
||||
// And recreate the options.
|
||||
for (var value in dragitemsoptions) {
|
||||
value = Number(value);
|
||||
var option = '<option value="' + value + '">' + dragitemsoptions[value] + '</option>';
|
||||
selector.append(option);
|
||||
var optionnode = selector.one('option[value="' + value + '"]');
|
||||
// Is this the currently selected value?
|
||||
if (value === selectedvalues[i]) {
|
||||
optionnode.set('selected', true);
|
||||
} else {
|
||||
// It is not the currently selected value, is it selectable?
|
||||
if (value !== 0) { // The 'no item' option is always selectable.
|
||||
// Variables to hold form values about this drag item.
|
||||
var noofdrags = this.form.get_form_value('drags', [value - 1, 'noofdrags']);
|
||||
if (Number(noofdrags) !== 0) { // 'noofdrags == 0' means infinite.
|
||||
// Go through all selected values in drop downs.
|
||||
for (var k in selectedvalues) {
|
||||
// Count down 'noofdrags' and if reach zero then set disabled option for this drag item.
|
||||
if (Number(selectedvalues[k]) === value) {
|
||||
if (Number(noofdrags) === 1) {
|
||||
optionnode.set('disabled', true);
|
||||
break;
|
||||
} else {
|
||||
noofdrags--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
stop_selector_events : function () {
|
||||
Y.all('fieldset#id_dropzoneheader select').detachAll();
|
||||
},
|
||||
|
||||
setup_form_events : function () {
|
||||
//events triggered by changes to form data
|
||||
|
||||
// Changes to labels.
|
||||
Y.all('fieldset#id_draggableitemheader input').on('change', function () {
|
||||
this.set_options_for_drag_item_selectors();
|
||||
}, this);
|
||||
|
||||
// Changes to selected drag item.
|
||||
Y.all('fieldset#id_draggableitemheader select').on('change', function () {
|
||||
this.set_options_for_drag_item_selectors();
|
||||
}, this);
|
||||
|
||||
// Change in selected item.
|
||||
Y.all('fieldset#id_dropzoneheader select').on('change', function () {
|
||||
this.set_options_for_drag_item_selectors();
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Low level operations on form.
|
||||
*/
|
||||
form : {
|
||||
to_name_with_index : function(name, indexes) {
|
||||
var indexstring = name;
|
||||
for (var i = 0; i < indexes.length; i++) {
|
||||
indexstring = indexstring + '[' + indexes[i] + ']';
|
||||
}
|
||||
return indexstring;
|
||||
},
|
||||
get_el : function (name, indexes) {
|
||||
var form = document.getElementById('mform1');
|
||||
return form.elements[this.to_name_with_index(name, indexes)];
|
||||
},
|
||||
get_form_value : function(name, indexes) {
|
||||
var el = this.get_el(name, indexes);
|
||||
if (el.type === 'checkbox') {
|
||||
return el.checked;
|
||||
} else {
|
||||
return el.value;
|
||||
}
|
||||
},
|
||||
set_form_value : function(name, indexes, value) {
|
||||
var el = this.get_el(name, indexes);
|
||||
if (el.type === 'checkbox') {
|
||||
el.checked = value;
|
||||
} else {
|
||||
el.value = value;
|
||||
}
|
||||
},
|
||||
from_name_with_index : function(name) {
|
||||
var toreturn = {};
|
||||
toreturn.indexes = [];
|
||||
var bracket = name.indexOf('[');
|
||||
toreturn.name = name.substring(0, bracket);
|
||||
while (bracket !== -1) {
|
||||
var end = name.indexOf(']', bracket + 1);
|
||||
toreturn.indexes.push(name.substring(bracket + 1, end));
|
||||
bracket = name.indexOf('[', end + 1);
|
||||
}
|
||||
return toreturn;
|
||||
}
|
||||
},
|
||||
|
||||
file_pickers : function () {
|
||||
var draftitemidstoname;
|
||||
var nametoparentnode;
|
||||
if (draftitemidstoname === undefined) {
|
||||
draftitemidstoname = {};
|
||||
nametoparentnode = {};
|
||||
var filepickers = Y.all('form.mform input.filepickerhidden');
|
||||
filepickers.each(function(filepicker) {
|
||||
draftitemidstoname[filepicker.get('value')] = filepicker.get('name');
|
||||
nametoparentnode[filepicker.get('name')] = filepicker.get('parentNode');
|
||||
}, this);
|
||||
}
|
||||
var toreturn = {
|
||||
file : function (name) {
|
||||
var parentnode = nametoparentnode[name];
|
||||
var fileanchor = parentnode.one('div.filepicker-filelist a');
|
||||
if (fileanchor) {
|
||||
return {href : fileanchor.get('href'), name : fileanchor.get('innerHTML')};
|
||||
} else {
|
||||
return {href : null, name : null};
|
||||
}
|
||||
},
|
||||
name : function (draftitemid) {
|
||||
return draftitemidstoname[draftitemid];
|
||||
}
|
||||
};
|
||||
return toreturn;
|
||||
}
|
||||
}, {NAME : DDMARKERFORMNAME, ATTRS : {maxsizes:{value:null}}});
|
||||
M.qtype_ddmarker = M.qtype_ddmarker || {};
|
||||
M.qtype_ddmarker.init_form = function(config) {
|
||||
return new DDMARKER_FORM(config);
|
||||
};
|
||||
}, '@VERSION@', {
|
||||
requires:['moodle-qtype_ddmarker-dd', 'form_filepicker', 'graphics', 'escape']
|
||||
});
|
10
question/type/ddmarker/yui/src/ddmarker/build.json
Normal file
10
question/type/ddmarker/yui/src/ddmarker/build.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"name": "moodle-qtype_ddmarker-dd",
|
||||
"builds": {
|
||||
"moodle-qtype_ddmarker-dd": {
|
||||
"jsfiles": [
|
||||
"ddmarker.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
560
question/type/ddmarker/yui/src/ddmarker/js/ddmarker.js
vendored
Normal file
560
question/type/ddmarker/yui/src/ddmarker/js/ddmarker.js
vendored
Normal file
|
@ -0,0 +1,560 @@
|
|||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
var DDMARKERDDNAME = 'moodle-qtype_ddmarker-dd';
|
||||
var DDMARKER_DD = function() {
|
||||
DDMARKER_DD.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
/**
|
||||
* This is the base class for the question rendering and question editing form code.
|
||||
*/
|
||||
Y.extend(DDMARKER_DD, Y.Base, {
|
||||
doc : null,
|
||||
polltimer : null,
|
||||
afterimageloaddone : false,
|
||||
graphics : null,
|
||||
poll_for_image_load : function (e, waitforimageconstrain, pause, doafterwords) {
|
||||
if (this.afterimageloaddone) {
|
||||
return;
|
||||
}
|
||||
var bgdone = this.doc.bg_img().get('complete');
|
||||
if (waitforimageconstrain) {
|
||||
bgdone = bgdone && this.doc.bg_img().hasClass('constrained');
|
||||
}
|
||||
if (bgdone) {
|
||||
if (this.polltimer !== null) {
|
||||
this.polltimer.cancel();
|
||||
this.polltimer = null;
|
||||
}
|
||||
this.doc.bg_img().detach('load', this.poll_for_image_load);
|
||||
if (pause !== 0) {
|
||||
Y.later(pause, this, doafterwords);
|
||||
} else {
|
||||
doafterwords.call(this);
|
||||
}
|
||||
this.afterimageloaddone = true;
|
||||
} else if (this.polltimer === null) {
|
||||
var pollarguments = [null, waitforimageconstrain, pause, doafterwords];
|
||||
this.polltimer =
|
||||
Y.later(1000, this, this.poll_for_image_load, pollarguments, true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Object to encapsulate operations on dd area.
|
||||
*/
|
||||
doc_structure : function () {
|
||||
var topnode = Y.one(this.get('topnode'));
|
||||
var dragitemsarea = topnode.one('div.dragitems');
|
||||
var dropbgarea = topnode.one('div.droparea');
|
||||
return {
|
||||
top_node : function() {
|
||||
return topnode;
|
||||
},
|
||||
bg_img : function() {
|
||||
return topnode.one('.dropbackground');
|
||||
},
|
||||
load_bg_img : function (url) {
|
||||
dropbgarea.setContent('<img class="dropbackground" src="' + url + '"/>');
|
||||
this.bg_img().on('load', this.on_image_load, this, 'bg_image');
|
||||
},
|
||||
drag_items : function() {
|
||||
return dragitemsarea.all('.dragitem');
|
||||
},
|
||||
drag_items_for_choice : function(choiceno) {
|
||||
return dragitemsarea.all('span.dragitem.choice' + choiceno);
|
||||
},
|
||||
drag_item_for_choice : function(choiceno, itemno) {
|
||||
return dragitemsarea.one('span.dragitem.choice' + choiceno +
|
||||
'.item' + itemno);
|
||||
},
|
||||
drag_item_being_dragged : function(choiceno) {
|
||||
return dragitemsarea.one('span.dragitem.beingdragged.choice' + choiceno);
|
||||
},
|
||||
drag_item_home : function (choiceno) {
|
||||
return dragitemsarea.one('span.draghome.choice' + choiceno);
|
||||
},
|
||||
drag_item_homes : function() {
|
||||
return dragitemsarea.all('span.draghome');
|
||||
},
|
||||
get_classname_numeric_suffix : function(node, prefix) {
|
||||
var classes = node.getAttribute('class');
|
||||
if (classes !== '') {
|
||||
var classesarr = classes.split(' ');
|
||||
for (var index = 0; index < classesarr.length; index++) {
|
||||
var patt1 = new RegExp('^' + prefix + '([0-9])+$');
|
||||
if (patt1.test(classesarr[index])) {
|
||||
var patt2 = new RegExp('([0-9])+$');
|
||||
var match = patt2.exec(classesarr[index]);
|
||||
return Number(match[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
inputs_for_choices : function () {
|
||||
return topnode.all('input.choices');
|
||||
},
|
||||
input_for_choice : function (choiceno) {
|
||||
return topnode.one('input.choice' + choiceno);
|
||||
},
|
||||
marker_texts : function () {
|
||||
return topnode.one('div.markertexts');
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
colours : ['#FFFFFF', '#B0C4DE', '#DCDCDC', '#D8BFD8',
|
||||
'#87CEFA','#DAA520', '#FFD700', '#F0E68C'],
|
||||
nextcolourindex : 0,
|
||||
restart_colours : function () {
|
||||
this.nextcolourindex = 0;
|
||||
},
|
||||
get_next_colour : function () {
|
||||
var colour = this.colours[this.nextcolourindex];
|
||||
this.nextcolourindex++;
|
||||
if (this.nextcolourindex === this.colours.length) {
|
||||
this.nextcolourindex = 0;
|
||||
}
|
||||
return colour;
|
||||
},
|
||||
convert_to_window_xy : function (bgimgxy) {
|
||||
return [Number(bgimgxy[0]) + this.doc.bg_img().getX() + 1,
|
||||
Number(bgimgxy[1]) + this.doc.bg_img().getY() + 1];
|
||||
},
|
||||
shapes : [],
|
||||
draw_drop_zone : function (dropzoneno, markertext, shape, coords, colour, link) {
|
||||
var existingmarkertext;
|
||||
if (link) {
|
||||
existingmarkertext = this.doc.marker_texts().one('span.markertext' + dropzoneno + ' a');
|
||||
} else {
|
||||
existingmarkertext = this.doc.marker_texts().one('span.markertext' + dropzoneno);
|
||||
}
|
||||
|
||||
if (existingmarkertext) {
|
||||
if (markertext !== '') {
|
||||
existingmarkertext.setContent(markertext);
|
||||
} else {
|
||||
existingmarkertext.remove(true);
|
||||
}
|
||||
} else if (markertext !== '') {
|
||||
var classnames = 'markertext markertext' + dropzoneno;
|
||||
if (link) {
|
||||
this.doc.marker_texts().append('<span class="' + classnames + '"><a href="#">' +
|
||||
markertext + '</a></span>');
|
||||
} else {
|
||||
this.doc.marker_texts().append('<span class="' + classnames + '">' +
|
||||
markertext + '</span>');
|
||||
}
|
||||
}
|
||||
var drawfunc = 'draw_shape_' + shape;
|
||||
if (this[drawfunc] instanceof Function){
|
||||
var xyfortext = this[drawfunc](dropzoneno, coords, colour);
|
||||
if (xyfortext !== null) {
|
||||
var markerspan = this.doc.top_node().one('div.ddarea div.markertexts span.markertext' + dropzoneno);
|
||||
if (markerspan !== null) {
|
||||
markerspan.setStyle('opacity', '0.6');
|
||||
xyfortext[0] -= markerspan.get('offsetWidth') / 2;
|
||||
xyfortext[1] -= markerspan.get('offsetHeight') / 2;
|
||||
markerspan.setXY(this.convert_to_window_xy(xyfortext));
|
||||
var markerspananchor = markerspan.one('a');
|
||||
if (markerspananchor !== null) {
|
||||
markerspananchor.once('click', function (e, dropzoneno) {
|
||||
var fill = this.shapes[dropzoneno].get('fill');
|
||||
fill.opacity = 1;
|
||||
this.shapes[dropzoneno].set('fill', fill);
|
||||
},
|
||||
this,
|
||||
dropzoneno
|
||||
);
|
||||
markerspananchor.set('tabIndex', 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
draw_shape_circle : function (dropzoneno, coords, colour) {
|
||||
var coordsparts = coords.match(/(\d+),(\d+);(\d+)/);
|
||||
if (coordsparts && coordsparts.length === 4) {
|
||||
var xy = [Number(coordsparts[1]) - coordsparts[3], Number(coordsparts[2]) - coordsparts[3]];
|
||||
if (this.coords_in_img(xy)) {
|
||||
var widthheight = [Number(coordsparts[3]) * 2, Number(coordsparts[3]) * 2];
|
||||
var shape = this.graphics.addShape({
|
||||
type: 'circle',
|
||||
width: widthheight[0],
|
||||
height: widthheight[1],
|
||||
fill: {
|
||||
color: colour,
|
||||
opacity: "0.5"
|
||||
},
|
||||
stroke: {
|
||||
weight: 1,
|
||||
color: "black"
|
||||
}
|
||||
});
|
||||
shape.setXY(this.convert_to_window_xy(xy));
|
||||
this.shapes[dropzoneno] = shape;
|
||||
return [Number(coordsparts[1]), Number(coordsparts[2])];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
draw_shape_rectangle : function (dropzoneno, coords, colour) {
|
||||
var coordsparts = coords.match(/(\d+),(\d+);(\d+),(\d+)/);
|
||||
if (coordsparts && coordsparts.length === 5) {
|
||||
var xy = [Number(coordsparts[1]), Number(coordsparts[2])];
|
||||
var widthheight = [Number(coordsparts[3]), Number(coordsparts[4])];
|
||||
if (this.coords_in_img([xy[0] + widthheight[0], xy[1] + widthheight[1]])) {
|
||||
var shape = this.graphics.addShape({
|
||||
type: 'rect',
|
||||
width: widthheight[0],
|
||||
height: widthheight[1],
|
||||
fill: {
|
||||
color: colour,
|
||||
opacity: "0.5"
|
||||
},
|
||||
stroke: {
|
||||
weight: 1,
|
||||
color: "black"
|
||||
}
|
||||
});
|
||||
shape.setXY(this.convert_to_window_xy(xy));
|
||||
this.shapes[dropzoneno] = shape;
|
||||
return [Number(xy[0]) + widthheight[0] / 2, Number(xy[1]) + widthheight[1] / 2];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
},
|
||||
draw_shape_polygon : function (dropzoneno, coords, colour) {
|
||||
var coordsparts = coords.split(';');
|
||||
var xy = [];
|
||||
for (var i in coordsparts) {
|
||||
var parts = coordsparts[i].match(/^(\d+),(\d+)$/);
|
||||
if (parts !== null && this.coords_in_img([parts[1], parts[2]])) {
|
||||
xy[xy.length] = [parts[1], parts[2]];
|
||||
}
|
||||
}
|
||||
if (xy.length > 2) {
|
||||
var polygon = this.graphics.addShape({
|
||||
type: "path",
|
||||
stroke: {
|
||||
weight: 1,
|
||||
color: "black"
|
||||
},
|
||||
fill: {
|
||||
color: colour,
|
||||
opacity : "0.5"
|
||||
}
|
||||
});
|
||||
var maxxy = [0,0];
|
||||
var minxy = [this.doc.bg_img().get('width'), this.doc.bg_img().get('height')];
|
||||
for (i = 0; i < xy.length; i++) {
|
||||
//calculate min and max points to find center to show marker on
|
||||
minxy[0] = Math.min(xy[i][0], minxy[0]);
|
||||
minxy[1] = Math.min(xy[i][1], minxy[1]);
|
||||
maxxy[0] = Math.max(xy[i][0], maxxy[0]);
|
||||
maxxy[1] = Math.max(xy[i][1], maxxy[1]);
|
||||
if (i === 0) {
|
||||
polygon.moveTo(xy[i][0], xy[i][1]);
|
||||
} else {
|
||||
polygon.lineTo(xy[i][0], xy[i][1]);
|
||||
}
|
||||
}
|
||||
if (Number(xy[0][0]) !== Number(xy[xy.length - 1][0]) || Number(xy[0][1]) !== Number(xy[xy.length - 1][1])) {
|
||||
polygon.lineTo(xy[0][0], xy[0][1]); // Close polygon if not already closed.
|
||||
}
|
||||
polygon.end();
|
||||
polygon.setXY(this.doc.bg_img().getXY());
|
||||
this.shapes[dropzoneno] = polygon;
|
||||
return [(minxy[0] + maxxy[0]) / 2, (minxy[1] + maxxy[1]) / 2];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
coords_in_img : function (coords) {
|
||||
return (coords[0] <= this.doc.bg_img().get('width') &&
|
||||
coords[1] <= this.doc.bg_img().get('height'));
|
||||
}
|
||||
}, {
|
||||
NAME : DDMARKERDDNAME,
|
||||
ATTRS : {
|
||||
drops : {value : null},
|
||||
readonly : {value : false},
|
||||
topnode : {value : null}
|
||||
}
|
||||
});
|
||||
M.qtype_ddmarker = M.qtype_ddmarker || {};
|
||||
M.qtype_ddmarker.dd_base_class = DDMARKER_DD;
|
||||
|
||||
var DDMARKERQUESTIONNAME = 'ddmarker_question';
|
||||
var DDMARKER_QUESTION = function() {
|
||||
DDMARKER_QUESTION.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
/**
|
||||
* This is the code for question rendering.
|
||||
*/
|
||||
Y.extend(DDMARKER_QUESTION, M.qtype_ddmarker.dd_base_class, {
|
||||
initializer : function() {
|
||||
this.doc = this.doc_structure(this);
|
||||
this.poll_for_image_load(null, false, 0, this.after_image_load);
|
||||
this.doc.bg_img().after('load', this.poll_for_image_load, this,
|
||||
false, 0, this.after_image_load);
|
||||
},
|
||||
after_image_load : function () {
|
||||
this.redraw_drags_and_drops();
|
||||
Y.later(2000, this, this.redraw_drags_and_drops, [], true);
|
||||
},
|
||||
clone_new_drag_item : function (draghome, itemno) {
|
||||
var drag = draghome.cloneNode(true);
|
||||
drag.removeClass('draghome');
|
||||
drag.addClass('dragitem');
|
||||
drag.addClass('item' + itemno);
|
||||
drag.one('span.markertext').setStyle('opacity', 0.6);
|
||||
draghome.insert(drag, 'after');
|
||||
if (!this.get('readonly')) {
|
||||
this.draggable(drag);
|
||||
}
|
||||
return drag;
|
||||
},
|
||||
draggable : function (drag) {
|
||||
var dd = new Y.DD.Drag({
|
||||
node: drag,
|
||||
dragMode: 'intersect'
|
||||
}).plug(Y.Plugin.DDConstrained, {constrain2node: this.doc.top_node()});
|
||||
dd.after('drag:start', function(e){
|
||||
var dragnode = e.target.get('node');
|
||||
dragnode.addClass('beingdragged');
|
||||
var choiceno = this.get_choiceno_for_node(dragnode);
|
||||
var itemno = this.get_itemno_for_node(dragnode);
|
||||
if (itemno !== null) {
|
||||
dragnode.removeClass('item' + dragnode);
|
||||
}
|
||||
this.save_all_xy_for_choice(choiceno, null);
|
||||
this.redraw_drags_and_drops();
|
||||
}, this);
|
||||
dd.after('drag:end', function(e) {
|
||||
var dragnode = e.target.get('node');
|
||||
dragnode.removeClass('beingdragged');
|
||||
var choiceno = this.get_choiceno_for_node(dragnode);
|
||||
this.save_all_xy_for_choice(choiceno, dragnode);
|
||||
this.redraw_drags_and_drops();
|
||||
}, this);
|
||||
//--- keyboard accessibility
|
||||
drag.set('tabIndex', 0);
|
||||
drag.on('dragchange', this.drop_zone_key_press, this);
|
||||
},
|
||||
save_all_xy_for_choice: function (choiceno, dropped) {
|
||||
var coords = [];
|
||||
var bgimgxy;
|
||||
for (var i = 0; i <= this.doc.drag_items_for_choice(choiceno).size(); i++) {
|
||||
var dragitem = this.doc.drag_item_for_choice(choiceno, i);
|
||||
if (dragitem) {
|
||||
dragitem.removeClass('item' + i);
|
||||
if (!dragitem.hasClass('beingdragged')) {
|
||||
bgimgxy = this.convert_to_bg_img_xy(dragitem.getXY());
|
||||
if (this.xy_in_bgimg(bgimgxy)) {
|
||||
dragitem.removeClass('item' + i);
|
||||
dragitem.addClass('item' + coords.length);
|
||||
coords[coords.length] = bgimgxy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dropped !== null){
|
||||
bgimgxy = this.convert_to_bg_img_xy(dropped.getXY());
|
||||
dropped.addClass('item' + coords.length);
|
||||
if (this.xy_in_bgimg(bgimgxy)) {
|
||||
coords[coords.length] = bgimgxy;
|
||||
}
|
||||
}
|
||||
this.set_form_value(choiceno, coords.join(';'));
|
||||
},
|
||||
reset_drag_xy : function (choiceno) {
|
||||
this.set_form_value(choiceno, '');
|
||||
},
|
||||
set_form_value : function (choiceno, value) {
|
||||
this.doc.input_for_choice(choiceno).set('value', value);
|
||||
},
|
||||
//make sure xy value is not out of bounds of bg image
|
||||
xy_in_bgimg : function (bgimgxy) {
|
||||
if ((bgimgxy[0] < 0) ||
|
||||
(bgimgxy[1] < 0) ||
|
||||
(bgimgxy[0] > this.doc.bg_img().get('width')) ||
|
||||
(bgimgxy[1] > this.doc.bg_img().get('height'))){
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
constrain_to_bgimg : function (windowxy) {
|
||||
var bgimgxy = this.convert_to_bg_img_xy(windowxy);
|
||||
bgimgxy[0] = Math.max(0, bgimgxy[0]);
|
||||
bgimgxy[1] = Math.max(0, bgimgxy[1]);
|
||||
bgimgxy[0] = Math.min(this.doc.bg_img().get('width'), bgimgxy[0]);
|
||||
bgimgxy[1] = Math.min(this.doc.bg_img().get('height'), bgimgxy[1]);
|
||||
return this.convert_to_window_xy(bgimgxy);
|
||||
},
|
||||
convert_to_bg_img_xy : function (windowxy) {
|
||||
return [Number(windowxy[0]) - this.doc.bg_img().getX() - 1,
|
||||
Number(windowxy[1]) - this.doc.bg_img().getY() - 1];
|
||||
},
|
||||
redraw_drags_and_drops : function() {
|
||||
this.doc.drag_items().each(function(item) {
|
||||
//if (!item.hasClass('beingdragged')){
|
||||
item.addClass('unneeded');
|
||||
//}
|
||||
}, this);
|
||||
this.doc.inputs_for_choices().each(function (input) {
|
||||
var choiceno = this.get_choiceno_for_node(input);
|
||||
var coords = this.get_coords(input);
|
||||
var dragitemhome = this.doc.drag_item_home(choiceno);
|
||||
for (var i = 0; i < coords.length; i++) {
|
||||
var dragitem = this.doc.drag_item_for_choice(choiceno, i);
|
||||
if (!dragitem || dragitem.hasClass('beingdragged')) {
|
||||
dragitem = this.clone_new_drag_item(dragitemhome, i);
|
||||
} else {
|
||||
dragitem.removeClass('unneeded');
|
||||
}
|
||||
dragitem.setXY(coords[i]);
|
||||
}
|
||||
}, this);
|
||||
this.doc.drag_items().each(function(item) {
|
||||
if (item.hasClass('unneeded') && !item.hasClass('beingdragged')) {
|
||||
item.remove(true);
|
||||
}
|
||||
}, this);
|
||||
if (this.graphics !== null) {
|
||||
this.graphics.clear();
|
||||
} else {
|
||||
this.graphics = new Y.Graphic(
|
||||
{render:this.doc.top_node().one("div.ddarea div.dropzones")}
|
||||
);
|
||||
}
|
||||
if (this.get('dropzones').length !== 0) {
|
||||
this.restart_colours();
|
||||
for (var dropzoneno in this.get('dropzones')) {
|
||||
var colourfordropzone = this.get_next_colour();
|
||||
var d = this.get('dropzones')[dropzoneno];
|
||||
this.draw_drop_zone(dropzoneno, d.markertext,
|
||||
d.shape, d.coords, colourfordropzone, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Determine what drag items need to be shown and
|
||||
* return coords of all drag items except any that are currently being dragged
|
||||
* based on contents of hidden inputs and whether drags are 'infinite' or how many drags should be shown.
|
||||
*/
|
||||
get_coords : function (input) {
|
||||
var choiceno = this.get_choiceno_for_node(input);
|
||||
var fv = input.get('value');
|
||||
var infinite = input.hasClass('infinite');
|
||||
var noofdrags = this.get_noofdrags_for_node(input);
|
||||
var dragging = (null !== this.doc.drag_item_being_dragged(choiceno));
|
||||
var coords = [];
|
||||
if (fv !== '') {
|
||||
var coordsstrings = fv.split(';');
|
||||
for (var i = 0; i < coordsstrings.length; i++) {
|
||||
coords[coords.length] = this.convert_to_window_xy(coordsstrings[i].split(','));
|
||||
}
|
||||
}
|
||||
var displayeddrags = coords.length + (dragging ? 1 : 0);
|
||||
if (infinite || (displayeddrags < noofdrags)) {
|
||||
coords[coords.length] = this.drag_home_xy(choiceno);
|
||||
}
|
||||
return coords;
|
||||
},
|
||||
drag_home_xy : function (choiceno) {
|
||||
var dragitemhome = this.doc.drag_item_home(choiceno);
|
||||
return [dragitemhome.getX(), dragitemhome.getY() - 12];
|
||||
},
|
||||
get_choiceno_for_node : function(node) {
|
||||
return Number(this.doc.get_classname_numeric_suffix(node, 'choice'));
|
||||
},
|
||||
get_itemno_for_node : function(node) {
|
||||
return Number(this.doc.get_classname_numeric_suffix(node, 'item'));
|
||||
},
|
||||
get_noofdrags_for_node : function(node) {
|
||||
return Number(this.doc.get_classname_numeric_suffix(node, 'noofdrags'));
|
||||
},
|
||||
|
||||
// Keyboard accessibility stuff below here.
|
||||
drop_zone_key_press : function (e) {
|
||||
var dragitem = e.target;
|
||||
var xy = dragitem.getXY();
|
||||
switch (e.direction) {
|
||||
case 'left' :
|
||||
xy[0] -= 1;
|
||||
break;
|
||||
case 'right' :
|
||||
xy[0] += 1;
|
||||
break;
|
||||
case 'down' :
|
||||
xy[1] += 1;
|
||||
break;
|
||||
case 'up' :
|
||||
xy[1] -= 1;
|
||||
break;
|
||||
case 'remove' :
|
||||
xy = null;
|
||||
break;
|
||||
}
|
||||
var choiceno = this.get_choiceno_for_node(dragitem);
|
||||
if (xy !== null) {
|
||||
xy = this.constrain_to_bgimg(xy);
|
||||
} else {
|
||||
xy = this.drag_home_xy(choiceno);
|
||||
}
|
||||
e.preventDefault();
|
||||
dragitem.setXY(xy);
|
||||
this.save_all_xy_for_choice(choiceno, null);
|
||||
}
|
||||
}, {NAME : DDMARKERQUESTIONNAME, ATTRS : {dropzones:{value:[]}}});
|
||||
|
||||
Y.Event.define('dragchange', {
|
||||
// Webkit and IE repeat keydown when you hold down arrow keys.
|
||||
// Opera links keypress to page scroll; others keydown.
|
||||
// Firefox prevents page scroll via preventDefault() on either
|
||||
// keydown or keypress.
|
||||
_event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress',
|
||||
|
||||
_keys: {
|
||||
'32': 'remove', // Space
|
||||
'37': 'left', // Left arrow
|
||||
'38': 'up', // Up arrow
|
||||
'39': 'right', // Right arrow
|
||||
'40': 'down', // Down arrow
|
||||
'65': 'left', // a
|
||||
'87': 'up', // w
|
||||
'68': 'right', // d
|
||||
'83': 'down', // s
|
||||
'27': 'remove' // Escape
|
||||
},
|
||||
|
||||
_keyHandler: function (e, notifier) {
|
||||
if (this._keys[e.keyCode]) {
|
||||
e.direction = this._keys[e.keyCode];
|
||||
notifier.fire(e);
|
||||
}
|
||||
},
|
||||
|
||||
on: function (node, sub, notifier) {
|
||||
sub._detacher = node.on(this._event, this._keyHandler,
|
||||
this, notifier);
|
||||
}
|
||||
});
|
||||
M.qtype_ddmarker.init_question = function(config) {
|
||||
return new DDMARKER_QUESTION(config);
|
||||
};
|
12
question/type/ddmarker/yui/src/ddmarker/meta/ddmarker.json
Normal file
12
question/type/ddmarker/yui/src/ddmarker/meta/ddmarker.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"moodle-qtype_ddmarker-dd": {
|
||||
"requires": [
|
||||
"node",
|
||||
"event-resize",
|
||||
"dd",
|
||||
"dd-drop",
|
||||
"dd-constrain",
|
||||
"graphics"
|
||||
]
|
||||
}
|
||||
}
|
10
question/type/ddmarker/yui/src/form/build.json
Normal file
10
question/type/ddmarker/yui/src/form/build.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"name": "moodle-qtype_ddmarker-form",
|
||||
"builds": {
|
||||
"moodle-qtype_ddmarker-form": {
|
||||
"jsfiles": [
|
||||
"form.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
267
question/type/ddmarker/yui/src/form/js/form.js
vendored
Normal file
267
question/type/ddmarker/yui/src/form/js/form.js
vendored
Normal file
|
@ -0,0 +1,267 @@
|
|||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* This is the question editing form code.
|
||||
*/
|
||||
var DDMARKERFORMNAME = 'moodle-qtype_ddmarker-form';
|
||||
var DDMARKER_FORM = function() {
|
||||
DDMARKER_FORM.superclass.constructor.apply(this, arguments);
|
||||
};
|
||||
Y.extend(DDMARKER_FORM, M.qtype_ddmarker.dd_base_class, {
|
||||
fp : null,
|
||||
|
||||
initializer : function() {
|
||||
this.fp = this.file_pickers();
|
||||
var tn = Y.one(this.get('topnode'));
|
||||
tn.one('div.fcontainer').append(
|
||||
'<div class="ddarea">' +
|
||||
'<div class="markertexts"></div>' +
|
||||
'<div class="droparea"></div>' +
|
||||
'<div class="dropzones"></div>' +
|
||||
'<div class="grid"></div>' +
|
||||
'</div>');
|
||||
this.doc = this.doc_structure(this);
|
||||
this.stop_selector_events();
|
||||
this.set_options_for_drag_item_selectors();
|
||||
this.setup_form_events();
|
||||
Y.later(500, this, this.update_drop_zones, [], true);
|
||||
Y.after(this.load_bg_image, M.form_filepicker, 'callback', this);
|
||||
this.load_bg_image();
|
||||
},
|
||||
|
||||
load_bg_image : function() {
|
||||
var bgimageurl = this.fp.file('bgimage').href;
|
||||
if (bgimageurl !== null) {
|
||||
this.doc.load_bg_img(bgimageurl);
|
||||
|
||||
var drop = new Y.DD.Drop({
|
||||
node: this.doc.bg_img()
|
||||
});
|
||||
|
||||
// Listen for a drop:hit on the background image.
|
||||
drop.on('drop:hit', function(e) {
|
||||
e.drag.get('node').setData('gooddrop', true);
|
||||
});
|
||||
|
||||
this.afterimageloaddone = false;
|
||||
this.doc.bg_img().on('load', this.constrain_image_size, this);
|
||||
}
|
||||
},
|
||||
|
||||
constrain_image_size : function (e) {
|
||||
var maxsize = this.get('maxsizes').bgimage;
|
||||
var reduceby = Math.max(e.target.get('width') / maxsize.width,
|
||||
e.target.get('height') / maxsize.height);
|
||||
if (reduceby > 1) {
|
||||
e.target.set('width', Math.floor(e.target.get('width') / reduceby));
|
||||
}
|
||||
e.target.addClass('constrained');
|
||||
e.target.detach('load', this.constrain_image_size);
|
||||
},
|
||||
|
||||
update_drop_zones : function () {
|
||||
|
||||
// Set up drop zones.
|
||||
if (this.graphics !== null) {
|
||||
this.graphics.destroy();
|
||||
}
|
||||
this.restart_colours();
|
||||
this.graphics = new Y.Graphic({render:"div.ddarea div.dropzones"});
|
||||
var noofdropzones = this.form.get_form_value('nodropzone', []);
|
||||
for (var dropzoneno = 0; dropzoneno < noofdropzones; dropzoneno++) {
|
||||
var dragitemno = this.form.get_form_value('drops', [dropzoneno, 'choice']);
|
||||
var markertext = this.get_marker_text(dragitemno);
|
||||
var shape = this.form.get_form_value('drops', [dropzoneno, 'shape']);
|
||||
var coords = this.get_coords(dropzoneno);
|
||||
var colourfordropzone = this.get_next_colour();
|
||||
Y.one('input#id_drops_' + dropzoneno + '_coords')
|
||||
.setStyle('background-color', colourfordropzone);
|
||||
this.draw_drop_zone(dropzoneno, markertext,
|
||||
shape, coords, colourfordropzone, false);
|
||||
}
|
||||
Y.one('div.ddarea .grid')
|
||||
.setXY(this.doc.bg_img().getXY())
|
||||
.setStyle('width', this.doc.bg_img().get('width'))
|
||||
.setStyle('height', this.doc.bg_img().get('height'));
|
||||
},
|
||||
|
||||
get_coords : function (dropzoneno) {
|
||||
var coords = this.form.get_form_value('drops', [dropzoneno, 'coords']);
|
||||
return coords.replace(new RegExp("\\s*", 'g'), '');
|
||||
},
|
||||
get_marker_text : function (markerno) {
|
||||
if (Number(markerno) !== 0) {
|
||||
var label = this.form.get_form_value('drags', [markerno - 1, 'label']);
|
||||
return label.replace(new RegExp("^\\s*(.*)\\s*$"), "$1");
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
set_options_for_drag_item_selectors : function () {
|
||||
var dragitemsoptions = {0: ''};
|
||||
for (var i = 1; i <= this.form.get_form_value('noitems', []); i++) {
|
||||
var label = this.get_marker_text(i);
|
||||
if (label !== "") {
|
||||
dragitemsoptions[i] = Y.Escape.html(label);
|
||||
}
|
||||
}
|
||||
// Get all the currently selected drags for each drop.
|
||||
var selectedvalues = [];
|
||||
var selector;
|
||||
for (i = 0; i < this.form.get_form_value('nodropzone', []); i++) {
|
||||
selector = Y.one('#id_drops_' + i + '_choice');
|
||||
selectedvalues[i] = Number(selector.get('value'));
|
||||
}
|
||||
for (i = 0; i < this.form.get_form_value('nodropzone', []); i++) {
|
||||
selector = Y.one('#id_drops_' + i + '_choice');
|
||||
// Remove all options for drag choice.
|
||||
selector.all('option').remove(true);
|
||||
// And recreate the options.
|
||||
for (var value in dragitemsoptions) {
|
||||
value = Number(value);
|
||||
var option = '<option value="' + value + '">' + dragitemsoptions[value] + '</option>';
|
||||
selector.append(option);
|
||||
var optionnode = selector.one('option[value="' + value + '"]');
|
||||
// Is this the currently selected value?
|
||||
if (value === selectedvalues[i]) {
|
||||
optionnode.set('selected', true);
|
||||
} else {
|
||||
// It is not the currently selected value, is it selectable?
|
||||
if (value !== 0) { // The 'no item' option is always selectable.
|
||||
// Variables to hold form values about this drag item.
|
||||
var noofdrags = this.form.get_form_value('drags', [value - 1, 'noofdrags']);
|
||||
if (Number(noofdrags) !== 0) { // 'noofdrags == 0' means infinite.
|
||||
// Go through all selected values in drop downs.
|
||||
for (var k in selectedvalues) {
|
||||
// Count down 'noofdrags' and if reach zero then set disabled option for this drag item.
|
||||
if (Number(selectedvalues[k]) === value) {
|
||||
if (Number(noofdrags) === 1) {
|
||||
optionnode.set('disabled', true);
|
||||
break;
|
||||
} else {
|
||||
noofdrags--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
stop_selector_events : function () {
|
||||
Y.all('fieldset#id_dropzoneheader select').detachAll();
|
||||
},
|
||||
|
||||
setup_form_events : function () {
|
||||
//events triggered by changes to form data
|
||||
|
||||
// Changes to labels.
|
||||
Y.all('fieldset#id_draggableitemheader input').on('change', function () {
|
||||
this.set_options_for_drag_item_selectors();
|
||||
}, this);
|
||||
|
||||
// Changes to selected drag item.
|
||||
Y.all('fieldset#id_draggableitemheader select').on('change', function () {
|
||||
this.set_options_for_drag_item_selectors();
|
||||
}, this);
|
||||
|
||||
// Change in selected item.
|
||||
Y.all('fieldset#id_dropzoneheader select').on('change', function () {
|
||||
this.set_options_for_drag_item_selectors();
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Low level operations on form.
|
||||
*/
|
||||
form : {
|
||||
to_name_with_index : function(name, indexes) {
|
||||
var indexstring = name;
|
||||
for (var i = 0; i < indexes.length; i++) {
|
||||
indexstring = indexstring + '[' + indexes[i] + ']';
|
||||
}
|
||||
return indexstring;
|
||||
},
|
||||
get_el : function (name, indexes) {
|
||||
var form = document.getElementById('mform1');
|
||||
return form.elements[this.to_name_with_index(name, indexes)];
|
||||
},
|
||||
get_form_value : function(name, indexes) {
|
||||
var el = this.get_el(name, indexes);
|
||||
if (el.type === 'checkbox') {
|
||||
return el.checked;
|
||||
} else {
|
||||
return el.value;
|
||||
}
|
||||
},
|
||||
set_form_value : function(name, indexes, value) {
|
||||
var el = this.get_el(name, indexes);
|
||||
if (el.type === 'checkbox') {
|
||||
el.checked = value;
|
||||
} else {
|
||||
el.value = value;
|
||||
}
|
||||
},
|
||||
from_name_with_index : function(name) {
|
||||
var toreturn = {};
|
||||
toreturn.indexes = [];
|
||||
var bracket = name.indexOf('[');
|
||||
toreturn.name = name.substring(0, bracket);
|
||||
while (bracket !== -1) {
|
||||
var end = name.indexOf(']', bracket + 1);
|
||||
toreturn.indexes.push(name.substring(bracket + 1, end));
|
||||
bracket = name.indexOf('[', end + 1);
|
||||
}
|
||||
return toreturn;
|
||||
}
|
||||
},
|
||||
|
||||
file_pickers : function () {
|
||||
var draftitemidstoname;
|
||||
var nametoparentnode;
|
||||
if (draftitemidstoname === undefined) {
|
||||
draftitemidstoname = {};
|
||||
nametoparentnode = {};
|
||||
var filepickers = Y.all('form.mform input.filepickerhidden');
|
||||
filepickers.each(function(filepicker) {
|
||||
draftitemidstoname[filepicker.get('value')] = filepicker.get('name');
|
||||
nametoparentnode[filepicker.get('name')] = filepicker.get('parentNode');
|
||||
}, this);
|
||||
}
|
||||
var toreturn = {
|
||||
file : function (name) {
|
||||
var parentnode = nametoparentnode[name];
|
||||
var fileanchor = parentnode.one('div.filepicker-filelist a');
|
||||
if (fileanchor) {
|
||||
return {href : fileanchor.get('href'), name : fileanchor.get('innerHTML')};
|
||||
} else {
|
||||
return {href : null, name : null};
|
||||
}
|
||||
},
|
||||
name : function (draftitemid) {
|
||||
return draftitemidstoname[draftitemid];
|
||||
}
|
||||
};
|
||||
return toreturn;
|
||||
}
|
||||
},{NAME : DDMARKERFORMNAME, ATTRS : {maxsizes:{value:null}}});
|
||||
|
||||
M.qtype_ddmarker = M.qtype_ddmarker || {};
|
||||
M.qtype_ddmarker.init_form = function(config) {
|
||||
return new DDMARKER_FORM(config);
|
||||
};
|
10
question/type/ddmarker/yui/src/form/meta/form.json
Normal file
10
question/type/ddmarker/yui/src/form/meta/form.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"moodle-qtype_ddmarker-form": {
|
||||
"requires": [
|
||||
"moodle-qtype_ddmarker-dd",
|
||||
"form_filepicker",
|
||||
"graphics",
|
||||
"escape"
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue