Index: lams_central/web/author2.jsp
===================================================================
diff -u -r06e2eedee66e1ee3087000324a326111dccdbcba -r8068bd165e981855f43bdeb2358e2a6b3654020c
--- lams_central/web/author2.jsp (.../author2.jsp) (revision 06e2eedee66e1ee3087000324a326111dccdbcba)
+++ lams_central/web/author2.jsp (.../author2.jsp) (revision 8068bd165e981855f43bdeb2358e2a6b3654020c)
@@ -67,6 +67,18 @@
+
+
Index: lams_central/web/includes/javascript/authoring/authoringActivity.js
===================================================================
diff -u -r06e2eedee66e1ee3087000324a326111dccdbcba -r8068bd165e981855f43bdeb2358e2a6b3654020c
--- lams_central/web/includes/javascript/authoring/authoringActivity.js (.../authoringActivity.js) (revision 06e2eedee66e1ee3087000324a326111dccdbcba)
+++ lams_central/web/includes/javascript/authoring/authoringActivity.js (.../authoringActivity.js) (revision 8068bd165e981855f43bdeb2358e2a6b3654020c)
@@ -34,7 +34,6 @@
this.id = +id;
this.uiid = +uiid || ++layout.ld.maxUIID;
this.toolContentID = toolContentID;
- this.type = 'tool';
this.toolID = +toolID;
this.title = title;
this.supportsOutputs = supportsOutputs;
@@ -58,8 +57,7 @@
this.groupingID = +groupingID;
this.groupingUIID = +groupingUIID || ++layout.ld.maxUIID;
this.uiid = +uiid || ++layout.ld.maxUIID;
- this.type = 'grouping';
- this.title = title;
+ this.title = title || 'Grouping';
this.groupingType = groupingType || 'random';
this.groupDivide = groupDivide || 'groups';
this.groupCount = +groupCount || 2;
@@ -84,7 +82,6 @@
GateActivity : function(id, uiid, x, y, gateType) {
this.id = +id;
this.uiid = +uiid || ++layout.ld.maxUIID;
- this.type = 'gate';
this.gateType = gateType || 'permission';
this.transitions = {
'from' : [],
@@ -102,7 +99,6 @@
* Either branching or converge point.
*/
BranchingEdgeActivity : function(id, uiid, x, y, title, branchingType, branchingActivity) {
- this.type = 'branchingEdge';
this.transitions = {
'from' : [],
'to' : []
@@ -154,7 +150,48 @@
/**
+ * Constructor for an Optional Activity.
+ */
+ OptionalActivity : function(id, uiid, x, y, title, minActivities, maxActivities) {
+ DecorationLib.Container.call(this, title || 'Optional Activity');
+
+ this.id = +id;
+ this.uiid = +uiid || ++layout.ld.maxUIID;
+ this.minActivities = minActivities || 0;
+ this.maxActivities = maxActivities || 0;
+ this.transitions = {
+ 'from' : [],
+ 'to' : []
+ };
+
+ this.loadPropertiesDialogContent = PropertyLib.optionalActivityProperties;
+
+ this.draw = ActivityLib.draw.optionalActivity;
+ this.draw(x, y);
+ },
+
+
+ /**
+ * Constructor for a Floating Activity.
+ */
+ FloatingActivity : function(id, uiid, x, y) {
+ DecorationLib.Container.call(this, 'Support Activity');
+
+ this.id = +id;
+ this.uiid = +uiid || ++layout.ld.maxUIID;
+
+ this.draw = ActivityLib.draw.floatingActivity;
+ this.draw(x, y);
+
+ // there can only be one
+ layout.floatingActivity = this;
+ },
+
+
+ /**
* Mehtods for drawing various kinds of activities.
+ * They are not defined in constructors so there is always a reference to an existing method,
+ * not a separate definition for each object instance.
*/
draw : {
@@ -329,6 +366,79 @@
this.items.shape = shape;
ActivityLib.activityHandlersInit(this);
+ },
+
+
+ optionalActivity : function(x, y, ignoredParam1, ignoredParam2, childActivities) {
+ if (x == undefined || y == undefined) {
+ // if no new coordinates are given, just redraw the activity
+ x = this.items.shape.getBBox().x;
+ y = this.items.shape.getBBox().y;
+ }
+
+ // either check what children are on canvas or use the priovided parameter
+ if (childActivities) {
+ this.childActivities = childActivities;
+ }
+
+ if (this.childActivities && this.childActivities.length > 0) {
+ // draw one by one, vertically
+ var activityY = y + 30,
+ allElements = paper.set(),
+ optionalActivity = this;
+ $.each(this.childActivities, function(){
+ this.parentActivity = optionalActivity;
+ this.draw(x + 20, activityY);
+ activityY = this.items.shape.getBBox().y2 + 10;
+ allElements.push(this.items.shape);
+ });
+ // area containing all drawn child activities
+ var box = allElements.getBBox();
+
+ this.drawContainer(x, y, box.x2 + 20, box.y2 + 20, layout.colors.optionalActivity);
+ } else {
+ this.drawContainer(x, y, x + 50, y + 70, layout.colors.optionalActivity);
+ }
+
+ // allow transition drawing and other activity behaviour
+ this.items.shape.unmousedown().mousedown(HandlerLib.activityMousedownHandler);
+
+ this.items.data('parentObject', this);
+ },
+
+
+ floatingActivity : function(x, y, ignoredParam1, ignoredParam2, childActivities) {
+ if (x == undefined || y == undefined) {
+ // if no new coordinates are given, just redraw the activity
+ x = this.items.shape.getBBox().x;
+ y = this.items.shape.getBBox().y;
+ }
+
+ // either check what children are on canvas or use the priovided parameter
+ if (childActivities) {
+ this.childActivities = childActivities;
+ }
+
+ if (this.childActivities && this.childActivities.length > 0) {
+ // draw one by one, horizontally
+ var activityX = x + 20,
+ allElements = paper.set(),
+ floatingActivity = this;
+ $.each(this.childActivities, function(){
+ this.parentActivity = floatingActivity;
+ this.draw(activityX, y + 30);
+ activityX = this.items.shape.getBBox().x2 + 10;
+ allElements.push(this.items.shape);
+ });
+ // area containing all drawn child activities
+ var box = allElements.getBBox();
+
+ this.drawContainer(x, y, box.x2 + 20, box.y2 + 20, layout.colors.optionalActivity);
+ } else {
+ this.drawContainer(x, y, x + 50, y + 70, layout.colors.optionalActivity);
+ }
+
+ this.items.data('parentObject', this);
}
},
@@ -347,7 +457,8 @@
'cursor' : 'pointer'
});
- if (activity.type == 'branchingEdge' && activity.branchingActivity.end) {
+ if (activity instanceof ActivityLib.BranchingEdgeActivity
+ && activity.branchingActivity.end) {
// highligh branching edges on hover
activity.branchingActivity.start.items.hover(HandlerLib.branchingEdgeMouseoverHandler,
HandlerLib.branchingEdgeMouseoutHandler);
@@ -361,7 +472,7 @@
* Deletes the given activity.
*/
removeActivity : function(activity, forceRemove) {
- if (!forceRemove && activity.type == 'branchingEdge'){
+ if (!forceRemove && activity instanceof ActivityLib.BranchingEdgeActivity){
// user removes one of the branching edges, so remove the whole activity
if (confirm('Are you sure you want to remove the whole branching activity?')){
var otherEdge = activity.isStart ? activity.branchingActivity.end
@@ -372,26 +483,46 @@
}
}
- // remove the transitions
- // need to use slice() to copy the array as it gets modified in removeTransition()
- $.each(activity.transitions.from.slice(), function() {
- ActivityLib.removeTransition(this);
- });
- $.each(activity.transitions.to.slice(), function() {
- ActivityLib.removeTransition(this);
- });
+ if (activity instanceof ActivityLib.FloatingActivity) {
+ layout.floatingActivity = null;
+ // re-enable the button
+ $('#floatingActivityButton').attr('disabled', null)
+ .css('opacity', 1);
+ } else {
+ // remove the transitions
+ // need to use slice() to copy the array as it gets modified in removeTransition()
+ $.each(activity.transitions.from.slice(), function() {
+ ActivityLib.removeTransition(this);
+ });
+ $.each(activity.transitions.to.slice(), function() {
+ ActivityLib.removeTransition(this);
+ });
+
+ // remove the activity from reference tables
+ layout.activities.splice(layout.activities.indexOf(activity), 1);
+ if (layout.items.copiedActivity = activity) {
+ layout.items.copiedActivity = null;
+ }
+ if (activity instanceof ActivityLib.GroupingActivity) {
+ $.each(layout.activities, function(){
+ if (activity == this.grouping) {
+ this.grouping = null;
+ this.draw();
+ }
+ });
+ }
+ }
- // remove the activity from reference tables
- layout.activities.splice(layout.activities.indexOf(activity), 1);
- if (layout.items.copiedActivity = activity) {
- layout.items.copiedActivity = null;
+ // remove the activity from parent activity
+ if (activity.parentActivity) {
+ activity.parentActivity.childActivities.splice(layout.parentActivity.childActivities.indexOf(activity), 1);
}
- if (activity.type == 'grouping') {
- $.each(layout.activities, function(){
- if (activity == this.grouping) {
- this.grouping = null;
- this.draw();
- }
+
+ // remove child activities
+ if (activity instanceof ActivityLib.OptionalActivity
+ || activity instanceof ActivityLib.FloatingActivity) {
+ $.each(activity.childActivities, function(){
+ ActivityLib.removeActivity(this);
});
}
@@ -404,19 +535,30 @@
* Draws a transition between two activities.
*/
addTransition : function(fromActivity, toActivity, redraw, id, uiid, title) {
+ if (toActivity.parentActivity){
+ toActivity = toActivity.parentActivity;
+ }
+ if (fromActivity.parentActivity){
+ fromActivity = fromActivity.parentActivity;
+ }
+ if (toActivity instanceof ActivityLib.FloatingActivity
+ || fromActivity instanceof ActivityLib.FloatingActivity){
+ return;
+ }
+
// only converge points are allowed to have few inbound transitions
if (!redraw
&& toActivity.transitions.to.length > 0
- && !(toActivity.type == 'branchingEdge' && !toActivity.isStart)) {
+ && !(toActivity instanceof ActivityLib.BranchingEdgeActivity && !toActivity.isStart)) {
alert('Transition to this activity already exists');
return;
}
// user chose to create outbound transition from an activity that already has one
if (!redraw
&& fromActivity.transitions.from.length > 0
- && !(fromActivity.type == 'branchingEdge' && fromActivity.isStart)
- && !(toActivity.type == 'branchingEdge' && toActivity.isStart)) {
+ && !(fromActivity instanceof ActivityLib.BranchingEdgeActivity && fromActivity.isStart)
+ && !(toActivity instanceof ActivityLib.BranchingEdgeActivity && toActivity.isStart)) {
if (confirm('Transition from this activity already exists.\n' +
'Do you want to create branching here?')) {
ActivityLib.addBranching(fromActivity, toActivity);
@@ -436,7 +578,7 @@
}
});
- if (!branch && fromActivity.type == 'branchingEdge' && fromActivity.isStart) {
+ if (!branch && fromActivity instanceof ActivityLib.BranchingEdgeActivity && fromActivity.isStart) {
// create a new branch
branch = new ActivityLib.BranchActivity(null, null, null, fromActivity.branchingActivity);
}
@@ -491,7 +633,7 @@
convergeActivity1 = convergeActivity1.transitions.from[0].toActivity;
};
- if (toActivity2.type == 'branchingEdge' && toActivity2.isStart) {
+ if (toActivity2 instanceof ActivityLib.BranchingEdgeActivity && toActivity2.isStart) {
// there is already a branching activity, reuse existing items
branchingEdgeStart = toActivity2;
branchingEdgeEnd = toActivity2.branchingActivity.end;
@@ -542,6 +684,7 @@
'stroke-dasharray' : '-'
});
object.items.resizeButton.show();
+ object.items.resizeButton.toFront();
object.items.selectEffect = true;
// also select encapsulated activities
@@ -550,7 +693,9 @@
object.items.fitButton.show();
$.each(childActivities, function(){
- ActivityLib.addSelectEffect(this, false);
+ if (!this.parentActivity) {
+ ActivityLib.addSelectEffect(this, false);
+ }
});
}
} else if (object instanceof ActivityLib.Transition) {
@@ -605,6 +750,7 @@
if (object) {
if (object.items.selectEffect) {
+ // different effects for different types of objects
if (object instanceof DecorationLib.Region) {
object.items.shape.attr({
'stroke' : 'black',
@@ -661,17 +807,69 @@
/**
* Drop the dragged activity on the canvas.
*/
- dropActivity : function(activity) {
- // redraw transitions
- $.each(activity.transitions.from.slice(), function(){
- ActivityLib.addTransition(activity, this.toActivity, true);
+ dropActivity : function(activity, x, y) {
+ if (!(activity instanceof DecorationLib.Container)) {
+ // check if it was removed from an Optional or Floating Activity
+ if (activity.parentActivity) {
+ var childActivities = DecorationLib.getChildActivities(activity.parentActivity.items.shape);
+ if ($.inArray(activity, childActivities) == -1) {
+ activity.parentActivity.draw();
+ ActivityLib.redrawTransitions(activity.parentActivity);
+ activity.parentActivity = null;
+ }
+ }
+
+ // check if it was added to an Optional or Floating Activity
+ var container = layout.floatingActivity && layout.floatingActivity.items.shape.isPointInside(x, y)
+ ? layout.floatingActivity : null;
+ if (!container) {
+ $.each(layout.activities, function(){
+ if (this instanceof ActivityLib.OptionalActivity && this.items.shape.isPointInside(x, y)) {
+ container = this;
+ return false;
+ }
+ });
+ }
+ if (container) {
+ if ($.inArray(activity, container.childActivities) == -1) {
+ $.each(activity.transitions.from, function(){
+ ActivityLib.removeTransition(this);
+ });
+ $.each(activity.transitions.to, function(){
+ ActivityLib.removeTransition(this);
+ });
+
+ // for properties dialog to reload
+ ActivityLib.removeSelectEffect(container);
+
+ container.childActivities.push(activity);
+ container.draw(null, null, null, null, childActivities);
+ ActivityLib.redrawTransitions(container);
+ }
+ }
+ }
+
+ ActivityLib.redrawTransitions(activity);
+
+ $.each(layout.regions, function(){
+ // redraw all annotation regions so they are pushed to back
+ this.draw();
});
- $.each(activity.transitions.to.slice(), function(){
- ActivityLib.addTransition(this.fromActivity, activity, true);
- });
},
+ redrawTransitions : function(activity) {
+ if (activity.transitions) {
+ $.each(activity.transitions.from.slice(), function(){
+ ActivityLib.addTransition(activity, this.toActivity, true);
+ });
+ $.each(activity.transitions.to.slice(), function(){
+ ActivityLib.addTransition(this.fromActivity, activity, true);
+ });
+ }
+ },
+
+
/**
* Open separate window with activity authoring on double click.
*/
@@ -755,7 +953,7 @@
// include the first activity
var branchLength = 1,
activity = this.transitionFrom.toActivity;
- if (activity.type == 'branchingEdge'
+ if (activity instanceof ActivityLib.BranchingEdgeActivity
&& branchingActivity == activity.branchingActivity){
// branch with no activities
return true;
@@ -764,7 +962,7 @@
while (activity.transitions.from.length > 0) {
activity = activity.transitions.from[0].toActivity;
// check if reached the end of branch
- if (activity.type == 'branchingEdge') {
+ if (activity instanceof ActivityLib.BranchingEdgeActivity) {
break;
} else {
branchLength++;
Index: lams_central/web/includes/javascript/authoring/authoringDecoration.js
===================================================================
diff -u -r06e2eedee66e1ee3087000324a326111dccdbcba -r8068bd165e981855f43bdeb2358e2a6b3654020c
--- lams_central/web/includes/javascript/authoring/authoringDecoration.js (.../authoringDecoration.js) (revision 06e2eedee66e1ee3087000324a326111dccdbcba)
+++ lams_central/web/includes/javascript/authoring/authoringDecoration.js (.../authoringDecoration.js) (revision 8068bd165e981855f43bdeb2358e2a6b3654020c)
@@ -5,16 +5,28 @@
var DecorationLib = {
/**
- * Constructor for region annotation.
+ * Abstract class for Region, Optional and Floating Activities.
*/
- Region : function(x, y, width, height, color, title) {
+ Container : function(title) {
this.title = title;
+ this.childActivities = [];
+ this.drawContainer = DecorationLib.methods.container.draw;
+ this.fit = DecorationLib.methods.container.fit;
+ },
+
+ /**
+ * Constructor for region annotation.
+ */
+ Region : function(x, y, x2, y2, color, title) {
+ DecorationLib.Container.call(this, title);
+ // we don't use it for region
+ this.childActivities = null;
+
this.draw = DecorationLib.methods.region.draw;
- this.fit = DecorationLib.methods.region.fit;
this.loadPropertiesDialogContent = PropertyLib.regionProperties;
- this.draw(x, y, width, height, color);
+ this.draw(x, y, x2, y2, color);
},
@@ -33,76 +45,46 @@
methods : {
-
- /**
- * Region methods
- */
- region : {
- draw : function(x, y, x2, y2, color, title){
+ container : {
+
+ draw : function(x, y, x2, y2, color){
// check for new coordinates or just take them from the existing shape
- var box = this.items && this.items.shape ? this.items.shape.getBBox() : null,
+ var box = this.items ? this.items.shape.getBBox() : null,
x = x ? x : box.x,
y = y ? y : box.y,
// take into account minimal size of rectangle
- x2 = x2 ? (x2 < x + 20 ? x + 20 : x2) : x + box.width,
- y2 = y2 ? (y2 < y + 20 ? y + 20 : y2) : y + box.height,
+ x2 = x2 ? Math.max(x2, x + 20) : x + box.width,
+ y2 = y2 ? Math.max(y2, y + 20) : y + box.height,
color = color ? color : this.items.shape.attr('fill');
- if (box) {
- this.items.remove();
- }
+ if (box) {
+ this.items.remove();
+ }
+
+ // the label
+ this.items = paper.set();
+ if (this.title) {
+ var label = paper.text(x + 7, y + 10, this.title)
+ .attr('text-anchor', 'start')
+ .toBack();
+ this.items.push(label);
- // the label
- this.items = paper.set();
- if (this.title) {
- var label = paper.text(x + 7, y + 10, this.title)
- .attr('text-anchor', 'start')
- .toBack();
- this.items.push(label);
- }
-
- // the rectangle
- this.items.shape = paper.path('M {0} {1} L {2} {1} L {2} {3} L {0} {3} z', x, y, x2, y2)
- .attr({
- 'fill' : color,
- 'cursor' : 'pointer'
- })
- .mousedown(HandlerLib.regionMousedownHandler)
- .click(HandlerLib.itemClickHandler)
- .toBack();
- this.items.push(this.items.shape);
-
- this.items.fitButton = paper.circle(x2 - 10, y + 10, 5)
- .attr({
- 'stroke' : null,
- 'fill' : 'blue',
- 'cursor' : 'pointer',
- 'title' : 'Fit'
- })
- .click(function(event){
- event.stopImmediatePropagation();
- var region = this.data('parentObject');
- region.fit();
- ActivityLib.addSelectEffect(region, true);
- })
- .hide();
- this.items.push(this.items.fitButton);
-
- this.items.resizeButton = paper.path(Raphael.format('M {0} {1} v {2} h -{2} z',
- x2, y2 - 15, 15))
- .attr({
- 'stroke' : null,
- 'fill' : 'blue',
- 'cursor' : 'se-resize'
- })
- .mousedown(HandlerLib.resizeRegionStartHandler)
- .hide();
- this.items.push(this.items.resizeButton);
-
- this.items.data('parentObject', this);
- },
-
-
+ // make sure title fits
+ x2 = Math.max(x2, label.getBBox().x2 + 5);
+ }
+
+ // the rectangle
+ this.items.shape = paper.path('M {0} {1} L {2} {1} L {2} {3} L {0} {3} z', x, y, x2, y2)
+ .attr({
+ 'fill' : color,
+ 'cursor' : 'pointer'
+ })
+ .mousedown(HandlerLib.containerMousedownHandler)
+ .click(HandlerLib.itemClickHandler)
+ .toBack();
+ this.items.push(this.items.shape);
+ },
+
/**
* Adjust the annotation so it envelops its child activities and nothing more.
*/
@@ -111,7 +93,7 @@
if (childActivities.length == 0) {
return;
}
-
+
ActivityLib.removeSelectEffect(this);
var allElements = paper.set();
@@ -126,7 +108,47 @@
}
},
+ /**
+ * Region methods
+ */
+ region : {
+ draw : function(x, y, x2, y2, color){
+ this.drawContainer(x, y, x2, y2, color);
+
+ var box = this.items.shape.getBBox();
+
+ this.items.fitButton = paper.circle(box.x2 - 10, box.y + 10, 5)
+ .attr({
+ 'stroke' : null,
+ 'fill' : 'blue',
+ 'cursor' : 'pointer',
+ 'title' : 'Fit'
+ })
+ .click(function(event){
+ event.stopImmediatePropagation();
+ var region = this.data('parentObject');
+ region.fit();
+ ActivityLib.addSelectEffect(region, true);
+ })
+ .hide();
+ this.items.push(this.items.fitButton);
+
+ this.items.resizeButton = paper.path(Raphael.format('M {0} {1} v {2} h -{2} z',
+ box.x2, box.y2 - 15, 15))
+ .attr({
+ 'stroke' : null,
+ 'fill' : 'blue',
+ 'cursor' : 'se-resize'
+ })
+ .mousedown(HandlerLib.resizeRegionStartHandler)
+ .hide();
+ this.items.push(this.items.resizeButton);
+
+ this.items.data('parentObject', this);
+ }
+ },
+
/**
* Label methods
*/
@@ -171,19 +193,25 @@
/**
- * Get activities enveloped by given shape (usually annotation region)
+ * Get activities enveloped by given container
*/
getChildActivities : function(shape){
var result = [];
$.each(layout.activities, function(){
- var activityBox = this.items.shape.getBBox();
-
- if (shape.isPointInside(activityBox.x, activityBox.y)
- && shape.isPointInside(activityBox.x2, activityBox.y2)) {
- result.push(this);
+ if (shape != this.items.shape) {
+ var activityBox = this.items.shape.getBBox();
+
+ if (shape.isPointInside(activityBox.x, activityBox.y)
+ && shape.isPointInside(activityBox.x2, activityBox.y2)) {
+ result.push(this);
+ }
}
});
+ var parentObject = shape.data('parentObject');
+ if (parentObject && !(parentObject instanceof DecorationLib.Region)) {
+ parentObject.childActivities = result;
+ }
return result;
},
@@ -199,4 +227,9 @@
layout.labels.splice(layout.labels.indexOf(label), 1);
label.items.remove();
}
-};
\ No newline at end of file
+};
+
+// set prototype hierarchy
+DecorationLib.Region.prototype = new DecorationLib.Container;
+ActivityLib.OptionalActivity.prototype = new DecorationLib.Container;
+ActivityLib.FloatingActivity.prototype = new DecorationLib.Container;
\ No newline at end of file
Index: lams_central/web/includes/javascript/authoring/authoringGeneral.js
===================================================================
diff -u -r06e2eedee66e1ee3087000324a326111dccdbcba -r8068bd165e981855f43bdeb2358e2a6b3654020c
--- lams_central/web/includes/javascript/authoring/authoringGeneral.js (.../authoringGeneral.js) (revision 06e2eedee66e1ee3087000324a326111dccdbcba)
+++ lams_central/web/includes/javascript/authoring/authoringGeneral.js (.../authoringGeneral.js) (revision 8068bd165e981855f43bdeb2358e2a6b3654020c)
@@ -11,6 +11,7 @@
'drawMode' : false,
// 'isZoomed' : false,
'activities' : null,
+ 'floatingActivity' : null,
'regions' : null,
'labels' : null,
'items' : {
@@ -65,7 +66,8 @@
'transition' : 'rgb(119,126,157)',
'binActive' : 'red',
'selectEffect' : 'blue',
- 'annotation' : 'yellow'
+ 'annotation' : 'yellow',
+ 'optionalActivity' : 'rgb(194,213,254)'
},
};
@@ -141,9 +143,14 @@
var toolID = draggable.draggable.attr('toolId'),
x = draggable.offset.left + canvas.scrollLeft() - canvas.offset().left,
y = draggable.offset.top + canvas.scrollTop() - canvas.offset().top,
- label = $('div', draggable.draggable).text();
-
- layout.activities.push(new ActivityLib.ToolActivity(null, null, null, toolID, x, y, label));
+ label = $('div', draggable.draggable).text(),
+ activity = new ActivityLib.ToolActivity(null, null, null, toolID, x, y, label),
+ translatedEvent = ActivityLib.translateEventOnCanvas(event),
+ eventX = translatedEvent[0],
+ eventY = translatedEvent[1];
+
+ layout.activities.push(activity);
+ ActivityLib.dropActivity(activity, eventX, eventY);
}
});
}
@@ -489,7 +496,7 @@
// Sequence Activity, i.e. a branch
case 8:
$.each(layout.activities, function(){
- if (this.type == 'branchingEdge'
+ if (this instanceof ActivityLib.BranchingEdgeActivity
&& activityData.parentActivityID == this.branchingActivity.id) {
// create a branch inside the branching activity
this.branchingActivity.branches.push(
@@ -555,9 +562,9 @@
var groupedActivity = this.activity,
groupingID = this.groupingID;
$.each(layout.activities, function(){
- if (this.type == 'grouping' && groupingID == this.groupingID) {
+ if (this instanceof ActivityLib.GroupingActivity && groupingID == this.groupingID) {
// add reference and redraw the grouped activity
- if (groupedActivity.type == 'branchingEdge') {
+ if (groupedActivity instanceof ActivityLib.BranchingEdgeActivity) {
groupedActivity.branchingActivity.grouping = this;
} else {
groupedActivity.grouping = this;
@@ -577,15 +584,15 @@
branch = null;
$.each(layout.activities, function(){
// is it the branch we're looking for?
- if (this.type == 'branchingEdge' && this.isStart) {
+ if (this instanceof ActivityLib.BranchingEdgeActivity && this.isStart) {
$.each(this.branchingActivity.branches, function(){
if (branchUIID == this.uiid) {
branch = this;
return false;
}
});
// is it the grouping we're looking for
- } else if (this.type == 'grouping') {
+ } else if (this instanceof ActivityLib.GroupingActivity) {
$.each(this.groups, function(){
if (groupUIID == this.uiid) {
group = this;
@@ -611,7 +618,7 @@
// draw starting and ending transitions in branches
$.each(layout.activities, function(){
- if (this.type == 'branchingEdge' && this.isStart) {
+ if (this instanceof ActivityLib.BranchingEdgeActivity && this.isStart) {
var branchingActivity = this.branchingActivity;
$.each(branchingActivity.branches, function(){
var branch = this,
@@ -635,7 +642,7 @@
// find which activities the transition belongs to
$.each(layout.activities, function(){
var activity = this,
- isBranching = activity.type == 'branchingEdge';
+ isBranching = activity instanceof ActivityLib.BranchingEdgeActivity;
// check if transition IDs match either a plain activity or a complex one
if (isBranching ? !activity.isStart && transition.fromActivityID == activity.branchingActivity.id
@@ -717,14 +724,14 @@
'groupingSupportType' : 2,
'applyGrouping' : isGrouped,
'groupingUIID' : isGrouped ? activity.grouping.groupingUIID : null,
- 'createGroupingUIID' : activity.type == 'grouping' ? activity.groupingUIID : null,
+ 'createGroupingUIID' : activity instanceof ActivityLib.GroupingActivity ? activity.groupingUIID : null,
'parentActivityID' : null,
'parentUIID' : null,
'libraryActivityUIImage' : iconPath,
'xCoord' : activityBox.x,
'yCoord' : activityBox.y,
'activityTitle' : activity.title,
- 'activityCategoryID' : activity.type == 'tool' ?
+ 'activityCategoryID' : activity instanceof ActivityLib.ToolActivity ?
layout.toolMetadata[toolID].activityCategoryID : 1,
'activityTypeID' : activityTypeID,
@@ -746,7 +753,7 @@
});
});
- if (activity.type == 'grouping') {
+ if (activity instanceof ActivityLib.GroupingActivity) {
// create a list of groupings
var groups = [],
groupingType = null;
Index: lams_central/web/includes/javascript/authoring/authoringHandler.js
===================================================================
diff -u -r06e2eedee66e1ee3087000324a326111dccdbcba -r8068bd165e981855f43bdeb2358e2a6b3654020c
--- lams_central/web/includes/javascript/authoring/authoringHandler.js (.../authoringHandler.js) (revision 06e2eedee66e1ee3087000324a326111dccdbcba)
+++ lams_central/web/includes/javascript/authoring/authoringHandler.js (.../authoringHandler.js) (revision 8068bd165e981855f43bdeb2358e2a6b3654020c)
@@ -105,6 +105,15 @@
items.isDragged = true;
items.attr('cursor', 'move');
+ var parentObject = draggedElement.data('parentObject');
+ sticky = parentObject && (parentObject instanceof ActivityLib.OptionalActivity
+ || parentObject instanceof ActivityLib.FloatingActivity);
+ if (sticky) {
+ $.each(parentObject.childActivities, function(){
+ this.items.hide();
+ });
+ }
+
canvas.mousemove(function(event) {
HandlerLib.dragItemsMoveHandler(items, event, startX, startY);
});
@@ -153,7 +162,7 @@
* Start drawing a transition.
*/
drawTransitionStartHandler : function(activity, event, x, y) {
- if (activity.fromTransition && activity.type != 'branchingEdge') {
+ if (activity.fromTransition && !(activity instanceof ActivityLib.BranchingEdgeActivity)) {
alert('Transition from this activity already exists');
return;
}
@@ -274,7 +283,7 @@
// immediatelly show which activities will be enveloped
var childActivities = DecorationLib.getChildActivities(data.shape);
$.each(layout.activities, function(){
- if ($.inArray(this, childActivities) > -1){
+ if (!this.parentActivity && $.inArray(this, childActivities) > -1){
ActivityLib.addSelectEffect(this, false);
} else {
ActivityLib.removeSelectEffect(this);
@@ -386,13 +395,16 @@
ActivityLib.removeActivity(activity);
} else {
HandlerLib.dropObject(activity);
- ActivityLib.dropActivity(activity);
+
+ var translatedEvent = ActivityLib.translateEventOnCanvas(event),
+ endX = translatedEvent[0],
+ endY = translatedEvent[1];
+ ActivityLib.dropActivity(activity, endX, endY);
}
}
// start dragging the activity
HandlerLib.dragItemsStartHandler(activity.items, this, mouseupHandler, event, x, y);
}
-
},
@@ -449,26 +461,33 @@
/**
- * Starts dragging a region
+ * Starts dragging a container
*/
- regionMousedownHandler : function(event, x, y){
+ containerMousedownHandler : function(event, x, y){
if (layout.drawMode || (event.originalEvent ?
event.originalEvent.defaultPrevented : event.defaultPrevented)){
return;
}
- var region = this.data('parentObject');
+ var container = this.data('parentObject');
// allow transition dragging
var mouseupHandler = function(event){
if (HandlerLib.isElemenentBinned(event)) {
- // if the region was over rubbish bin, remove it
- DecorationLib.removeRegion(region);
+ // if the container was over rubbish bin, remove it
+ if (container instanceof DecorationLib.Region) {
+ DecorationLib.removeRegion(container);
+ } else {
+ ActivityLib.removeActivity(container);
+ }
} else {
- HandlerLib.dropObject(region);
+ HandlerLib.dropObject(container);
+ if (container instanceof ActivityLib.FloatingActivity) {
+ ActivityLib.dropActivity(container);
+ }
}
}
- HandlerLib.dragItemsStartHandler(region.items, this, mouseupHandler, event, x, y);
+ HandlerLib.dragItemsStartHandler(container.items, this, mouseupHandler, event, x, y);
},
Index: lams_central/web/includes/javascript/authoring/authoringMenu.js
===================================================================
diff -u -r06e2eedee66e1ee3087000324a326111dccdbcba -r8068bd165e981855f43bdeb2358e2a6b3654020c
--- lams_central/web/includes/javascript/authoring/authoringMenu.js (.../authoringMenu.js) (revision 06e2eedee66e1ee3087000324a326111dccdbcba)
+++ lams_central/web/includes/javascript/authoring/authoringMenu.js (.../authoringMenu.js) (revision 8068bd165e981855f43bdeb2358e2a6b3654020c)
@@ -90,7 +90,7 @@
x = translatedEvent[0] - 47,
y = translatedEvent[1] - 2;
- layout.activities.push(new ActivityLib.GroupingActivity(null, null, x, y, 'Grouping'));
+ layout.activities.push(new ActivityLib.GroupingActivity(null, null, x, y));
HandlerLib.resetCanvasMode(true);
});
@@ -148,6 +148,65 @@
/**
+ * Creates a new optional activity.
+ */
+ addOptionalActivity : function() {
+ HandlerLib.resetCanvasMode();
+
+ var dialog = layout.items.infoDialog.text('Click to add an optional activity container.');
+ dialog.dialog('open');
+
+ canvas.css('cursor', 'pointer').click(function(event){
+ dialog.text('');
+ dialog.dialog('close');
+
+
+ var translatedEvent = ActivityLib.translateEventOnCanvas(event),
+ x = translatedEvent[0],
+ y = translatedEvent[1];
+
+ HandlerLib.resetCanvasMode(true);
+
+ layout.activities.push(new ActivityLib.OptionalActivity(null, null, x, y));
+ });
+ },
+
+
+ /**
+ * Creates a new floating activity.
+ */
+ addFloatingActivity : function() {
+ if (layout.floatingActivity) {
+ // there can be only one
+ return;
+ }
+ HandlerLib.resetCanvasMode();
+
+ var dialog = layout.items.infoDialog.text('Click to add a support activity container.');
+ dialog.dialog('open');
+
+ canvas.css('cursor', 'pointer').click(function(event){
+ dialog.text('');
+ dialog.dialog('close');
+
+
+ var translatedEvent = ActivityLib.translateEventOnCanvas(event),
+ x = translatedEvent[0],
+ y = translatedEvent[1];
+
+ HandlerLib.resetCanvasMode(true);
+
+ // do not add it to layout.activities as it behaves differently
+ new ActivityLib.FloatingActivity(null, null, x, y);
+
+ // there can be only one, so disable the button
+ $('#floatingActivityButton').attr('disabled', 'disabled')
+ .css('opacity', 0.2);
+ });
+ },
+
+
+ /**
* Creates a new transition.
*/
addTransition : function() {
@@ -314,17 +373,19 @@
+ 'Do you want to continue?')) {
return;
}
-
- // just to refresh the state of canvas
- HandlerLib.resetCanvasMode(true);
if (layout.activities.length == 0) {
// no activities, nothing to do
return;
}
+ // just to refresh the state of canvas
+ HandlerLib.resetCanvasMode(true);
+
// activities are arranged in a grid
var row = 0,
+ // for special cases when row needs to shifted more
+ forceRowY = null,
column = 0,
// check how many columns current paper can hold
maxColumns = Math.floor((paper.width - layout.conf.arrangeHorizontalPadding)
@@ -334,15 +395,21 @@
// check how many rows current paper can hold
maxRows = Math.floor((paper.height - layout.conf.arrangeVerticalPadding)
/ layout.conf.arrangeVerticalSpace),
- // make a shallow copy of activities array
- activitiesCopy = layout.activities.slice(),
+ // a shallow copy of activities array without inner activities
+ activitiesCopy = [],
// just to speed up processing when there are only activities with no transitions left
onlyDetachedLeft = false;
+
+ $.each(layout.activities, function(){
+ if (!this.parentActivity){
+ activitiesCopy.push(this);
+ }
+ });
// branches will not be broken into few rows; if they are long, paper will be resized
// find the longes branch to find the new paper size
$.each(layout.activities, function(){
- if (this.type == 'branchingEdge' && this.isStart) {
+ if (this instanceof ActivityLib.BranchingEdgeActivity && this.isStart) {
// refresh branching metadata
ActivityLib.updateBranchesLength(this.branchingActivity);
// add start and end edges to the result
@@ -352,6 +419,12 @@
}
}
});
+
+ // check how many child activities are in Floating Activity, if any
+ if (layout.floatingActivity && layout.floatingActivity.childActivities.length > subsequenceMaxLength) {
+ subsequenceMaxLength = childActivities.length;
+ }
+
// resize paper horizontally, if needed
if (subsequenceMaxLength > maxColumns) {
maxColumns = subsequenceMaxLength;
@@ -400,7 +473,7 @@
// crawl through a sequence of activities
while (activity) {
- if (activity.type == 'branchingEdge') {
+ if (activity instanceof ActivityLib.BranchingEdgeActivity) {
if (activity.isStart) {
// draw branching edges straight away and remove them from normall processing
var branchingActivity = activity.branchingActivity,
@@ -409,7 +482,7 @@
complex = {
end : end
},
- // can the whole branching fit in curren canvas width?
+ // can the whole branching fit in current canvas width?
branchingFits = column + branchingActivity.longestBranchLength + 2 <= maxColumns;
if (!branchingFits) {
// start branching from the left side of canvas
@@ -465,21 +538,39 @@
var x = layout.conf.arrangeHorizontalPadding + column * layout.conf.arrangeHorizontalSpace,
y = layout.conf.arrangeVerticalPadding + row * layout.conf.arrangeVerticalSpace;
- if (activity.type == 'gate') {
+ if (activity instanceof ActivityLib.GateActivity) {
// adjust placement for gate activity, so it's in the middle of its cell
x += 57;
y += 10;
+ } else if (activity instanceof ActivityLib.OptionalActivity){
+ x -= 20;
}
activity.draw(x, y);
// remove the activity so we do not process it twice
activitiesCopy.splice(activitiesCopy.indexOf(activity), 1);
+
+ // learn where a tall Optional Activity has its end
+ // and later start drawing activities lower than in the next row
+ if (activity instanceof ActivityLib.OptionalActivity && activity.childActivities.length > 1) {
+ var activityEndY = activity.items.shape.getBBox().y2;
+ if (!forceRowY || activityEndY > forceRowY) {
+ forceRowY = activityEndY;
+ }
+ }
}
// find the next row and column
column = (column + 1) % maxColumns;
if (column == 0) {
row++;
+ // if an Optional Activity forced next activities to be drawn lower than usual
+ if (forceRowY) {
+ while (forceRowY > layout.conf.arrangeVerticalPadding + 10 + row * layout.conf.arrangeVerticalSpace) {
+ row++;
+ }
+ forceRowY = null;
+ }
}
// does the activity has further activities?
@@ -531,6 +622,15 @@
};
};
+ if (layout.floatingActivity) {
+ row++;
+ column = 0;
+ var x = layout.conf.arrangeHorizontalPadding,
+ y = layout.conf.arrangeVerticalPadding - 30 + row * layout.conf.arrangeVerticalSpace;
+
+ layout.floatingActivity.draw(x, y);
+ }
+
// redraw transitions one by one
$.each(layout.activities, function(){
$.each(this.transitions.from.slice(), function(){
@@ -544,13 +644,15 @@
* Removes existing activities and prepares canvas for a new sequence.
*/
newLearningDesign : function(force, soft){
+ // force means that user should not be asked for confirmation.
if (!force && (layout.activities.length > 0
|| layout.regions.length > 0
|| layout.labels.length > 0)
&& !confirm('Are you sure you want to remove all existing elements?')){
return;
}
+ // soft means that data is manually reset, instead of simply reloading the page.
if (soft) {
$('#ldDescriptionFieldTitle').text('');
CKEDITOR.instances['ldDescriptionFieldDescription'].setData(null);
@@ -561,6 +663,7 @@
layout.activities = [];
layout.regions = [];
layout.labels = [];
+ layout.floatingActivity = null;
if (paper) {
paper.clear();
@@ -599,7 +702,7 @@
return;
}
// only tool activities can be copied (todo?)
- if (activity.type != 'tool') {
+ if (!(activity instanceof ActivityLib.ToolActivity)) {
alert('Sorry, you can not paste this type of activity');
return;
}
Index: lams_central/web/includes/javascript/authoring/authoringProperty.js
===================================================================
diff -u -r06e2eedee66e1ee3087000324a326111dccdbcba -r8068bd165e981855f43bdeb2358e2a6b3654020c
--- lams_central/web/includes/javascript/authoring/authoringProperty.js (.../authoringProperty.js) (revision 06e2eedee66e1ee3087000324a326111dccdbcba)
+++ lams_central/web/includes/javascript/authoring/authoringProperty.js (.../authoringProperty.js) (revision 8068bd165e981855f43bdeb2358e2a6b3654020c)
@@ -405,6 +405,67 @@
/**
+ * Properties dialog content for Optional Activity.
+ */
+ optionalActivityProperties : function() {
+ var activity = this,
+ content = activity.propertiesContent;
+
+ activity.minActivities = Math.min(activity.minActivities, activity.childActivities.length);
+ activity.maxActivities = Math.min(activity.maxActivities, activity.childActivities.length);
+
+ if (!content) {
+ // first run, create the content
+ content = activity.propertiesContent = $('#propertiesContentOptionalActivity').clone().attr('id', null)
+ .show().data('parentObject', activity);
+ $('.propertiesContentFieldTitle', content).val(activity.title);
+
+ $('input', content).change(function(){
+ // extract changed properties and redraw the transition
+ var content = $(this).closest('.dialogContainer'),
+ activity = content.data('parentObject'),
+ newTitle = $('.propertiesContentFieldTitle', content).val();
+ if (newTitle != activity.title) {
+ activity.title = newTitle;
+ activity.draw();
+ }
+ });
+
+ $('.propertiesContentFieldOptionalActivityMin', content).spinner({'min' : 0})
+ .on('spinchange', function(){
+ var value = +$(this).val();
+ activity.minActivities = Math.min(value, activity.childActivities.length);
+ if (value != activity.minActivities) {
+ $(this, content).spinner('value', activity.minActivities);
+ }
+ if (activity.minActivities > activity.maxActivities) {
+ $('.propertiesContentFieldOptionalActivityMax', content).spinner('value', value);
+ }
+ $('.propertiesContentFieldOptionalActivityMax', content).spinner('option', 'min', value);
+ });
+
+
+ $('.propertiesContentFieldOptionalActivityMax', content).spinner({'min' : 0})
+ .on('spinchange', function(){
+ var value = +$(this).val();
+ activity.maxActivities = Math.min(value, activity.childActivities.length);
+ if (value != activity.maxActivities) {
+ $(this, content).spinner('value', activity.maxActivities);
+ }
+ });
+ }
+
+ $('.propertiesContentFieldOptionalActivityMin', content).spinner('value', activity.minActivities)
+ .spinner('option', 'max', activity.childActivities.length);
+ $('.propertiesContentFieldOptionalActivityMax', content).spinner('value', activity.maxActivities)
+ .spinner('option', {
+ 'min' : activity.minActivities,
+ 'max' : activity.childActivities.length
+ });
+ },
+
+
+ /**
* Properties dialog content for regions (annotations).
*/
regionProperties : function() {
@@ -474,7 +535,7 @@
var emptyOption = $(''),
groupingDropdown = $('.propertiesContentFieldGrouping', content).empty().append(emptyOption);
$.each(layout.activities, function(){
- if (this.type == 'grouping') {
+ if (this instanceof ActivityLib.GropuingActivity) {
var option = $('').text(this.title)
.appendTo(groupingDropdown)
// store reference to grouping object
@@ -499,7 +560,7 @@
var emptyOption = $(''),
inputDropdown = $('.propertiesContentFieldInput', content).empty().append(emptyOption);
$.each(layout.activities, function(){
- if (this.type == 'tool' && layout.toolMetadata[this.toolID].supportsOutputs) {
+ if (this instanceof ActivityLib.ToolActivity && layout.toolMetadata[this.toolID].supportsOutputs) {
var option = $('').text(this.title)
.appendTo(inputDropdown)
// store reference to grouping object