editor: core plugin - edumed - first take on replace exercise
authorAleksander Łukasz <aleksander.lukasz@nowoczesnapolska.org.pl>
Thu, 3 Jul 2014 14:32:47 +0000 (16:32 +0200)
committerAleksander Łukasz <aleksander.lukasz@nowoczesnapolska.org.pl>
Thu, 14 Aug 2014 14:26:13 +0000 (16:26 +0200)
src/editor/plugins/core/edumed/edumed.js
src/editor/plugins/core/edumed/edumed.less
src/editor/plugins/core/edumed/replace/actions.js [new file with mode: 0644]
src/editor/plugins/core/edumed/replace/element.js [new file with mode: 0644]
src/editor/plugins/core/edumed/replace/replace.less [new file with mode: 0644]
src/editor/plugins/core/edumed/replace/tip.html [new file with mode: 0644]
src/editor/plugins/core/edumed/replace/view.html [new file with mode: 0644]

index 25f31c9..e4a330b 100644 (file)
@@ -4,12 +4,14 @@ define(function(require) {
 
 var actions = require('./actions'),
     gapsActions = require('./gaps/actions'),
+    replaceActions = require('./replace/actions'),
     orderExerciseElement = require('./order/element'),
-    gapsExerciseElement = require('./gaps/element');
+    gapsExerciseElement = require('./gaps/element'),
+    replaceExerciseElement = require('./replace/element');
 
 return {
-    actions: actions.concat(gapsActions),
-    canvasElements: [orderExerciseElement, gapsExerciseElement]
+    actions: actions.concat(gapsActions).concat(replaceActions),
+    canvasElements: [orderExerciseElement, gapsExerciseElement, replaceExerciseElement]
 };
 
 });
\ No newline at end of file
index 248bedb..e1a2c4d 100644 (file)
@@ -1,5 +1,6 @@
 @import 'order/order.less';
 @import 'gaps/gaps.less';
+@import 'replace/replace.less';
 
 .edumed-exercise {
 
diff --git a/src/editor/plugins/core/edumed/replace/actions.js b/src/editor/plugins/core/edumed/replace/actions.js
new file mode 100644 (file)
index 0000000..be4782a
--- /dev/null
@@ -0,0 +1,154 @@
+define(function(require) {
+    
+'use strict';
+
+/* globals gettext */
+
+var _ = require('libs/underscore'),
+    Dialog = require('views/dialog/dialog');
+
+var markToReplace = {
+    name: 'markToReplace',
+    params: {
+        fragment: {type: 'context', name: 'fragment'}
+    },
+    stateDefaults: {
+        icon: null,
+        label: gettext('Mark to replace'),
+        execute: function(callback, params) {
+            var doc = params.fragment.document,
+                dialog = Dialog.create({
+                    title: gettext('Enter text to replace with'),
+                    executeButtonText: gettext('Apply'),
+                    cancelButtonText: gettext('Cancel'),
+                    fields: [
+                        {label: gettext('Text'), name: 'text', type: 'input'}
+                    ]
+                });
+
+
+            dialog.on('execute', function(event) {
+                doc.transaction(function() {
+                    var wrapper = params.fragment.startNode.parent().wrapText({
+                            _with: {tagName: 'span', attrs: {'class': 'answer', answer: event.formData.text}},
+                            offsetStart: params.fragment.startOffset,
+                            offsetEnd: params.fragment.endOffset,
+                            textNodeIdx: [params.fragment.startNode.getIndex(), params.fragment.endNode.getIndex()]
+                        }),
+                        last = _.last(wrapper.contents());
+
+                    return doc.createFragment(doc.CaretFragment, {node: last, offset: last.getText().length});
+                }, {
+                    metadata: {
+                        description: gettext('Mark to replace')
+                    },
+                    success: function(ret) { event.success(); callback(ret);}
+                });
+            });
+            dialog.show();
+
+        }
+    },
+    getState: function(params) {
+        return {
+            allowed: params.fragment &&
+                        params.fragment.isValid() &&
+                        params.fragment instanceof params.fragment.TextRangeFragment &&
+                        params.fragment.hasSameBoundries() &&
+                        params.fragment.startNode.isInside('exercise.replace') &&
+                        !params.fragment.startNode.isInside({tagName: 'span', klass: 'answer'}),
+                        
+            description: gettext('Mark selection to replacement')
+        };
+    }
+};
+
+var removeReplaceMark = {
+    name: 'removeReplaceMark',
+    params: {
+        fragment: {type: 'context', name: 'fragment'}
+    },
+    stateDefaults: {
+        icon: null,
+        label: gettext('Remove replace mark'),
+        execute: function(callback, params) {
+            var doc = params.fragment.document;
+
+            doc.transaction(function() {
+                var ret = params.fragment.node.getParent('answer').unwrapContent();
+
+                return doc.createFragment(doc.CaretFragment, {node:ret.element2, offset: ret.element2.getText().length});
+            }, {
+                metadata: {
+                    description: gettext('Remove replace mark')
+                },
+                success: callback
+            });
+        }
+    },
+    getState: function(params) {
+        return {
+            allowed: params.fragment &&
+                        params.fragment.isValid() &&
+                        params.fragment instanceof params.fragment.NodeFragment &&
+                        params.fragment.node.isInside('exercise.replace') &&
+                        params.fragment.node.isInside('answer'),
+                        
+            description: gettext('Remove replace mark')
+        };
+    }
+};
+
+var editReplaceMark = {
+    name: 'editReplaceMark',
+    params: {
+        fragment: {type: 'context', name: 'fragment'}
+    },
+    stateDefaults: {
+        icon: null,
+        label: gettext('Edit replace mark'),
+        execute: function(callback, params) {
+            var doc = params.fragment.document,
+                answerNode = params.fragment.node.getParent('answer'),
+                dialog = Dialog.create({
+                    title: gettext('Edit text to replace with'),
+                    executeButtonText: gettext('Apply'),
+                    cancelButtonText: gettext('Cancel'),
+                    fields: [
+                        {label: gettext('Text'), name: 'text', type: 'input', initialValue: answerNode.getAttr('answer')}
+                    ]
+                });
+
+
+            dialog.on('execute', function(event) {
+                doc.transaction(function() {
+                    answerNode.setAttr('answer', event.formData.text);
+                    var node = answerNode.contents()[0];
+                    return doc.createFragment(doc.CaretFragment, {node: node, offset: node.getText().length});
+                }, {
+                    metadata: {
+                        description: gettext('Edit answer')
+                    },
+                    success: function(ret) { event.success(); callback(ret);}
+                });
+            });
+            dialog.show();
+
+        }
+    },
+    getState: function(params) {
+        return {
+            allowed: params.fragment &&
+                        params.fragment.isValid() &&
+                        params.fragment instanceof params.fragment.NodeFragment &&
+                        params.fragment.node.isInside('exercise.replace') &&
+                        params.fragment.node.isInside('answer'),
+                    
+            description: gettext('Mark selection to replacement')
+        };
+    }
+};
+
+return [markToReplace, removeReplaceMark, editReplaceMark];
+
+});
\ No newline at end of file
diff --git a/src/editor/plugins/core/edumed/replace/element.js b/src/editor/plugins/core/edumed/replace/element.js
new file mode 100644 (file)
index 0000000..3f12f26
--- /dev/null
@@ -0,0 +1,86 @@
+define(function(require) {
+    
+'use strict';
+
+
+var $ = require('libs/jquery'),
+    _ = require('libs/underscore'),
+    documentElement = require('modules/documentCanvas/canvas/documentElement'),
+    genericElement = require('modules/documentCanvas/canvas/genericElement'),
+    viewTemplate = require('libs/text!./view.html'),
+    tipTemplate = require('libs/text!./tip.html');
+
+var AnswerElement = Object.create(genericElement);
+_.extend(AnswerElement, {
+    init: function() {
+        genericElement.init.call(this);
+        this.tip = $(tipTemplate);
+        this.tip.text(this.wlxmlNode.getAttr('answer') || '');
+        this.tip.on('click', function(e) {
+            var doc = this.wlxmlNode.document,
+                textNode = this.wlxmlNode.contents()[0];
+            e.preventDefault();
+            e.stopPropagation();
+
+            if(textNode) {
+                this.canvas.select(doc.createFragment(doc.CaretFragment, {node:textNode, offset: textNode.getText().length}));
+                this.canvas.showContextMenu(this, {x: e.clientX, y: e.clientY});
+            }
+        }.bind(this));
+        this.addWidget(this.tip);
+    },
+    onNodeAttrChange: function(event) {
+        if(event.meta.attr === 'answer') {
+            this.tip.text(event.meta.newVal || '');
+        }
+    }
+});
+
+var ReplaceExerciseElement = Object.create(documentElement.DocumentNodeElement.prototype);
+_.extend(ReplaceExerciseElement, {
+    init: function() {
+        documentElement.DocumentNodeElement.prototype.init.call(this);
+        var view  = $(_.template(viewTemplate)());
+        this._container().append(view);
+
+
+        this.elementsRegister.register(
+            {tag: 'span', klass: 'answer', prototype: AnswerElement}
+        );
+
+        this.createContainer(this.wlxmlNode.contents(), {
+            manages: function() {
+                return true;
+            },
+            dom: view.find('.content')
+        });
+
+        this.addToContextMenu('core.markToReplace');
+        this.contextMenuActions[0].on('actionExecuted', function(ret) {
+            if(ret instanceof this.wlxmlNode.document.Fragment && ret.isValid()) {
+                this.canvas.select(ret);
+            }
+        }.bind(this));
+        this.addToContextMenu('core.editReplaceMark');
+        this.addToContextMenu('core.removeReplaceMark');
+
+    },
+    getVerticallyFirstTextElement: function() {
+        var toret;
+        this.containers.some(function(container) {
+            toret = container.getVerticallyFirstTextElement();
+            return !!toret;
+        });
+        return toret;
+    }
+});
+
+return {tag: 'div', klass: 'exercise.replace', prototype: ReplaceExerciseElement};
+
+});
+
+
+    
+
+
+
diff --git a/src/editor/plugins/core/edumed/replace/replace.less b/src/editor/plugins/core/edumed/replace/replace.less
new file mode 100644 (file)
index 0000000..161a538
--- /dev/null
@@ -0,0 +1,16 @@
+.exercise-replace {
+    [wlxml-class="answer"] {
+        //color: @red;
+        text-decoration: line-through;
+    }
+
+    .tip {
+        position: absolute;
+        top: -12px;
+        font-size: 9px;
+        line-height: 12px;
+        color: lighten(@red, 10%);
+        white-space: nowrap;
+    }
+
+}
\ No newline at end of file
diff --git a/src/editor/plugins/core/edumed/replace/tip.html b/src/editor/plugins/core/edumed/replace/tip.html
new file mode 100644 (file)
index 0000000..5a82bef
--- /dev/null
@@ -0,0 +1,2 @@
+<div class="tip"></div>
+
diff --git a/src/editor/plugins/core/edumed/replace/view.html b/src/editor/plugins/core/edumed/replace/view.html
new file mode 100644 (file)
index 0000000..2378d40
--- /dev/null
@@ -0,0 +1,4 @@
+<div class="edumed-exercise exercise-replace">
+    <div class="header"><%= gettext('Exercise') %></div>
+    <div class="content"></div>
+</div>
\ No newline at end of file