wip: breakContent & others transformations - still need to confirm the api
authorAleksander Łukasz <aleksander.lukasz@nowoczesnapolska.org.pl>
Fri, 29 Nov 2013 15:26:49 +0000 (16:26 +0100)
committerAleksander Łukasz <aleksander.lukasz@nowoczesnapolska.org.pl>
Mon, 2 Dec 2013 21:56:12 +0000 (22:56 +0100)
src/editor/modules/documentCanvas/canvas/canvas.js
src/editor/modules/documentCanvas/canvas/keyboard.js
src/editor/modules/documentCanvas/commands.js
src/editor/modules/nodePane/nodePane.js
src/editor/plugins/core.js [new file with mode: 0644]
src/smartxml/smartxml.js
src/smartxml/transformation_api_exp.js [new file with mode: 0644]
src/smartxml/transformations.js [new file with mode: 0644]
src/wlxml/wlxml.js

index e181195..ae75d61 100644 (file)
@@ -39,7 +39,7 @@ $.extend(TextHandler.prototype, {
     },
     setText: function(text, node) {
         //this.canvas.wlxmlDocument.transform('setText', {node:node, text: text});
-        node.transform('setText', {text: text});
+        node.transform('smartxml.setText', {text: text});
 
     }
 
index dfe3f66..418dc93 100644 (file)
@@ -75,22 +75,32 @@ handlers.push({key: KEYS.ENTER,
                 }
 
                 //var nodes = position.element.data('wlxmlNode').split({offset: position.offset}),
-                var nodes = position.element.data('wlxmlNode').transform('split', {offset: position.offset}),
-                    newEmpty,
-                    goto,
-                    gotoOptions;
-
-                if(position.offsetAtBeginning)
-                    newEmpty = nodes.first;
-                else if(position.offsetAtEnd)
-                    newEmpty = nodes.second;
+                // var nodes = position.element.data('wlxmlNode').transform('split', {offset: position.offset}),
+                //     newEmpty,
+                //     goto,
+                //     gotoOptions;
+
+                // if(position.offsetAtBeginning)
+                //     newEmpty = nodes.first;
+                // else if(position.offsetAtEnd)
+                //     newEmpty = nodes.second;
                 
-                if(newEmpty) {
-                    //goto = newEmpty.append({text: ''});
+                // if(newEmpty) {
+                //     //goto = newEmpty.append({text: ''});
+                //     gotoOptions = {};
+                // } else {
+                //     goto = nodes.second;
+                //     gotoOptions = {caretTo: 'start'};
+                // }
+
+                var result = position.element.data('wlxmlNode').transform('rng.breakContent', {offset: position.offset}),
+                    goto, gotoOptions;
+                if(result.emptyText) {
+                    goto = result.createdEmpty;
                     gotoOptions = {};
                 } else {
-                    goto = nodes.second;
-                    gotoOptions = {caretTo: 'start'};
+                    goto = result.second;
+                    gotoOptions = {caretTo: 'start'};   
                 }
 
                 canvas.setCurrentElement(utils.findCanvasElement(goto), gotoOptions);
index 57a5130..8793b9c 100644 (file)
@@ -38,7 +38,9 @@ commands.register('remove-node', function(canvas) {
         parent1 = selectionStart.element.parent() || undefined,
         parent2 = selectionEnd.element.parent() || undefined;
 
-    canvas.wlxmlDocument.transform('detach2', {node:canvas.getCurrentNodeElement().data('wlxmlNode')});
+//    canvas.wlxmlDocument.transform('detach2', {node:canvas.getCurrentNodeElement().data('wlxmlNode')});
+    canvas.getCurrentNodeElement().data('wlxmlNode').transform('smartxml.detach');
+
 });
 
 commands.register('unwrap-node', function(canvas) {
@@ -108,15 +110,23 @@ commands.register('newNodeRequested', function(canvas, params) {
             if(cursor.isSelectingWithinElement()) {
                 var wlxmlNode = selectionStart.element.data('wlxmlNode'),
                     caretTo = selectionStart.offset < selectionEnd.offset ? 'start' : 'end',
-                    wrapper = wlxmlNode.wrapWith({tagName: params.wlxmlTag, attrs: {'class': params.wlxmlClass}, start: selectionStart.offset, end: selectionEnd.offset}),
-                    wrapperCanvasElement = utils.findCanvasElement(wrapper);
+                    //wrapper = wlxmlNode.wrapWith({tagName: params.wlxmlTag, attrs: {'class': params.wlxmlClass}, start: selectionStart.offset, end: selectionEnd.offset}),
+                    wrapper = wlxmlNode.transform('smartxml.wrapWith', {tagName: params.wlxmlTag, attrs: {'class': params.wlxmlClass}, start: selectionStart.offset, end: selectionEnd.offset})
+                    ;
+                var wrapperCanvasElement = utils.findCanvasElement(wrapper);
                 canvas.setCurrentElement(wrapperCanvasElement.children()[0], {caretTo: caretTo});
             }
             else {
                 var wlxmlNode = selectionStart.element.data('wlxmlNode').parent(),
                     caretTo = selectionStart.element.sameNode(cursor.getSelectionAnchor().element) ? 'end' : 'start';
 
-                var wrapper = wlxmlNode.wrapText({
+                // var wrapper = wlxmlNode.wrapText({
+                //     _with: {tagName: params.wlxmlTag, attrs: {'class': params.wlxmlClass}},
+                //     offsetStart: selectionStart.offset,
+                //     offsetEnd: selectionEnd.offset,
+                //     textNodeIdx: [wlxmlNode.indexOf(selectionStart.element.data('wlxmlNode')), wlxmlNode.indexOf(selectionEnd.element.data('wlxmlNode'))] //parent.childIndex(selectionEnd.element)]
+                // }),
+                var wrapper = wlxmlNode.transform('smartxml.wrapText', {
                     _with: {tagName: params.wlxmlTag, attrs: {'class': params.wlxmlClass}},
                     offsetStart: selectionStart.offset,
                     offsetEnd: selectionEnd.offset,
@@ -131,17 +141,23 @@ commands.register('newNodeRequested', function(canvas, params) {
                 siblingParents = canvas.wlxmlDocument.getSiblingParents({node1: node1, node2: node2});
 
             if(siblingParents) {
-                canvas.wlxmlDocument.wrapNodes({
-                    element1: siblingParents.node1,
-                    element2: siblingParents.node2,
+                // canvas.wlxmlDocument.wrapNodes({
+                //     element1: siblingParents.node1,
+                //     element2: siblingParents.node2,
+                //     _with: {tagName: params.wlxmlTag, attrs: {klass: params.wlxmlClass}}
+                // });
+                canvas.wlxmlDocument.transform('smartxml.wrapNodes', {
+                    node1: siblingParents.node1,
+                    node2: siblingParents.node2,
                     _with: {tagName: params.wlxmlTag, attrs: {klass: params.wlxmlClass}}
                 });
             }
         }
     } else if(canvas.getCurrentNodeElement()) {
-        var node = findCanvasElement(canvas.getCurrentNodeElement),
-            wrapper = node.wrapWith({tagName: params.wlxmlTag, attrs: {klass: params.wlxmlClass}});
-        canvas.setCurrentElement(findCanvasElement(wrapper));
+        var node = canvas.getCurrentNodeElement().data('wlxmlNode'),
+            // wrapper = node.wrapWith({tagName: params.wlxmlTag, attrs: {klass: params.wlxmlClass}});
+            wrapper = node.transform('smartxml.wrapWith', {tagName: params.wlxmlTag, attrs: {klass: params.wlxmlClass}});
+        canvas.setCurrentElement(utils.findCanvasElement(wrapper));
     }
 
 
index dbdd951..1ecafe1 100644 (file)
@@ -20,11 +20,11 @@ return function(sandbox) {
                 
         if(attr === 'Class') {
             //currentNode.document.transform('setClass', {node: currentNode, klass: value});
-            currentNode.transform('setAttr', {name: 'class', value: value});
+            currentNode.transform('smartxml.setAttr', {name: 'class', value: value});
         }
         //currentNode['set' + attr](value);
     });
-    
+   
     return {
         start: function() {
             sandbox.publish('ready');
@@ -33,6 +33,15 @@ return function(sandbox) {
             return view;
         },
         setNodeElement: function(wlxmlNodeElement) {
+            var module = this;
+            if(!currentNode) {
+                wlxmlNodeElement.document.on('change', function(event) {
+                    if(event.type === 'nodeAttrChange' && event.meta.node.sameNode(currentNode)) {
+                        module.setNodeElement(currentNode);
+                    }
+                });
+            }
+
             view.find('.rng-module-nodePane-tagSelect').val(wlxmlNodeElement.getTagName());
 
             var escapedClassName = (wlxmlNodeElement.getClass() || '').replace(/\./g, '-');
@@ -40,7 +49,8 @@ return function(sandbox) {
 
             var widget = metaWidget.create({attrs:wlxmlNodeElement.getMetaAttributes()});
             widget.on('valueChanged', function(key, value) {
-                wlxmlNodeElement.setMetaAttribute(key, value);
+                wlxmlNodeElement.transform('wlxml.setMetaAttribute', {name: key, value: value});
+                //wlxmlNodeElement.setMetaAttribute(key, value);
             });
             view.find('.metaFields').empty().append(widget.el);
 
diff --git a/src/editor/plugins/core.js b/src/editor/plugins/core.js
new file mode 100644 (file)
index 0000000..d8b01c8
--- /dev/null
@@ -0,0 +1,86 @@
+define([
+
+], function() {
+    
+'use strict';
+
+var breakContentTransformation = {
+    impl: function(args) {
+        var node = this.context,
+            newNodes, emptyNode, emptyText;
+        newNodes = node.transform('smartxml.split', {offset: args.offset});
+        [newNodes.first, newNodes.second].some(function(newNode) {
+            if(!(newNode.contents().length)) {
+                newNode.transform('smartxml.append', {text: ''});
+                return true; // break
+            }
+        });
+        return _.extend(newNodes, {emptyText: emptyText});
+    },
+    isAllowed: function() {
+
+    }
+};
+
+
+var breakContentAction = function(document, context) {
+    var textNode = context.cursor.currentNode;
+    if(textNode) {
+        var result, goto;
+
+        result = textNode.transform('core.break-content', {offset: context.cursor.offset});
+
+        if(result.emptyText) {
+            goto = result.createdEmpty;
+            gotoOptions = {};
+        } else {
+            goto = result.second;
+            gotoOptions = {caretTo: 'start'};   
+        }
+
+        context.setCurrentElement(goto, gotoOptions);
+    }
+}
+breakContentAction.isAllowed = function(document, context) {
+    /* globals Node */
+    var node = context.cursor.currentNode;
+    return node.nodeType === Node.TEXT_NODE;
+}
+
+return {
+    keyHandlers: [
+        {key: 'ENTER', target: 'main-document-area', handler: function(editor) {
+            var action = editor.getAction('core.break-document-content');
+            if(action.isAllowed()) {
+                action.execute();
+            }
+        }},
+        {key: 'ENTER', target: 'main-document-area', actionHandler: 'core.break-document-content'}
+    ],
+    
+    documentActions: [
+        {
+            name: 'core.break-document-content',
+            context: 'main-document-area',
+            label: 'break here'
+            icon: 'core:some-name',
+            action: breakContentAction
+        }
+    ],
+
+    // zapisywanie dokumentu:
+
+    documentTransformations: [
+        {name: 'core.break-content', textNode: true, t: breakContentTransformation},
+
+        // transformacja z poziomu smartxml
+        {name: 'core.wrap-with', textNode: true, t: wrapWith}
+
+        // list plugin:
+        {name: 'list.remove-list', elementNode: 'list', t: null}
+        // hipotetyczna akcja na itemie listy
+        {name: 'list.extract', elementNode: 'item', requiresParent: 'list', requiresInParents: '?'}
+    ]
+};
+
+});
\ No newline at end of file
index 1379153..712c0ad 100644 (file)
@@ -2,8 +2,9 @@ define([
     'libs/jquery',
     'libs/underscore',
     'libs/backbone',
-    'smartxml/events'
-], function($, _, Backbone, events) {
+    'smartxml/events',
+    'smartxml/transformations'
+], function($, _, Backbone, events, transformations) {
     
 'use strict';
 /* globals Node */
@@ -25,14 +26,6 @@ var INSERTION = function(implementation) {
     return toret;
 };
 
-// var TRANSFORMATION = function(name, implementation) {
-//     //implementation._isTransformation = true;
-
-//     createDumbTransformation(name, implementation, )
-
-//     return implementation;
-// };
-
 var DocumentNode = function(nativeNode, document) {
     if(!document) {
         throw new Error('undefined document for a node');
@@ -44,8 +37,10 @@ var DocumentNode = function(nativeNode, document) {
 
 $.extend(DocumentNode.prototype, {
 
+    transformations: new transformations.TransformationStorage(),
+
     transform: function(name, args) {
-        var Transformation = contextTransformations[name],
+        var Transformation = this.transformations.get(name),
             transformation;
         if(Transformation) {
             transformation = new Transformation(this.document, this, args);
@@ -380,6 +375,52 @@ $.extend(ElementNode.prototype, {
     }
 });
 
+// trans
+
+// todo - split+append
+
+ElementNode.prototype.transformations.register(transformations.createContextTransformation({
+    name: 'smartxml.setAttr',
+    impl: function(args) {
+        this.setAttr(args.name, args.value);
+    },
+    getChangeRoot: function() {
+        return this.context;
+    }
+}));
+
+DocumentNode.prototype.transformations.register(transformations.createContextTransformation({
+    name: 'smartxml.wrapWith',
+    getChangeRoot: function() {
+        return this.context.parent();
+    },
+    impl: function(args) {
+        return this.wrapWith(args);
+    }
+}));
+
+DocumentNode.prototype.transformations.register(transformations.createContextTransformation({
+    name: 'smartxml.wrapText',
+    getChangeRoot: function() {
+        return this.context;
+    },
+    impl: function(args) {
+        return this.wrapText(args);
+    }
+}));
+
+DocumentNode.prototype.transformations.register(transformations.createContextTransformation({
+    name: 'smartxml.detach',
+    getChangeRoot: function() {
+        return this.context.parent();
+    },
+    impl: function(args) {
+        return this.detach();
+    }
+}));
+
+///
+
 var TextNode = function(nativeNode, document) {
     DocumentNode.call(this, nativeNode, document);
 };
@@ -467,6 +508,49 @@ $.extend(TextNode.prototype, {
 });
 
 
+TextNode.prototype.transformations.register(transformations.createContextTransformation({
+    name: 'rng.breakContent',
+    // impl: function(args) {
+    //     var node = this.context,
+    //         newNodes, emptyNode, emptyText;
+    //     newNodes = node.transform('smartxml.split', {offset: args.offset});
+    //     [newNodes.first, newNodes.second].some(function(newNode) {
+    //         if(!(newNode.contents().length)) {
+    //             newNode.transform('smartxml.append', {text: ''});
+    //             return true; // break
+    //         }
+    //     });
+    //     return _.extend(newNodes, {emptyText: emptyText});
+    // },
+    impl: function(args) {
+        var node = this,
+            newNodes, emptyNode, emptyText;
+        newNodes = node.split({offset: args.offset});
+        [newNodes.first, newNodes.second].some(function(newNode) {
+            if(!(newNode.contents().length)) {
+                newNode.append({text: ''});
+                return true; // break
+            }
+        });
+        return _.extend(newNodes, {emptyText: emptyText});
+    },
+    getChangeRoot: function() {
+        return this.context.parent().parent();
+    }
+}));
+
+
+ElementNode.prototype.transformations.register(transformations.createContextTransformation({
+    name: 'smartxml.setText',
+    impl: function(args) {
+        this.setText(args.text);
+    },
+    getChangeRoot: function() {
+        return this.context;
+    }
+}));
+
+
 var parseXML = function(xml) {
     return $($.trim(xml))[0];
 };
@@ -480,6 +564,7 @@ var Document = function(xml) {
 $.extend(Document.prototype, Backbone.Events, {
     ElementNodeFactory: ElementNode,
     TextNodeFactory: TextNode,
+    transformations: new transformations.TransformationStorage(),
 
     createDocumentNode: function(from) {
         if(!(from instanceof Node)) {
@@ -651,17 +736,15 @@ $.extend(Document.prototype, Backbone.Events, {
         return insertion.ofNode;
     },
 
-    transform: function(transformationName, args) {
+    transform: function(transformation, args) {
         console.log('transform');
-        var Transformation, transformation, toret;
-        if(typeof transformationName === 'string') {
-            Transformation = transformations[transformationName];
+        var Transformation, toret;
+        if(typeof transformation === 'string') {
+            Transformation = this.transformations.get(transformation);
             if(Transformation) {
-                transformation = new Transformation(args);
+                transformation = new Transformation(this, this, args);
             }
-        } else {
-            transformation = transformationName;
-        }
+        } 
         if(transformation) {
             toret = transformation.run();
             this.undoStack.push(transformation);
@@ -669,7 +752,7 @@ $.extend(Document.prototype, Backbone.Events, {
             this.redoStack = [];
             return toret;
         } else {
-            throw new Error('Transformation ' + transformationName + ' doesn\'t exist!');
+            throw new Error('Transformation ' + transformation + ' doesn\'t exist!');
         }
     },
     undo: function() {
@@ -705,314 +788,20 @@ var defineDocumentProperties = function(doc, $document) {
     }, configurable: true});
 };
 
+Document.prototype.transformations.register(transformations.createContextTransformation({
+    name: 'smartxml.wrapNodes',
+    // init: function() {
 
-// var registerTransformationsFromObject = function(object) {
-//     _.values(object).filter(function(val) {
-//         return typeof val === 'function' && val._isTransformation;
-//     })
-//     .forEach(function(val) {
-//         registerTransformation(val._transformationName, val, object);
-//     });
-// };
-// registerTransformationsFromObject(ElementNode.prototype);
-// registerTransformationsFromObject(TextNode.prototype);
-// registerTransformationsFromObject(Document.prototype);
-
-// var Transformation = function() {
-// };
-// $.extend(Transformation.prototype, {
-
-// });
-
-
-// var createDumbTransformation = function(impl, contextObject) {
-//     var DumbTransformation = function(args) {
-//         this.args = this.args;
-//     };
-//     DumbTransformation.prototype = Object.create(Transformation.prototype);
-//     $.extend(DumbTransformation.prototype, {
-//         run: function() {
-//             impl.apply(contextObject, this.args);
-//         }
-//     });
-
-//     return DumbTransformation;
-
-
-// };
-
-var transformations = {};
-// var registerTransformation = function(name, impl, contextObject) {
-//     if(typeof impl === 'function') {
-//         transformations[name] = createDumbTransformation(impl, contextObject);
-//     }
-// };
-
-// registerTransformation('detachx', DocumentNode.prototype.detach,  )
-
-
-// 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 Transformation = function(args) {
-    this.args = args;
-};
-$.extend(Transformation.prototype, {
-    run: function() {
-        throw new Error('not implemented');
-    },
-    undo: function() {
-        throw new Error('not implemented');
-    },
-});
-
-var createGenericTransformation = function(desc) {
-    var GenericTransformation = function(document, args) {
-        //document.getNodeByPath(contextPath).call(this, args);
-        this.args = args;
-        this.document = document;
-    };
-    $.extend(GenericTransformation.prototype, {
-        run: function() {
-            var changeRoot = desc.getChangeRoot ? desc.getChangeRoot.call(this) : this.document.root;
-            this.snapshot = changeRoot.clone();
-            this.changeRootPath = changeRoot.getPath();
-            return desc.impl.call(this.context, this.args); // a argumenty do metody?
-        },
-        undo: function() {
-            this.document.getNodeByPath(this.changeRootPath).replaceWith(this.snapshot);
-        },
-    });
-
-    return GenericTransformation;
-};
-
-// var T = createGenericTransformation({impl: function() {}});
-// var t = T(doc, {a:1,b:2,c3:3});
-
-
-var createContextTransformation = function(desc) {
-    // mozna sie pozbyc przez przeniesienie object/context na koniec argumentow konstruktora generic transformation
-    var GenericTransformation = createGenericTransformation(desc);
-
-    var ContextTransformation = function(document, object, args) {
-        var contextPath = object.getPath();
-
-        GenericTransformation.call(this, document, args);
-        
-        Object.defineProperty(this, 'context', {
-            get: function() {
-                return document.getNodeByPath(contextPath);
-            }
-        });
-    }
-    ContextTransformation.prototype = Object.create(GenericTransformation.prototype);
-    return ContextTransformation;
-}
-// var T = createContextTransformation({impl: function() {}});
-// var t = T(doc, node, {a:1,b:2,c3:3});
-///
-
-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();
-    // }
-});
-
-// var TRANSFORMATION2 = function(f, getChangeRoot, undo) {
-//     var context = this,
-
-
-//     var transformation = createContextTransformation({
-//         impl: f,
-//         getChangeRoot: getChangeRoot,
-
-//     });
-
-//     var toret = function() {
-//         var 
-//         f.apply(context, createArgs ? createArgs(arguments) : arguments)
-//     };
-//     return toret;
-// }
-
-transformations['detach2'] = createTransformation({
-    // impl: function() {
-    //     //this.setAttr('class', 'cite'); //  
+    //     return this.context;
     // },
-    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);
+        this.wrapNodes(args);
     },
-    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);
 
 return {
     documentFromXML: function(xml) {
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
diff --git a/src/smartxml/transformations.js b/src/smartxml/transformations.js
new file mode 100644 (file)
index 0000000..53dc384
--- /dev/null
@@ -0,0 +1,142 @@
+define(function(require) {
+    
+'use strict';
+
+var _ = require('libs/underscore'),
+    toret = {};
+
+
+toret.createGenericTransformation = function(desc) {
+    
+    var GenericTransformation = function(document, args) {
+        this.args = args || {};
+
+        var transformation = this;
+        _.keys(this.args).forEach(function(key) {
+            if(transformation.args[key].nodeType) { //@@ change to instanceof check, fix circular dependency
+                var value = transformation.args[key],
+                    path = value.getPath();
+                Object.defineProperty(transformation.args, key, {
+                    get: function() {
+                        if(transformation.hasRun) {
+                            console.log('returning via path');
+                            return transformation.document.getNodeByPath(path);
+                        } else {
+                            console.log('returning original arg');
+                            return value;
+
+                        }
+                    }
+                });
+            }
+        });
+        this.document = document;
+        this.hasRun = false;
+        if(desc.init) {
+            desc.init.call(this);
+        }
+    };
+    _.extend(GenericTransformation.prototype, {
+        name: desc.name,
+        run: function() {
+            var changeRoot;
+            if(!desc.undo) {
+                changeRoot = desc.getChangeRoot ? desc.getChangeRoot.call(this) : this.document.root;
+                this.snapshot = changeRoot.clone();
+                this.changeRootPath = changeRoot.getPath();
+            }
+            var toret = desc.impl.call(this.context, this.args); // a argumenty do metody?
+            this.hasRun = true;
+            return toret;
+        },
+        undo: function() {
+            if(desc.undo) {
+                desc.undo.call(this.context);
+            } else {
+                this.document.getNodeByPath(this.changeRootPath).replaceWith(this.snapshot);
+            }
+        },
+    });
+
+    return GenericTransformation;
+};
+// var T = createGenericTransformation({impl: function() {}});
+// var t = T(doc, {a:1,b:2,c3:3});
+
+
+toret.createContextTransformation = function(desc) {
+    // mozna sie pozbyc przez przeniesienie object/context na koniec argumentow konstruktora generic transformation
+    var GenericTransformation = toret.createGenericTransformation(desc);
+
+    var ContextTransformation = function(document, object, args) {
+        GenericTransformation.call(this, document, args);
+
+        if(document === object) {
+            this.context = document;
+        } else {      
+            var contextPath = object.getPath();
+            Object.defineProperty(this, 'context', {
+                get: function() {
+                    // todo: to jakos inaczej, bo np. this.context w undo transformacji before to juz nie ten sam obiekt
+                    // moze transformacja powinna zwracac zmodyfikowana sciezke do obiektu po dzialaniu run?
+                    
+                    // tu tez trick z hasRun
+                    return document.getNodeByPath(contextPath);
+                }
+            });
+        }
+    }
+    ContextTransformation.prototype = Object.create(GenericTransformation.prototype);
+    return ContextTransformation;
+}
+// var T = createContextTransformation({impl: function() {}});
+// var t = T(doc, node, {a:1,b:2,c3:3});
+///
+
+
+
+toret.TransformationStorage = function() {};
+
+_.extend(toret.TransformationStorage.prototype, {
+    _transformations: {},
+    
+    register: function(Transformation) {
+        var list = (this._transformations[Transformation.prototype.name] = this._transformations[Transformation.prototype.name] || []);
+        list.push(Transformation);
+    },
+
+    get: function(name) {
+        var transformations = this._transformations[name];
+        if(!transformations) {
+            throw new Error('Transformation "' + name + '" not found!');
+        }
+        // na razie zwraca pierwsza
+        return transformations[0];
+    }
+});
+
+
+
+// var registerTransformationFromMethod = (object, methodName, desc) {
+//         if(!object[methodName]) {
+//             throw new Exeption('Cannot register transformation from unknown method ' + methodName + ' on ' + object);
+//         }
+//         desc.impl = object[name];
+//         Transformation = createContextTransformation(desc);
+//         object.prototype.registerContextTransformation(name, createContextTransformation(method));
+// };
+
+
+// registerTransformationFromMethod(ElementNode, 'setAttr', {
+//     impl: function(args) {
+//         this.setAttr(args.name, args.value);
+//     },
+//     getChangeRoot: function() {
+//         return this.context;
+//     }
+
+// });
+
+return toret;
+
+});
\ No newline at end of file
index 63d2307..e6ea26b 100644 (file)
@@ -1,8 +1,9 @@
 define([
     'libs/jquery',
     'libs/underscore',
-    'smartxml/smartxml'
-], function($, _, smartxml) {
+    'smartxml/smartxml',
+    'smartxml/transformations'
+], function($, _, smartxml, transformations) {
     
 'use strict';
 
@@ -109,7 +110,15 @@ $.extend(WLXMLElementNode.prototype, smartxml.ElementNode.prototype, {
     }
 });
 
-
+WLXMLElementNode.prototype.transformations.register(transformations.createContextTransformation({
+    name: 'wlxml.setMetaAttribute',
+    impl: function(args) {
+        this.setMetaAttribute(args.name, args.value);
+    },
+    getChangeRoot: function() {
+        return this.context;
+    }
+}));