Index: lams_learning/web/includes/javascript/main.js
===================================================================
RCS file: /usr/local/cvsroot/lams_learning/web/includes/javascript/main.js,v
diff -u -r1.1 -r1.2
--- lams_learning/web/includes/javascript/main.js 20 Nov 2012 14:47:03 -0000 1.1
+++ lams_learning/web/includes/javascript/main.js 29 Nov 2012 15:23:34 -0000 1.2
@@ -1,4 +1,4 @@
-var isInternetExplorer = navigator.appName.indexOf("Microsoft") != -1;
+var isInternetExplorer = navigator.appName.indexOf("Microsoft") != -1;
// ----- WINDOW MANIPULATION -----
@@ -22,6 +22,10 @@
"yes");
}
+function loadFrame(url) {
+ $('#contentFrame').attr('src', url);
+}
+
function viewNotebookEntries(){
openPopUp(APP_URL + "notebook.do?method=viewAll&lessonID=" + lessonId,
"Notebook",
@@ -79,16 +83,16 @@
}
}
-// open/clos Notebook or Support frames
+// open/close Notebook or Support frames
function toggleBarPart(name) {
var part = $('#' + name + 'Part');
var togglerCell = $('#' + name + 'TogglerCell');
if (part.is(':visible')) {
part.hide();
- togglerCell.html('â–˛');
+ togglerCell.html('▲');
} else {
part.show();
- togglerCell.html('â–Ľ');
+ togglerCell.html('▼');
}
resizeElements();
}
@@ -105,109 +109,529 @@
return formFilled;
}
+// double click triggers also single click event, this method prevents it
+function handleClicks(elem, click, dblclick) {
+ if (click) {
+ elem.click(function(e) {
+ setTimeout(function() {
+ if (elem.clickcounter) {
+ elem.clickcounter--;
+ } else {
+ click.call();
+ }
+ }, 300);
+ });
+ }
+ if (dblclick) {
+ elem.dblclick(function() {
+ elem.clickcounter = 2;
+ dblclick.call();
+ });
+ }
+}
+
//------------- RAPHAEL --------------
-var PATH_CURRENT_ACTIVITY = " v16 h16 v-16 z";
-var PATH_COMPLETED_ACTIVITY = " m -8 0 a 8 8 0 1 0 16 0 a 8 8 0 1 0 -16 0";
-var PATH_ATTEMPTED_ACTIVITY = " v16 h16 v-16 z m6 0 10 10";
-var PATH_TOSTART_ACTIVITY = " l8 16 l8 -16 z";
+var PATH_SQUARE = " v16 h16 v-16 z";
+var PATH_BIG_SQUARE = " v26 h26 v-26 z";
+var PATH_CIRCLE = " m -8 0 a 8 8 0 1 0 16 0 a 8 8 0 1 0 -16 0";
+var PATH_TRIANGLE = " l8 16 l8 -16 z";
+var PATH_OCTAGON = " l-7 7 v12 l7 7 h12 l7 -7 v-12 l-7 -7 z";
+// dark red
var COLOR_CURRENT_ACTIVITY = "rgb(187,0,0)";
+// dark blue
var COLOR_COMPLETED_ACTIVITY = "rgb(0,0,153)";
-var COLOR_ATTEMPTED_ACTIVITY = "rgb(187,0,0)";
+// green
var COLOR_TOSTART_ACTIVITY = "rgb(0,153,0)";
+// black
+var COLOR_STROKE_ACTIVITY = "rgb(0,0,0)";
+// red
+var COLOR_GATE = "rgb(255,0,0)";
+// white
+var COLOR_GATE_TEXT = "rgb(255,255,255)";
+// gray
+var COLOR_OPTIONAL_BACKGROUND = "rgb(153,153,153)";
var paper = null;
var controlFramePadding = null;
var currentActivityId = null;
var activities = [];
-function Activity(index, name, id, status, url) {
- this.index = index;
+// This should be the super class for Activities, but it's hard to accomplish in JS
+// It is a set of common methods instead.
+var ActivityUtils = {
+ // shape* methods are just preparing data, there is no actual drawing yet
+ shapeByStatus : function(activity) {
+ if (activity.status == 0) {
+ ActivityUtils.shapeCurrentActivity(activity);
+ } else if (activity.status == 1) {
+ ActivityUtils.shapeCompletedActivity(activity);
+ } else if (activity.status == 2) {
+ ActivityUtils.shapeAttemptedActivity(activity);
+ } else if (activity.status == 3) {
+ ActivityUtils.shapeToStartActivity(activity);
+ }
+ },
+
+ shapeCurrentActivity : function(activity) {
+ // dark red square
+ activity.path = "M" + (activity.middle - 8) + " " + activity.y + PATH_SQUARE;
+ activity.fill = COLOR_CURRENT_ACTIVITY;
+ activity.stroke = COLOR_STROKE_ACTIVITY;
+ activity.statusTooltip = LABEL_CURRENT_ACTIVITY;
+ },
+
+ shapeCompletedActivity : function(activity) {
+ // dark blue circle
+ activity.path = "M" + activity.middle + " " + (activity.y + 8) + PATH_CIRCLE;
+ activity.fill = COLOR_COMPLETED_ACTIVITY;
+ activity.stroke = COLOR_STROKE_ACTIVITY;
+ activity.statusTooltip = LABEL_COMPLETED_ACTIVITY;
+ },
+
+ shapeAttemptedActivity : function(activity) {
+ // green square with dark red arc
+ activity.path = "M" + (activity.middle - 8) + " " + activity.y + PATH_SQUARE;
+ activity.fill = COLOR_TOSTART_ACTIVITY;
+ activity.stroke = COLOR_STROKE_ACTIVITY;
+ activity.statusTooltip = LABEL_ATTEMPTED_ACTIVITY;
+
+ // this and similar methods are run when activity shape is drawn for real
+ activity.addDecoration = function(act) {
+ act.decoration = act.paper.set();
+ // get exact Y where square was drawn
+ // it is different than activity.y in OptionalActivity because of gray square behind it
+ var y = act.shape.attr('path')[0][2];
+ var arc = act.paper.path("M" + (act.middle - 8) + " " + y + " a16 16 0 0 0 16 16 v-16 z");
+ arc.attr({
+ 'fill' : COLOR_CURRENT_ACTIVITY,
+ 'opacity' : 0,
+ 'cursor' : 'pointer'
+ });
+ act.decoration.push(arc);
+ }
+ },
+
+ shapeToStartActivity : function(activity) {
+ // green triangle
+ activity.path = "M" + (activity.middle - 8) + " " + activity.y + PATH_TRIANGLE;
+ activity.fill = COLOR_TOSTART_ACTIVITY;
+ activity.stroke = COLOR_STROKE_ACTIVITY;
+ activity.statusTooltip = LABEL_TOSTART_ACTIVITY;
+ },
+
+ shapeGateActivity : function(activity) {
+ // red octagon
+ activity.path = "M" + (activity.middle - 6) + " " + activity.y + PATH_OCTAGON;
+ activity.fill = COLOR_GATE;
+
+ activity.addDecoration = function(act) {
+ act.decoration = activity.paper.set();
+
+ var text = act.paper.text(act.middle, act.y + 13, "STOP");
+ text.attr({
+ 'opacity' : 0,
+ 'font-size' : 9,
+ 'font' : 'sans-serif',
+ 'stroke' : COLOR_GATE_TEXT,
+ 'cursor' : 'pointer'
+ });
+ act.decoration.push(text);
+
+ if (act.status == 0) {
+ // dark red edge when current activity
+ act.statusTooltip = LABEL_CURRENT_ACTIVITY;
+
+ var edge = act.paper.path(act.path);
+ edge.attr({
+ 'opacity' : 0,
+ 'stroke' : COLOR_CURRENT_ACTIVITY,
+ 'stroke-width' : 3,
+ 'cursor' : 'pointer'
+ });
+ act.decoration.push(edge);
+ } else {
+ act.statusTooltip = LABEL_TOSTART_ACTIVITY;
+ }
+ }
+ },
+
+ shapeOptionalActivityContainer : function(activity) {
+ var addDecoration = activity.addDecoration;
+ activity.addDecoration = function(act) {
+ // run previous addDecoration(), for example defined in Attempted Activity
+ if (addDecoration) {
+ addDecoration(act);
+ }
+
+ // gray squar in background
+ var square = act.paper.path("M" + (act.middle - 13) + " " + act.y + PATH_BIG_SQUARE);
+ square.attr({
+ 'opacity' : 0,
+ 'fill' : COLOR_OPTIONAL_BACKGROUND,
+ 'cursor' : 'pointer'
+ });
+
+ // so it goes behind, not to front like other decoration
+ act.decorationWraps = true;
+ square.decorationWraps = true;
+
+ if (!act.decoration) {
+ act.decoration = act.paper.set();
+ }
+ act.decoration.push(square);
+ }
+ },
+
+ // return some attributes in Raphael consumable way
+ getShapeAttributes : function(activity) {
+ return {
+ 'path' : activity.path,
+ 'fill' : activity.fill,
+ 'stroke' : activity.stroke,
+ 'cursor' : 'pointer'
+ }
+ },
+
+
+ // adds handlers to activity for mouse interactions
+ // long method with simple actions
+ addEffects : function(activity) {
+ // remove any existing handlers
+ ActivityUtils.removeHover(activity.shape);
+ if (activity.shape.events) {
+ while (activity.shape.events.length){
+ activity.shape.events.pop().unbind();
+ }
+ }
+
+ var mouseover = function(e, x, y) {
+ // add glowing effect on hover
+ if (activity.decorationWraps) {
+ activity.decoration.forEach(function(elem){
+ if (elem.decorationWraps) {
+ // glow the wrapping decoration element
+ // for example gray square in Optonal Activity container
+ // bigger than inner activity shape
+ activity.shape.glowRef = elem.glow({
+ color : elem.attr('fill')
+ });
+ return false;
+ }
+ });
+ } else {
+ activity.shape.glowRef = activity.shape.glow({
+ color : activity.shape.attr('fill')
+ });
+ }
+
+ // add tooltip
+ var tooltipText = '' + activity.name + '
' + activity.statusTooltip;
+ // move to proper place and show
+ tooltipDiv.stop(true, true).css("left", 30).css("top", y + 20)
+ .html(tooltipText)
+ .delay(1000).fadeIn();
+ }
+
+ var mouseout = function() {
+ ActivityUtils.removeHover(activity.shape);
+ }
+
+ var isSupportActivity = activity instanceof SupportActivity;
+ var dblclick = activity.url ? function(){
+ // open pop up if it is a support or completed activity
+ if (isSupportActivity || activity.status == 1) {
+ openActivity(activity.url);
+
+ if (isSupportActivity) {
+ activity.transformToAttempted();
+ }
+ } else {
+ loadFrame(activity.url);
+ }
+ } : null;
+
+
+ var click = activity.type == 'o' ? function() {
+ ActivityUtils.showOptionalContent(activity);
+ } : null;
+
+ activity.shape.hover(mouseover, mouseout);
+ handleClicks(activity.shape, click, dblclick);
+ if (activity.decoration) {
+ activity.decoration.forEach(function(elem){
+ elem.hover(mouseover, mouseout);
+ handleClicks(elem, click, dblclick);
+ });
+ }
+ },
+
+ removeHover : function(shape){
+ if (shape.glowRef) {
+ shape.glowRef.remove();
+ shape.glowRef = null;
+ }
+ tooltipDiv.stop(true, true).fadeOut();
+ },
+
+ // copy important properties and morph visible elements
+ transform : function(sourceActivity, targetActivity) {
+ if (sourceActivity.status != targetActivity.status) {
+ sourceActivity.height = targetActivity.height;
+ sourceActivity.path = targetActivity.path;
+ sourceActivity.fill = targetActivity.fill;
+ sourceActivity.stroke = targetActivity.stroke;
+ sourceActivity.status = targetActivity.status;
+ sourceActivity.statusTooltip = targetActivity.statusTooltip;
+ sourceActivity.addDecoration = targetActivity.addDecoration;
+
+ ActivityUtils.animate(sourceActivity, sourceActivity.decoration);
+ }
+
+ if (sourceActivity.childActivities) {
+ var isCurrent = false;
+ $.each(sourceActivity.childActivities, function(childActivityIndex, childActivity){
+ var targetChildActivity = targetActivity.childActivities[childActivityIndex];
+ isCurrent |= targetChildActivity.status == 0;
+ ActivityUtils.transform(childActivity, targetChildActivity);
+ });
+ if (isCurrent && sourceActivity.showChildren) {
+ // optional sequence just became current, show it
+ sourceActivity.showChildren();
+ }
+ }
+ },
+
+ animate : function(activity, oldDecoration){
+ if (activity.shape) {
+ ActivityUtils.removeDecoration(oldDecoration, false);
+ activity.shape.animate(ActivityUtils.getShapeAttributes(activity), 2000, "linear", function() {
+ ActivityUtils.addDecoration(activity, false);
+ if(!(activity instanceof OptionalActivity)){
+ ActivityUtils.addEffects(activity);
+ }
+ });
+ }
+ },
+
+ removeDecoration : function(decoration, quick){
+ if (decoration) {
+ decoration.forEach(function(elem){
+ elem.animate({'opacity' : 0}, quick ? 0 : 1000, "linear");
+ });
+ }
+ },
+
+ // quick is for inital drawing, no nice effect is needed
+ addDecoration : function(activity, quick){
+ if (activity.decoration) {
+ activity.decoration.clear();
+ activity.decoration = null;
+ }
+
+ if (activity.addDecoration) {
+ activity.addDecoration(activity);
+ activity.decoration.forEach(function(elem){
+ if (elem.decorationWraps) {
+ // decoration element is bigger that activity shape, put it in background
+ elem.toBack();
+ } else {
+ elem.toFront();
+ }
+
+ elem.animate({'opacity' : 1}, quick ? 0 : 1000, "linear");
+ });
+ }
+ },
+
+ // draw pop up with optional activities
+ showOptionalContent : function(activity) {
+ // hide glow if shown (IE)
+ ActivityUtils.removeHover(activity.shape);
+ $('div.optionalActivity').hide();
+
+ if (!activity.optionalContent) {
+ var containerName = 'optionalActivityContent' + activity.y;
+ activity.optionalContent = $('