Index: lams_build/3rdParty.userlibraries =================================================================== RCS file: /usr/local/cvsroot/lams_build/3rdParty.userlibraries,v diff -u -r1.69 -r1.70 --- lams_build/3rdParty.userlibraries 8 Apr 2013 10:52:41 -0000 1.69 +++ lams_build/3rdParty.userlibraries 5 Nov 2013 17:37:17 -0000 1.70 @@ -45,5 +45,6 @@ + Index: lams_build/build.xml =================================================================== RCS file: /usr/local/cvsroot/lams_build/build.xml,v diff -u -r1.117 -r1.118 --- lams_build/build.xml 9 Sep 2013 06:21:43 -0000 1.117 +++ lams_build/build.xml 5 Nov 2013 17:37:17 -0000 1.118 @@ -199,6 +199,7 @@ + Index: lams_build/lib/json/gson-2.2.4.jar =================================================================== RCS file: /usr/local/cvsroot/lams_build/lib/json/gson-2.2.4.jar,v diff -u Binary files differ Index: lams_central/src/java/org/lamsfoundation/lams/authoring/web/AuthoringAction.java =================================================================== RCS file: /usr/local/cvsroot/lams_central/src/java/org/lamsfoundation/lams/authoring/web/AuthoringAction.java,v diff -u -r1.38 -r1.39 --- lams_central/src/java/org/lamsfoundation/lams/authoring/web/AuthoringAction.java 21 Oct 2013 13:47:28 -0000 1.38 +++ lams_central/src/java/org/lamsfoundation/lams/authoring/web/AuthoringAction.java 5 Nov 2013 17:37:19 -0000 1.39 @@ -25,6 +25,7 @@ import java.io.IOException; import java.io.PrintWriter; +import java.io.Writer; import java.util.LinkedList; import java.util.List; import java.util.Vector; @@ -42,6 +43,7 @@ import org.apache.tomcat.util.json.JSONException; import org.apache.tomcat.util.json.JSONObject; import org.lamsfoundation.lams.authoring.service.IAuthoringService; +import org.lamsfoundation.lams.learningdesign.dto.LearningDesignDTO; import org.lamsfoundation.lams.learningdesign.dto.LicenseDTO; import org.lamsfoundation.lams.learningdesign.service.ILearningDesignService; import org.lamsfoundation.lams.lesson.Lesson; @@ -67,6 +69,9 @@ import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + /** * @author Manpreet Minhas * @@ -101,7 +106,7 @@ request.setAttribute("tools", getLearningDesignService().getToolDTOs(request.getRemoteUser())); return mapping.findForward("openAutoring"); } - + /** * Output the supplied WDDX packet. If the request parameter USE_JSP_OUTPUT is set, then it sets the session * attribute "parameterName" to the wddx packet string. If USE_JSP_OUTPUT is not set, then the packet is written out @@ -157,6 +162,21 @@ return outputPacket(mapping, request, response, wddxPacket, "details"); } + public ActionForward getLearningDesignJSON(ActionMapping mapping, ActionForm form, HttpServletRequest request, + HttpServletResponse response) throws ServletException, IOException { + JSONObject responseJSON = new JSONObject(); + + long learningDesignID = WebUtil.readLongParam(request, AttributeNames.PARAM_LEARNINGDESIGN_ID); + LearningDesignDTO learningDesignDTO = getLearningDesignService().getLearningDesignDTO(learningDesignID, + getUserLanguage()); + + response.setContentType("application/json;charset=utf-8"); + Writer responseWriter = response.getWriter(); + Gson gson = new GsonBuilder().create(); + gson.toJson(learningDesignDTO, responseWriter); + return null; + } + public ActionForward finishLearningDesignEdit(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { @@ -471,7 +491,6 @@ } return AuthoringAction.toolService; } - private ILearningDesignService getLearningDesignService() { if (AuthoringAction.learningDesignService == null) { Index: lams_central/web/author2.jsp =================================================================== RCS file: /usr/local/cvsroot/lams_central/web/Attic/author2.jsp,v diff -u -r1.5 -r1.6 --- lams_central/web/author2.jsp 21 Oct 2013 13:47:27 -0000 1.5 +++ lams_central/web/author2.jsp 5 Nov 2013 17:37:18 -0000 1.6 @@ -21,6 +21,10 @@ + + + + -
-
+
Open
@@ -45,11 +48,11 @@
 
    -
  • Gate
  • -
  • Branch
  • +
  • Gate
  • +
  • Branch
-
+
Group
@@ -94,17 +97,10 @@ Recently used sequences - - -
- Open -
-
- Cancel -
- -
+ +
+
\ No newline at end of file Index: lams_central/web/css/authoring.css =================================================================== RCS file: /usr/local/cvsroot/lams_central/web/css/authoring.css,v diff -u -r1.3 -r1.4 --- lams_central/web/css/authoring.css 21 Oct 2013 13:47:28 -0000 1.3 +++ lams_central/web/css/authoring.css 5 Nov 2013 17:37:18 -0000 1.4 @@ -2,7 +2,7 @@ display: none; } -div#openLearningDesignDialog { +.ui-dialog { font-size: 12px; } @@ -14,7 +14,7 @@ div#openLearningDesignDialog > table { width: 100%; - height: 735px; + height: 684px; table-layout: fixed; border-top: thin dotted #2E6E9E; } @@ -52,27 +52,18 @@ td#canvasCell { text-align: center; padding: 10px 0px 0px 10px; + vertical-align: middle; } div#canvasDiv { overflow: auto; - height: 685px; + height: 665px; } img#ldScreenshotLoading { padding-top: 200px; } -#buttonsCell { - border-top: thin dotted #2E6E9E; - padding-top: 5px; -} - -#buttonsCell > div { - float: right; - margin-left: 10px; -} - div#toolbar { padding: 3px; height: 27px; Fisheye: Tag 1.7 refers to a dead (removed) revision in file `lams_central/web/includes/javascript/authoring.js'. Fisheye: No comparison available. Pass `N' to diff? Index: lams_central/web/includes/javascript/authoring/authoringActivity.js =================================================================== RCS file: /usr/local/cvsroot/lams_central/web/includes/javascript/authoring/authoringActivity.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ lams_central/web/includes/javascript/authoring/authoringActivity.js 5 Nov 2013 17:37:19 -0000 1.1 @@ -0,0 +1,353 @@ +/** + * This file contains methods for Activity manipulation on canvas. + */ + +var ActivityLib = { + + /** + * Constructor for a Tool Activity. + */ + ToolActivity: function(id, toolID, x, y, label) { + this.id = id; + this.type = 'tool'; + this.toolID = toolID; + + // create activity SVG elements + paper.setStart(); + var shape = paper.rect(x, y, layout.conf.activityWidth, layout.conf.activityHeight) + .attr({ + 'fill' : layout.colors.activity + }); + + paper.image(layout.toolIcons[toolID], shape.attr('x') + + shape.attr('width') / 2 - 15, shape.attr('y') + 2, 30, 30); + + paper.text(shape.attr('x') + shape.attr('width') / 2, shape + .attr('y') + 40, label); + + this.items = paper.setFinish(); + this.items.shape = shape; + + ActivityLib.initActivity(this); + }, + + /** + * Constructor for a Grouping Activity. + */ + GroupingActivity : function(id, x, y) { + this.id = id; + this.type = 'group'; + + // create activity SVG elements + paper.setStart(); + var shape = paper.rect( + x - layout.conf.activityWidth/2, + y - layout.conf.activityHeight/2, + layout.conf.activityWidth, layout.conf.activityHeight) + .attr({ + 'fill' : layout.colors.activity + }); + + paper.image('../images/grouping.gif', + shape.attr('x') + shape.attr('width') / 2 - 15, + shape.attr('y') + 2, + 30, 30); + + paper.text(shape.attr('x') + shape.attr('width') / 2, shape + .attr('y') + 40, 'Grouping'); + + this.items = paper.setFinish(); + this.items.shape = shape; + + ActivityLib.initActivity(this); + }, + + /** + * Constructor for a Gate Activity. + */ + GateActivity : function(id, x, y) { + this.id = id; + this.type = 'gate'; + + // create activity SVG elements + paper.setStart(); + var shape = paper.path('M ' + x + ' ' + y + layout.defs.gate) + .attr({ + 'fill' : layout.colors.gate + }); + + paper.text(x + 7, y + 14, 'STOP') + .attr({ + 'font-size' : 9, + 'font' : 'sans-serif', + 'stroke' : layout.colors.gateText + }); + + this.items = paper.setFinish(); + this.items.shape = shape; + + ActivityLib.initActivity(this); + }, + + + BranchingEdgeActivity : function(id, x, y, branchingActivity) { + this.type = 'branchingEdge'; + if (branchingActivity) { + this.isStart = false; + branchingActivity.end = this; + } else { + this.isStart = true; + branchingActivity = new ActivityLib.BranchingActivity(id, this); + } + this.branchingActivity = branchingActivity; + + // create activity SVG elements + paper.setStart(); + var shape = paper.path('M ' + x + ' ' + y + + (this.isStart ? layout.defs.branchingEdgeStart : layout.defs.branchingEdgeEnd)) + .attr({ + 'fill' : this.isStart ? layout.colors.branchingEdgeStart + : layout.colors.branchingEdgeEnd + }); + + paper.text(x, y + 14, this.isStart ? 'Branching point' + : 'Converge point') + .attr({ + 'font-size' : 9, + 'font' : 'sans-serif' + }); + + this.items = paper.setFinish(); + this.items.shape = shape; + + ActivityLib.initActivity(this); + }, + + + BranchingActivity : function(id, branchingEdgeStart) { + this.id = id; + this.start = branchingEdgeStart; + this.branches = []; + }, + + + BranchActivity : function(id, branchingActivity, transitionFrom) { + this.id = id; + this.transitionsFrom = transitionFrom; + this.branchingActivity = branchingActivity; + branchingActivity.branches.push(this); + }, + + + /** + * Make a new activity fully functional on canvas. + */ + initActivity : function(activity) { + activity.items.mousedown(function(event, x, y){ + if (event.ctrlKey) { + // when CTRL is held down, start drawing a transition + HandlerLib.drawTransitionStartHandler(activity, event, x, y); + } else { + // start dragging the activity + var mouseupHandler = function(){ + HandlerLib.dragActivityEndHandler(activity); + }; + + HandlerLib.dragItemsStartHandler(activity.items, this, mouseupHandler, event, x, y); + } + }) + .click(function(event){ + // inform that user wants to select, not drag the activity + activity.items.clicked = true; + HandlerLib.selectActivityHandler(event, activity); + }) + .dblclick(function(){ + // inform that user wants to open, not drag the activity + activity.items.clicked = true; + HandlerLib.openActivityAuthoringHandler(activity); + }) + .attr({ + 'cursor' : 'pointer' + }); + + if (activity.type == 'branchingEdge' && activity.branchingActivity.end) { + var startItems = activity.branchingActivity.start.items; + var endItems = activity.branchingActivity.end.items; + var mouseover = function(){ + if (!startItems.isDragged && !endItems.isDragged) { + startItems.shape.attr('fill', layout.colors.branchingEdgeMatch); + endItems.shape.attr('fill', layout.colors.branchingEdgeMatch); + } + }; + var mouseout = function(){ + if (!startItems.isDragged && !endItems.isDragged) { + startItems.shape.attr('fill', layout.colors.branchingEdgeStart); + endItems.shape.attr('fill', layout.colors.branchingEdgeEnd); + } + }; + startItems.shape.hover(mouseover, mouseout); + endItems.shape.hover(mouseover, mouseout); + } + }, + + + /** + * Deletes the given activity. + */ + removeActivity : function(activity, forceRemove) { + if (!forceRemove && activity.type == 'branchingEdge'){ + if (confirm('Are you sure you want to remove the whole branching activity?')){ + var otherEdge = activity.isStart ? activity.branchingActivity.end + : activity.branchingActivity.start; + ActivityLib.removeActivity(otherEdge, true); + } else { + return; + } + } + + // remove the transitions + if (activity.fromTransition) { + var toActivity = activity.fromTransition.toActivity; + // if grouping activity is gone, remove the grouping effect + if (activity.type == 'group' && toActivity.items.groupingEffect) { + toActivity.items.groupingEffect.remove(); + toActivity.items.groupingEffect = null; + } + toActivity.toTransition = null; + activity.fromTransition.remove(); + } + if (activity.toTransition){ + activity.toTransition.fromActivity.fromTransition = null + activity.toTransition.remove(); + } + + // remove the activity from activities table + activities.splice(activities.indexOf(activity), 1); + // visually remove the activity + activity.items.remove(); + }, + + + /** + * Draws a transition between two activities. + */ + drawTransition : function(fromActivity, toActivity) { + // remove the existing activities + if (fromActivity.fromTransition) { + fromActivity.fromTransition.remove(); + } + if (toActivity.toTransition) { + toActivity.toTransition.remove(); + } + + // calculate middle points of each activity + var fromActivityBox = fromActivity.items.shape.getBBox(); + var toActivityBox = toActivity.items.shape.getBBox(); + var startX = fromActivityBox.x + fromActivityBox.width / 2; + var startY = fromActivityBox.y + fromActivityBox.height / 2; + var endX = toActivityBox.x + toActivityBox.width / 2; + var endY = toActivityBox.y + toActivityBox.height / 2; + + // do the actual drawing + paper.setStart(); + paper.path('M ' + startX + ' ' + startY + ' L ' + endX + ' ' + endY) + .attr({ + 'stroke' : layout.colors.transition, + 'stroke-width' : 2 + }); + + // draw the arrow and turn it in the same direction as the line + var angle = 90 + Math.atan2(endY - startY, endX - startX) * 180 / Math.PI; + var arrowX = (startX + (endX - startX)/2); + var arrowY = (startY + (endY - startY)/2); + var arrowPath = Raphael.transformPath('M ' + arrowX + ' ' + arrowY + layout.defs.transArrow, + 'R ' + angle + ' ' + arrowX + ' ' + arrowY); + paper.path(arrowPath) + .attr({ + 'stroke' : layout.colors.transition, + 'fill' : layout.colors.transition + }); + transition = paper.setFinish(); + + // set up references in activities and the transition + transition.fromActivity = fromActivity; + transition.toActivity = toActivity; + fromActivity.fromTransition = transition; + toActivity.toTransition = transition; + transition.toBack(); + transition.mousedown(function(event, x, y){ + // allow transition dragging + var mouseupHandler = function(){ + HandlerLib.dragTransitionEndHandler(transition); + }; + HandlerLib.dragItemsStartHandler(transition, this, mouseupHandler, event, x, y) + }); + + // add grouping effect if previous activity is of grouping type + if (fromActivity.type == 'group' && toActivity.type != 'gate') { + ActivityLib.addGroupingEffect(toActivity); + } + }, + + + /** + * Removes the given transition. + */ + removeTransition : function(transition) { + if (transition.toActivity.items.groupingEffect) { + transition.toActivity.items.groupingEffect.remove(); + transition.toActivity.items.groupingEffect = null; + } + transition.fromActivity.fromTransition = null; + transition.toActivity.toTransition = null; + transition.remove(); + }, + + + /** + * Draws an extra border around the selected activity. + */ + addSelectEffect : function (activity) { + // do not draw twice + if (!activity.items.selectEffect) { + var box = activity.items.shape.getBBox(); + + // a simple rectange a bit wider than the actual activity boundaries + activity.items.selectEffect = paper.rect( + box.x - 7, + box.y - 7, + box.width + 14, + box.height + 14) + .attr({ + 'stroke' : layout.colors.selectEffect, + 'stroke-dasharray' : '-' + }); + activity.items.push(activity.items.selectEffect); + + // show the properties dialog for the selected activity + layout.items.selectedActivity = activity; + layout.items.propertiesDialog.dialog('open'); + } + }, + + + /** + * Adds visual grouping effect on an activity. + */ + addGroupingEffect : function(activity) { + if (!activity.items.groupingEffect) { + var shape = activity.items.shape; + + activity.items.groupingEffect = paper.rect( + shape.attr('x') + 5, + shape.attr('y') + 5, + layout.conf.activityWidth, + layout.conf.activityHeight) + .attr({ + 'fill' : shape.attr('fill') + }) + .toBack(); + activity.items.push(activity.items.groupingEffect); + } + } +}; \ No newline at end of file Index: lams_central/web/includes/javascript/authoring/authoringGeneral.js =================================================================== RCS file: /usr/local/cvsroot/lams_central/web/includes/javascript/authoring/authoringGeneral.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ lams_central/web/includes/javascript/authoring/authoringGeneral.js 5 Nov 2013 17:37:18 -0000 1.1 @@ -0,0 +1,313 @@ +/** + * This file contains global methods for Authoring. + */ + +// few publicly visible variables +var paper = null; +var canvas = null; +var activities = []; + +// configuration and storage of various elements +var layout = { + 'toolIcons': {}, + 'conf' : { + 'activityWidth' : 125, + 'activityHeight' : 50, + 'propertiesDialogDimOpacity' : 0.3, + 'propertiesDialogDimThreshold' : 100, + 'propertiesDialogDimThrottle' : 100, + 'dragStartThreshold' : 300 + }, + 'defs' : { + 'bin' : 'M 830 680 h -50 l 10 50 h 30 z', + 'transArrow' : ' l 10 15 a 25 25 0 0 0 -20 0 z', + 'gate' : ' l-8 8 v14 l8 8 h14 l8 -8 v-14 l-8 -8 z', + 'branchingEdgeStart' : ' m -8 0 a 8 8 0 1 0 16 0 a 8 8 0 1 0 -16 0', + 'branchingEdgeEnd' : ' m -8 0 a 8 8 0 1 0 16 0 a 8 8 0 1 0 -16 0' + }, + 'colors' : { + 'activity' : '#A9C8FD', + 'gate' : 'red', + 'gateText' : 'white', + 'transition' : 'rgb(119,126,157)', + 'binActive' : 'red', + 'selectEffect' : 'blue', + 'branchingEdgeStart' : 'green', + 'branchingEdgeEnd' : 'red', + 'branchingEdgeMatch' : 'blue' + }, + 'items' : { + 'bin' : null, + 'selectedActivity' : null, + 'propertiesDialog' : null + } +}; + + +/** + * Initialises the whole Authoring window. + */ +$(document).ready(function() { + paper = Raphael('canvas'); + canvas = $('#canvas'); + + initLayout(); + initTemplates(); +}); + + +/** + * Draw boxes with Tool Activity templates. + */ +function initTemplates(){ + $('.template').each(function(){ + var toolId = $(this).attr('toolId'); + // register tool icons so they are later easily accessible + layout.toolIcons[toolId] = $('img', this).attr('src'); + + // if a tool's name is too long and gets broken into two lines + // make some adjustments to layout + var toolName = $('div', this); + if (toolName.text().length > 15){ + toolName.css('padding-top', '4px'); + } + + // allow dragging a template to canvas + $(this).draggable({ + 'containment' : '#authoringTable', + 'revert' : 'invalid', + 'distance' : 20, + 'scroll' : false, + 'scope' : 'template', + 'helper' : function(event){ + // build a simple helper + var helper = $(this).clone().css({ + 'width' : '135px', + 'border' : 'thin black solid', + 'z-index' : 1, + 'cursor' : 'move' + }); + + // Chrome does not render name of the tool correctly in the helper + // so just remove it + helper.children('div').remove(); + return helper; + } + }); + }); + + // allow dropping templates to canvas + canvas.droppable({ + 'tolerance' : 'touch', + 'scope' : 'template', + 'drop' : function (event, draggable) { + // get rid of helper div + $(draggable.helper).remove(); + + // calculate the position and create an instance of the tool activity + var toolID = draggable.draggable.attr('toolId'); + var canvasOffset = canvas.offset(); + var x = draggable.offset.left - canvasOffset.left; + var y = draggable.offset.top - canvasOffset.top; + var label = $('div', draggable.draggable).text(); + + activities.push(new ActivityLib.ToolActivity(null, toolID, x, y, label)); + } + }); +} + + +/** + * Initialises various Authoring widgets. + */ +function initLayout() { + // draw rubbish bin on canvas + layout.items.bin = paper.path(layout.defs.bin); + + // add jQuery UI button functionality + $('.ui-button').button(); + $(".split-ui-button").each(function(){ + // drop down buttons + var buttonContainer = $(this); + var buttons = buttonContainer.children(); + + buttons.first().button() + .next().button({ + text : false, + icons : { + primary : "ui-icon-triangle-1-s" + } + }); + buttonContainer.buttonset().next().hide().menu(); + + buttons.each(function(){ + var button = $(this); + if (!button.attr('onclick')) { + button.click(function() { + var menu = $(this).parent().next().show().position({ + my : "left top+2px", + at : "left bottom", + of : $(this).parent() + }); + $(document).one("click", function() { + menu.hide(); + }); + return false; + }); + } + }); + }); + + // initialise the properties dialog singleton + var propertiesDialog = layout.items.propertiesDialog = + $('
Properties go here
') + .appendTo('body') + .dialog({ + 'autoOpen' : false, + 'closeOnEscape' : false, + 'position' : { + 'my' : 'left top', + 'at' : 'left top', + 'of' : '#canvas' + }, + 'resizable' : false, + 'title' : 'Properties' + }); + // for proximity detection throttling (see handlers) + propertiesDialog.lastRun = 0; + // remove close button, add dimming + propertiesDialog.container = propertiesDialog.closest('.ui-dialog'); + propertiesDialog.container.addClass('propertiesDialog') + .css('opacity', layout.conf.propertiesDialogDimOpacity) + .mousemove(HandlerLib.approachPropertiesDialogHandler) + .find('.ui-dialog-titlebar-close').remove(); + + // initalise open Learning Design dialog + var openLDDialog = $('#openLearningDesignDialog').dialog({ + 'autoOpen' : false, + 'position' : { + 'my' : 'left top', + 'at' : 'left+5px top+5px', + 'of' : 'body' + }, + 'resizable' : false, + 'width' : 990, + 'height' : 780, + 'dialogClass': 'dialog-no-title', + 'buttons' : [ + { + 'text' : 'Open', + 'id' : 'openLDButton', + 'click' : function() { + var dialog = $(this); + var tree = dialog.dialog('option', 'ldTree'); + var ldNode = tree.getHighlightedNode(); + // no LD was chosen + if (!ldNode || !ldNode.data.learningDesignId) { + alert("Please choose a sequence"); + return; + } + + dialog.dialog('close'); + openLearningDesign(ldNode.data.learningDesignId); + } + }, + { + 'text' : 'Cancel', + 'id' : 'cancelLDButton', + 'click' : function() { + $(this).dialog('close'); + } + } + ] + }); + + $('#ldScreenshotAuthor', openLDDialog).load(function(){ + // hide "loading" animation + $('.ldChoiceDependentCanvasElement').css('display', 'none'); + // show the thumbnail + $(this).css('display', 'inline'); + }); + // there should be no focus, just highlight + YAHOO.widget.TreeView.FOCUS_CLASS_NAME = null; + + + $('#infoDialog').dialog({ + 'autoOpen' : false, + 'height' : 35, + 'width' : 290, + 'modal' : false, + 'resizable' : false, + 'show' : 'fold', + 'hide' : 'fold', + 'dialogClass': 'dialog-no-title', + 'position' : {my: "right top", + at: "right top+5px", + of: '#canvas' + } + }); + + HandlerLib.resetCanvasMode(); +} + + +/** + * Replace current canvas contents with the loaded sequence. + */ +function openLearningDesign(learningDesignId) { + // get LD details + $.ajax({ + cache : false, + url : LAMS_URL + "authoring/author.do", + dataType : 'json', + data : { + 'method' : 'getLearningDesignJSON', + 'learningDesignID': learningDesignId + }, + success : function(ld) { + if (!ld) { + alert('Error while loading sequence'); + return; + } + + // reset the canvas + paper.clear(); + // draw the rubbish bin again + layout.items.bin = paper.path(layout.defs.bin); + // create visual representation of the loaded activities + activities = []; + $.each(ld.activities, function() { + var activity = this; + + activities.push(new ActivityLib.ToolActivity(activity.activityID, + activity.toolID, activity.xCoord, activity.yCoord, + activity.activityTitle)); + }); + + // draw transitions + $.each(ld.transitions, function(){ + var transition = this; + var fromActivity = null + var toActivity = null; + + // find which activities the transition belongs to + $.each(activities, function(){ + var activity = this; + if (activity.id == transition.fromActivityID) { + fromActivity = activity; + } else if (activity.id == transition.toActivityID) { + toActivity = activity; + } + + // found both transition ends, draw it and stop the iteration + if (fromActivity && toActivity) { + ActivityLib.drawTransition(fromActivity, toActivity); + return false; + } + }); + }); + + HandlerLib.resetCanvasMode(); + } + }); +} \ No newline at end of file Index: lams_central/web/includes/javascript/authoring/authoringHandler.js =================================================================== RCS file: /usr/local/cvsroot/lams_central/web/includes/javascript/authoring/authoringHandler.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ lams_central/web/includes/javascript/authoring/authoringHandler.js 5 Nov 2013 17:37:19 -0000 1.1 @@ -0,0 +1,328 @@ +/** + * This file contains event handlers for interaction with canvas and other Authoring elements. + */ + +var HandlerLib = { + + /** + * Default mode for canvas. Run after special mode is no longer needed. + */ + resetCanvasMode : function(){ + canvas.css('cursor', 'default') + .off('click') + // if clicked anywhere, activity selection is gone + .click(HandlerLib.unselectActivityHandler) + .off('mouseup') + .off('mousemove') + // when mouse gets closer to properties dialog, make it fully visible + .mousemove(HandlerLib.approachPropertiesDialogHandler); + }, + + + /** + * Makes properties dialog fully visible. + */ + approachPropertiesDialogHandler : function(event) { + // properties dialog is a singleton + var dialog = layout.items.propertiesDialog; + // do not run this method too often + var thisRun = new Date().getTime(); + if (thisRun - dialog.lastRun < layout.conf.propertiesDialogDimThrottle){ + return; + } + dialog.lastRun = thisRun; + + // is the dialog visible at all? + var activity = layout.items.selectedActivity; + if (activity) { + // calculate dim/show threshold + var container = dialog.container, + dialogPosition = container.offset(), + dialogStartX = dialogPosition.left, + dialogStartY = dialogPosition.top, + dialogEndX = dialogStartX + container.width(), + dialogEndY = dialogStartY + container.height(), + dimTreshold = layout.conf.propertiesDialogDimThreshold, + tooFarX = event.pageX < dialogStartX - dimTreshold || event.pageX > dialogEndX + dimTreshold, + tooFarY = event.pageY < dialogStartY - dimTreshold || event.pageY > dialogEndY + dimTreshold, + opacity = tooFarX || tooFarY ? layout.conf.propertiesDialogDimOpacity : 1; + + container.css('opacity', opacity); + } + }, + + + /** + * Show selection border around the clicked activity. + */ + selectActivityHandler : function(event, activity) { + if (activity != layout.items.selectedActivity) { + HandlerLib.unselectActivityHandler(event); + ActivityLib.addSelectEffect(activity); + } + // so canvas handler unselectActivityHandler() is not run + event.preventDefault(); + }, + + + /** + * Remove activity selection when user clicks on canvas. + */ + unselectActivityHandler : function(event) { + // check if user clicked on empty space on canvas + // or on some element on top of it + var defaultPrevented = event.originalEvent ? + event.originalEvent.defaultPrevented : event.defaultPrevented; + if (!defaultPrevented) { + var selectedActivity = layout.items.selectedActivity; + // does selection exist at all? + if (selectedActivity) { + selectedActivity.items.selectEffect.remove(); + selectedActivity.items.selectEffect = null; + + // no selected activity = no properties dialog + layout.items.propertiesDialog.dialog('close'); + layout.items.selectedActivity = null; + } + } + }, + + + /** + * Open separate window with activity authoring on double click. + */ + openActivityAuthoringHandler : function(activity){ + // fetch authoring URL for a Tool Activity + if (!activity.authorURL && activity.toolID) { + $.ajax({ + async : false, + cache : false, + url : LAMS_URL + "authoring/author.do", + dataType : 'json', + data : { + 'method' : 'createToolContent', + 'toolID' : activity.toolID, + 'contentFolderID' : contentFolderID + }, + success : function(response) { + activity.authorURL = response.authorURL; + activity.id = response.toolContentID; + if (!contentFolderID) { + // if LD did not have contentFolderID, it was just generated + // so remember it + contentFolderID = response.contentFolderID; + } + } + }); + } + + if (activity.authorURL) { + window.open(activity.authorURL, 'activityAuthoring' + activity.id, + "HEIGHT=800,WIDTH=1024,resizable=yes,scrollbars=yes,status=false," + + "menubar=no,toolbar=no"); + } + }, + + + /** + * Start dragging an activity or transition. + */ + dragItemsStartHandler : function(items, draggedElement, mouseupHandler, event, startX, startY) { + items.toFront(); + // clear "clicked" flag, just in case + items.clicked = false; + + if (items.dragStarter) { + // prevent confusion when double clicking + clearTimeout(items.dragStarter); + } + + // run only if "click" event was not generated, i.e. user really wants to drag + items.dragStarter = setTimeout(function(){ + items.dragStarter = null; + // "clicked" flag is set by mouseup event + // and it means that user does not want to drag, but to click + if (items.clicked) { + items.clicked = false; + return; + } + items.isDragged = true; + items.attr('cursor', 'move'); + + canvas.mousemove(function(event) { + HandlerLib.dragItemsMoveHandler(items, event, startX, startY); + }); + + var mouseup = function(){ + // finish dragging - restore various elements' default state + items.isDragged = false; + items.unmouseup(); + HandlerLib.resetCanvasMode(); + layout.items.bin.attr('fill', 'transparent'); + + // do whetver needs to be done with the dragged elements + mouseupHandler(); + }; + + // if user moves mouse very quickly, mouseup event can be triggered + // for canvas, not the dragged elements + canvas.mouseup(mouseup); + items.mouseup(mouseup); + }, layout.conf.dragStartThreshold); + }, + + + /** + * Moves dragged elements on the canvas. + */ + dragItemsMoveHandler : function(items, event, startX, startY) { + var dx = event.pageX - startX; + var dy = event.pageY - startY; + items.transform('t' + dx + ' ' + dy); + + if (items.groupingEffect) { + items.groupingEffect.toBack(); + } + + // highlight rubbish bin if dragged elements are over it + if (Raphael.isBBoxIntersect(layout.items.bin.getBBox(), items.getBBox())) { + layout.items.bin.attr('fill', layout.colors.binActive); + } else { + layout.items.bin.attr('fill', 'transparent'); + } + }, + + + /** + * Drop the dragged activity on the canvas. + */ + dragActivityEndHandler : function(activity) { + // if the activity was over rubbish bin, remove it + if (Raphael.isBBoxIntersect(layout.items.bin.getBBox(), activity.items.shape.getBBox())) { + ActivityLib.removeActivity(activity); + return; + } + + // finally transform the dragged elements + var transformation = activity.items.shape.attr('transform'); + activity.items.transform(''); + if (transformation.length > 0) { + activity.items.forEach(function(elem) { + // some elements (rectangles) have "x", some are paths + if (elem.attr('x')) { + elem.attr({ + 'x' : elem.attr('x') + transformation[0][1], + 'y' : elem.attr('y') + transformation[0][2] + }); + } else { + var path = elem.attr('path'); + elem.attr('path', Raphael.transformPath(path, transformation)); + } + }); + } + + if (activity.items.groupingEffect) { + activity.items.groupingEffect.toBack(); + } + + // redraw transitions + if (activity.fromTransition) { + ActivityLib.drawTransition(activity, activity.fromTransition.toActivity); + } + if (activity.toTransition) { + ActivityLib.drawTransition(activity.toTransition.fromActivity, activity); + } + }, + + + /** + * Drop the dragged transition. + */ + dragTransitionEndHandler : function(transition) { + // if the transition was over rubbish bin, remove it + if (Raphael.isBBoxIntersect(layout.items.bin.getBBox(), transition.getBBox())) { + ActivityLib.removeTransition(transition); + } else { + // cancel drag + transition.transform(''); + transition.toBack(); + } + }, + + /** + * Start drawing a transition. + */ + drawTransitionStartHandler : function(activity, event, x, y) { + if (activity.fromTransition && activity.type != 'branchingEdge') { + alert('Transition from this activity already exists'); + return; + } + + var startX = x - canvas.offset().left; + var startY = y - canvas.offset().top; + + canvas.mousemove(function(event){ + HandlerLib.drawTransitionMoveHandler(activity, event, startX, startY); + }).mouseup(function(event){ + HandlerLib.drawTransitionEndHandler(activity, event); + }); + }, + + + /** + * Keep drawing a transition. + */ + drawTransitionMoveHandler : function(activity, event, startX, startY) { + // remove the temporary transition (dashed line) + if (activity.tempTransition) { + activity.tempTransition.remove(); + activity.tempTransition = null; + } + if (!event.ctrlKey) { + canvas.off('mousemove'); + canvas.off('mouseup'); + return; + } + + var endX = event.pageX - canvas.offset().left; + var endY = event.pageY - canvas.offset().top; + + // draw a temporary transition so user sees what he is doing + activity.tempTransition = paper.set(); + activity.tempTransition.push(paper.circle(startX, startY, 3)); + activity.tempTransition.push(paper.path('M ' + startX + ' ' + startY + + 'L ' + endX + ' ' + endY).attr({ + 'arrow-end' : 'open-wide-long', + 'stroke-dasharray' : '- ' + })); + }, + + + /** + * Finalise transition drawing. + */ + drawTransitionEndHandler : function(activity, event) { + HandlerLib.resetCanvasMode(); + + //remove the temporary transition + if (activity.tempTransition) { + activity.tempTransition.remove(); + activity.tempTransition = null; + } + + var endX = event.pageX - canvas.offset().left; + var endY = event.pageY - canvas.offset().top; + var endActivity = null; + // find the target activity + $.each(activities, function(){ + if (this.items.shape.isPointInside(endX, endY)) { + endActivity = this; + return false; + } + }); + + if (endActivity) { + ActivityLib.drawTransition(activity, endActivity); + } + } +}; \ No newline at end of file Index: lams_central/web/includes/javascript/authoring/authoringMenu.js =================================================================== RCS file: /usr/local/cvsroot/lams_central/web/includes/javascript/authoring/authoringMenu.js,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ lams_central/web/includes/javascript/authoring/authoringMenu.js 5 Nov 2013 17:37:19 -0000 1.1 @@ -0,0 +1,165 @@ +/** + * This file contains methods for menu in Authoring. + */ + +var MenuLib = { + + addBranching : function(){ + var dialog = $('#infoDialog').text('Place the branching point'); + dialog.dialog('open'); + + var branchingActivity = null; + canvas.css('cursor', 'pointer').click(function(event){ + // pageX and pageY tell event coordinates relative to the whole page + // we need relative to canvas + var x = event.pageX - canvas.offset().left; + var y = event.pageY - canvas.offset().top; + + var isStart = !branchingActivity; + var branchingEdge = new ActivityLib.BranchingEdgeActivity(null, x, y, branchingActivity); + activities.push(branchingEdge); + + if (isStart) { + branchingActivity = branchingEdge.branchingActivity; + dialog.text('Place the converge point'); + } else { + dialog.text(''); + dialog.dialog('close'); + + HandlerLib.resetCanvasMode(); + } + }); + }, + + /** + * Run when grouping is dropped on canvas. Creates a new grouping activity. + */ + addGrouping : function() { + canvas.css('cursor', 'url("../images/grouping.gif"), move').click(function(event){ + // pageX and pageY tell event coordinates relative to the whole page + // we need relative to canvas + var x = event.pageX - canvas.offset().left; + var y = event.pageY - canvas.offset().top; + + activities.push(new ActivityLib.GroupingActivity(null, x, y)); + HandlerLib.resetCanvasMode(); + }); + }, + + + /** + * Run when gate is dropped on canvas. Creates a new gate activity. + */ + addGate : function() { + canvas.css('cursor', 'url("../images/stop.gif"), move').click(function(event){ + // pageX and pageY tell event coordinates relative to the whole page + // we need relative to canvas + var x = event.pageX - canvas.offset().left; + var y = event.pageY - canvas.offset().top; + + activities.push(new ActivityLib.GateActivity(null, x, y)); + HandlerLib.resetCanvasMode(); + }); + }, + + + /** + * Opens "Open sequence" dialog where an user can choose a Learning Design to load. + */ + openLearningDesign : function(){ + var dialog = $('#openLearningDesignDialog'); + // remove the directory tree, if it remained for last dialog opening + $('#learningDesignTree', dialog).empty(); + dialog.dialog('open'); + + // get users' folders and LDs + var treeNodes = MenuLib.getFolderContents(); + // genearate folder tree for the widget + var tree = new YAHOO.widget.TreeView('learningDesignTree', treeNodes); + // store the tree in the dialog's data + dialog.dialog('option', 'ldTree', tree); + // make folder contents load dynamically on open + tree.setDynamicLoad(function(node, callback){ + // load subfolder contents + var childNodeData = MenuLib.getFolderContents(node.data.folderID); + if (childNodeData) { + $.each(childNodeData, function(){ + // create and add a leaf + new YAHOO.widget.TextNode(this, node); + }); + } + + // required by YUI + callback(); + }); + + tree.singleNodeHighlight = true; + tree.subscribe('clickEvent', function(event){ + + if (!event.node.data.learningDesignId){ + // it is a folder + return false; + } + + // display "loading" animation and finally LD thumbnail + $('.ldChoiceDependentCanvasElement').css('display', 'none'); + if (event.node.highlightState == 0) { + $('#ldScreenshotLoading').css('display', 'inline'); + // get the image of the chosen LD + $('#ldScreenshotAuthor').attr('src', LD_THUMBNAIL_URL_BASE + event.node.data.learningDesignId); + $('#ldScreenshotAuthor').css('width', 'auto').css('height', 'auto'); + } else { + // toggleCanvasResize(CANVAS_RESIZE_OPTION_NONE); + } + }); + tree.subscribe('clickEvent',tree.onEventToggleHighlight); + tree.render(); + + // expand the first (user) folder + tree.getRoot().children[0].expand(); + }, + + + /** + * Loads subfolders and LDs from the server. + */ + getFolderContents : function(folderID) { + var result = null; + + $.ajax({ + url : LAMS_URL + 'home.do', + data : { + 'method' : 'getFolderContents', + 'folderID' : folderID + }, + cache : false, + async: false, + dataType : 'json', + success : function(response) { + result = []; + + // parse the response; extract folders and LDs + if (response.folders) { + $.each(response.folders, function(){ + result.push({'type' : 'text', + 'label' : this.isRunSequencesFolder ? + LABEL_RUN_SEQUENCES_FOLDER : this.name, + 'folderID' : this.folderID + }); + }); + } + if (response.learningDesigns) { + $.each(response.learningDesigns, function(){ + result.push({'type' : 'text', + 'label' : this.name, + 'isLeaf' : true, + 'learningDesignId' : this.learningDesignId + }); + }); + } + } + }); + + return result; + } +}; \ No newline at end of file