wip: breakContent & others transformations - still need to confirm the api
[fnpeditor.git] / src / smartxml / transformation_api_exp.js
diff --git a/src/smartxml/transformation_api_exp.js b/src/smartxml/transformation_api_exp.js
new file mode 100644 (file)
index 0000000..7a03763
--- /dev/null
@@ -0,0 +1,288 @@
+//use case: edytor robi split, jesli split był na koncu (czyli druga czesc jest pusta)
+// chce tam dodac wezel tekstowy
+
+// flow: plugin_key_handler(enter, main-canvas-area) -> plugin_document_action('break-content')
+// --w srodku--> refactoring tego co teraz w keyboard:
+
+//1. jedna transformacja z warunkiem (Zarejestrowana przez plugin)
+
+
+
+var breakContentTransformation = {
+    impl: function(args) {
+        var node = this.context;
+            emptyText = false,
+            newNodes,
+            emptyNode;
+
+        newNodes = node.transform('core.split', {offset: args.offset});
+
+        if(args.offset === 0)
+            emptyNode = newNodes.first;
+        else if(args.offset === node.getText().length); //@ nie ma atEnd :(
+            emptyNode = newNodes.second;
+        
+        if(emptyNode) {
+            emptyText = emptyNode.transform('core.append', {text: ''});
+        }
+
+        return _.extend(newNodes, {emptyText: emptyText});
+    }
+};
+
+
+var breakContentAction = function(document, context) {
+    var textNode = context.currentTextNode;
+    if(textNode) {
+        var result, goto;
+
+        result = textNode.transform('core.break-content', {offset: context.offset});
+
+        if(result.emptyText) {
+            goto = result.createdEmpty;
+            gotoOptions = {};
+        } else {
+            goto = result.second;
+            gotoOptions = {caretTo: 'start'};   
+        }
+
+        context.setCurrentElement(goto, gotoOptions);
+    }
+}
+
+var toret = {
+    keyHandlers: [
+        {key: 'ENTER', target: 'main-document-area', handler: function(editor) {
+            editor.getAction('core.break-document-content').execute();
+        }},
+    ],
+    
+    actions: [
+        {name: 'core.break-document-content', context: 'main-document-area', action: breakContentAction}
+    ],
+
+    // zapisywanie dokumentu:
+
+    contextTransformations: [
+        {name: 'core.break-content', textNode: true, t: breakContentTransformation},
+
+        // list plugin:
+        {name: 'list.remove-list', elementNode: 'list', t: null}
+        // hipotetyczna akcja na itemie listy
+        {name: 'list.extract', elementNode: 'item', requiresParent: 'list', requiresInParents: '?'}
+    ],
+
+}
+
+
+/// STARE
+
+// 1. detach via totalny fallback
+var DetachNodeTransformation = function(args) {
+    this.node = args.node;
+    this.document = this.node.document;
+};
+$.extend(DetachNodeTransformation.prototype, {
+    run: function() {
+        this.oldRoot = this.node.document.root.clone();
+        this.path = this.node.getPath();
+        this.node.detach(); // @TS
+        
+    },
+    undo: function() {
+        this.document.root.replaceWith(this.oldRoot); // this.getDocument?
+        this.node = this.document.getNodeByPath(this.path);
+    }
+});
+transformations['detach'] = DetachNodeTransformation;
+
+//2. detach via wskazanie changeroot
+
+var Detach2NodeTransformation = function(args) {
+    this.nodePath = args.node.getPath();
+    this.document = args.node.document;
+};
+$.extend(Detach2NodeTransformation.prototype, {
+    run: function() {
+        var node = this.document.getNodeByPath(this.nodePath),
+            root = node.parent() ? node.parent() : this.document.root;
+        
+        this.rootPath = root.getPath();
+        this.oldRoot = (root).clone();
+        node.detach();
+    },
+    undo: function() {
+        this.document.getNodeByPath(this.rootPath).replaceWith(this.oldRoot);
+    }
+});
+//transformations['detach2'] = Detach2NodeTransformation;
+
+//2a. generyczna transformacja
+
+var createTransformation = function(desc) {
+
+    var NodeTransformation = function(args) {
+        this.nodePath = args.node.getPath();
+        this.document = args.node.document;
+        this.args = args;
+    };
+    $.extend(NodeTransformation.prototype, {
+        run: function() {
+            var node = this.document.getNodeByPath(this.nodePath),
+                root;
+
+            if(desc.getRoot) {
+                root = desc.getRoot(node);
+            } else {
+                root = this.document.root;
+            }
+            
+            this.rootPath = root.getPath();
+            this.oldRoot = (root).clone();
+            desc.impl.call(node, this.args);
+        },
+        undo: function() {
+            this.document.getNodeByPath(this.rootPath).replaceWith(this.oldRoot);
+        }
+    });
+
+    return NodeTransformation;
+}
+
+
+
+var contextTransformations = {};
+contextTransformations['setText'] = createContextTransformation({
+    impl: function(args) {
+        this.setText(args.text);
+    },
+    getChangeRoot: function() {
+        return this.context;
+    }
+});
+
+contextTransformations['setAttr'] = createContextTransformation({
+    impl: function(args) {
+        this.setAttr(args.name, args.value);
+    },
+    getChangeRoot: function() {
+        return this.context;
+    }
+});
+
+contextTransformations['split'] = createContextTransformation({
+    impl: function(args) {
+        return this.split({offset: args.offset});
+    }//,
+    // getChangeRoot: function() {
+    //     return this.context.parent().parent();
+    // }
+});
+
+
+contextTransformations['before'] = createContextTransformation({
+    getChangeRoot: function() {
+        return this.context.parent();
+    },
+    impl: function(args) {
+        this.before(args.node)
+    },
+
+});
+
+contextTransformations['before'] = createContextTransformation({
+    impl: function(args) {
+        this.before(args.node)
+    },
+    undo: function() {
+        this.context.detach();
+    }
+});
+
+
+
+transformations['detach2'] = createTransformation({
+    // impl: function() {
+    //     //this.setAttr('class', 'cite'); //  
+    // },
+    impl: ElementNode.prototype.detach,
+    getRoot: function(node) {
+        return node.parent();
+    }
+
+});
+
+transformations['setText-old'] = createTransformation({
+    impl: function(args) {
+        this.setText(args.text)
+    },
+    getRoot: function(node) {
+        return node;
+    }
+
+});
+
+transformations['setClass-old'] = createTransformation({
+    impl: function(args) {
+        this.setClass(args.klass);
+    },
+    getRoot: function(node) {
+        return node;
+    }
+})
+
+//3. detach z pełnym własnym redo
+
+var Detach3NodeTransformation = function(args) {
+    this.node = args.node;
+    this.document = this.node.document;
+};
+$.extend(Detach3NodeTransformation.prototype, {
+    run: function() {
+        //this.index = this.node.getIndex();
+        //this.parent = this.node.parent();
+        
+        this.path = this.node.getPath();
+        if(this.node.isSurroundedByTextElements()) {
+            this.prevText = this.node.prev().getText();
+            this.nextText = this.node.next().getText();
+            this.merge = true;
+        } else {
+            this.prevText = this.nextText = null;
+            this.merge = false;
+        }
+
+        this.node.detach();
+    },
+    undo: function() {
+        var parent = this.document.getNodeByPath(this.path.slice(0,-1)),
+            idx = _.last(this.path);
+        var inserted = parent.insertAtIndex(this.node, idx);
+        if(this.merge) {
+            if(inserted.next()) {
+                inserted.before({text: this.prevText});
+                inserted.next().setText(this.nextText);
+            } else {
+                inserted.prev().setText(this.prevText);
+                inserted.after({text: this.nextText});
+            }
+        }
+    }
+});
+transformations['detach3'] = Detach3NodeTransformation;
+
+
+var registerTransformationsFromObject = function(object) {
+    _.pairs(object).filter(function(pair) {
+        var property = pair[1];
+        return typeof property === 'function' && property._isTransformation;
+    })
+    .forEach(function(pair) {
+        var name = pair[0],
+            method = pair[1];
+        object.registerTransformation(name, createContextTransformation(method));
+    });
+};
+registerTransformationsFromObject(ElementNode.prototype);
+registerTransformationsFromObject(TextNode.prototype);
+registerTransformationsFromObject(Document.prototype);
\ No newline at end of file