Refactoring: cleaning directories structure
authorAleksander Łukasz <aleksander.lukasz@nowoczesnapolska.org.pl>
Fri, 13 Sep 2013 12:37:32 +0000 (14:37 +0200)
committerAleksander Łukasz <aleksander.lukasz@nowoczesnapolska.org.pl>
Fri, 13 Sep 2013 12:37:32 +0000 (14:37 +0200)
159 files changed:
Gruntfile.js
entrypoint.js [deleted file]
fnpjs/layout.js [deleted file]
fnpjs/runner.js [deleted file]
fnpjs/vbox.js [deleted file]
karma.conf.js
modules.js [deleted file]
modules/data/data.js [deleted file]
modules/data/data.less [deleted file]
modules/data/saveDialog.html [deleted file]
modules/data/saveDialog.js [deleted file]
modules/data/saveDialog.less [deleted file]
modules/diffViewer/diff.html [deleted file]
modules/diffViewer/diffViewer.js [deleted file]
modules/diffViewer/diffViewer.less [deleted file]
modules/documentCanvas/canvas/canvas.js [deleted file]
modules/documentCanvas/canvas/canvas.test.js [deleted file]
modules/documentCanvas/canvas/documentElement.js [deleted file]
modules/documentCanvas/canvas/keyboard.js [deleted file]
modules/documentCanvas/canvas/utils.js [deleted file]
modules/documentCanvas/canvas/utils.test.js [deleted file]
modules/documentCanvas/canvas/widgets.js [deleted file]
modules/documentCanvas/canvas/widgets.less [deleted file]
modules/documentCanvas/canvas/wlxmlManagers.js [deleted file]
modules/documentCanvas/classAttributes.js [deleted file]
modules/documentCanvas/commands.js [deleted file]
modules/documentCanvas/documentCanvas.js [deleted file]
modules/documentCanvas/documentCanvas.less [deleted file]
modules/documentCanvas/nodes.less [deleted file]
modules/documentCanvas/template.html [deleted file]
modules/documentCanvas/tests/classAttributes.test.js [deleted file]
modules/documentHistory/documentHistory.js [deleted file]
modules/documentHistory/documentHistory.less [deleted file]
modules/documentHistory/restoreDialog.js [deleted file]
modules/documentHistory/templates/item.html [deleted file]
modules/documentHistory/templates/main.html [deleted file]
modules/documentHistory/templates/restoreDialog.html [deleted file]
modules/documentToolbar/documentToolbar.js [deleted file]
modules/documentToolbar/documentToolbar.less [deleted file]
modules/documentToolbar/template.html [deleted file]
modules/indicator/indicator.js [deleted file]
modules/indicator/indicator.less [deleted file]
modules/indicator/template.html [deleted file]
modules/mainBar/mainBar.js [deleted file]
modules/mainBar/mainBar.less [deleted file]
modules/mainBar/template.html [deleted file]
modules/metadataEditor/metadataEditor.js [deleted file]
modules/metadataEditor/metadataEditor.less [deleted file]
modules/metadataEditor/templates/item.html [deleted file]
modules/metadataEditor/templates/main.html [deleted file]
modules/metadataEditor/transformations.js [deleted file]
modules/nodeBreadCrumbs/nodeBreadCrumbs.js [deleted file]
modules/nodeBreadCrumbs/template.html [deleted file]
modules/nodeFamilyTree/nodeFamilyTree.js [deleted file]
modules/nodeFamilyTree/nodeFamilyTree.less [deleted file]
modules/nodeFamilyTree/template.html [deleted file]
modules/nodePane/metaWidget/metaWidget.js [deleted file]
modules/nodePane/metaWidget/metaWidget.test.js [deleted file]
modules/nodePane/metaWidget/stringField.html [deleted file]
modules/nodePane/nodePane.js [deleted file]
modules/nodePane/nodePane.less [deleted file]
modules/nodePane/template.html [deleted file]
modules/rng/diffLayout.html [deleted file]
modules/rng/diffLayout.less [deleted file]
modules/rng/editingLayout.html [deleted file]
modules/rng/editingLayout.less [deleted file]
modules/rng/mainLayout.html [deleted file]
modules/rng/mainLayout.less [deleted file]
modules/rng/rng.js [deleted file]
modules/rng/rng.less [deleted file]
modules/sourceEditor/sourceEditor.js [deleted file]
modules/sourceEditor/sourceEditor.less [deleted file]
rng.js [deleted file]
src/editor/entrypoint.js [new file with mode: 0644]
src/editor/modules.js [new file with mode: 0644]
src/editor/modules/data/data.js [new file with mode: 0644]
src/editor/modules/data/data.less [new file with mode: 0644]
src/editor/modules/data/saveDialog.html [new file with mode: 0644]
src/editor/modules/data/saveDialog.js [new file with mode: 0644]
src/editor/modules/data/saveDialog.less [new file with mode: 0644]
src/editor/modules/diffViewer/diff.html [new file with mode: 0644]
src/editor/modules/diffViewer/diffViewer.js [new file with mode: 0644]
src/editor/modules/diffViewer/diffViewer.less [new file with mode: 0644]
src/editor/modules/documentCanvas/canvas/canvas.js [new file with mode: 0644]
src/editor/modules/documentCanvas/canvas/canvas.test.js [new file with mode: 0644]
src/editor/modules/documentCanvas/canvas/documentElement.js [new file with mode: 0644]
src/editor/modules/documentCanvas/canvas/keyboard.js [new file with mode: 0644]
src/editor/modules/documentCanvas/canvas/utils.js [new file with mode: 0644]
src/editor/modules/documentCanvas/canvas/utils.test.js [new file with mode: 0644]
src/editor/modules/documentCanvas/canvas/widgets.js [new file with mode: 0644]
src/editor/modules/documentCanvas/canvas/widgets.less [new file with mode: 0644]
src/editor/modules/documentCanvas/canvas/wlxmlManagers.js [new file with mode: 0644]
src/editor/modules/documentCanvas/classAttributes.js [new file with mode: 0644]
src/editor/modules/documentCanvas/commands.js [new file with mode: 0644]
src/editor/modules/documentCanvas/documentCanvas.js [new file with mode: 0644]
src/editor/modules/documentCanvas/documentCanvas.less [new file with mode: 0644]
src/editor/modules/documentCanvas/nodes.less [new file with mode: 0644]
src/editor/modules/documentCanvas/template.html [new file with mode: 0644]
src/editor/modules/documentCanvas/tests/classAttributes.test.js [new file with mode: 0644]
src/editor/modules/documentHistory/documentHistory.js [new file with mode: 0644]
src/editor/modules/documentHistory/documentHistory.less [new file with mode: 0644]
src/editor/modules/documentHistory/restoreDialog.js [new file with mode: 0644]
src/editor/modules/documentHistory/templates/item.html [new file with mode: 0644]
src/editor/modules/documentHistory/templates/main.html [new file with mode: 0644]
src/editor/modules/documentHistory/templates/restoreDialog.html [new file with mode: 0644]
src/editor/modules/documentToolbar/documentToolbar.js [new file with mode: 0644]
src/editor/modules/documentToolbar/documentToolbar.less [new file with mode: 0644]
src/editor/modules/documentToolbar/template.html [new file with mode: 0644]
src/editor/modules/indicator/indicator.js [new file with mode: 0644]
src/editor/modules/indicator/indicator.less [new file with mode: 0644]
src/editor/modules/indicator/template.html [new file with mode: 0644]
src/editor/modules/mainBar/mainBar.js [new file with mode: 0644]
src/editor/modules/mainBar/mainBar.less [new file with mode: 0644]
src/editor/modules/mainBar/template.html [new file with mode: 0644]
src/editor/modules/metadataEditor/metadataEditor.js [new file with mode: 0644]
src/editor/modules/metadataEditor/metadataEditor.less [new file with mode: 0644]
src/editor/modules/metadataEditor/templates/item.html [new file with mode: 0644]
src/editor/modules/metadataEditor/templates/main.html [new file with mode: 0644]
src/editor/modules/metadataEditor/transformations.js [new file with mode: 0644]
src/editor/modules/nodeBreadCrumbs/nodeBreadCrumbs.js [new file with mode: 0644]
src/editor/modules/nodeBreadCrumbs/template.html [new file with mode: 0644]
src/editor/modules/nodeFamilyTree/nodeFamilyTree.js [new file with mode: 0644]
src/editor/modules/nodeFamilyTree/nodeFamilyTree.less [new file with mode: 0644]
src/editor/modules/nodeFamilyTree/template.html [new file with mode: 0644]
src/editor/modules/nodePane/metaWidget/metaWidget.js [new file with mode: 0644]
src/editor/modules/nodePane/metaWidget/metaWidget.test.js [new file with mode: 0644]
src/editor/modules/nodePane/metaWidget/stringField.html [new file with mode: 0644]
src/editor/modules/nodePane/nodePane.js [new file with mode: 0644]
src/editor/modules/nodePane/nodePane.less [new file with mode: 0644]
src/editor/modules/nodePane/template.html [new file with mode: 0644]
src/editor/modules/rng/diffLayout.html [new file with mode: 0644]
src/editor/modules/rng/diffLayout.less [new file with mode: 0644]
src/editor/modules/rng/editingLayout.html [new file with mode: 0644]
src/editor/modules/rng/editingLayout.less [new file with mode: 0644]
src/editor/modules/rng/mainLayout.html [new file with mode: 0644]
src/editor/modules/rng/mainLayout.less [new file with mode: 0644]
src/editor/modules/rng/rng.js [new file with mode: 0644]
src/editor/modules/rng/rng.less [new file with mode: 0644]
src/editor/modules/sourceEditor/sourceEditor.js [new file with mode: 0644]
src/editor/modules/sourceEditor/sourceEditor.less [new file with mode: 0644]
src/editor/rng.js [new file with mode: 0644]
src/editor/styles/common.less [new file with mode: 0644]
src/editor/styles/main.less [new file with mode: 0644]
src/editor/styles/mixins.less [new file with mode: 0644]
src/editor/utils/wlxml.js [new file with mode: 0644]
src/editor/views/tabs/tabs.js [new file with mode: 0644]
src/editor/views/tabs/templates/handle.html [new file with mode: 0644]
src/editor/views/tabs/templates/main.html [new file with mode: 0644]
src/fnpjs/layout.js [new file with mode: 0644]
src/fnpjs/runner.js [new file with mode: 0644]
src/fnpjs/vbox.js [new file with mode: 0644]
styles/common.less [deleted file]
styles/main.less [deleted file]
styles/mixins.less [deleted file]
tests/main.js
utils/wlxml.js [deleted file]
views/tabs/tabs.js [deleted file]
views/tabs/templates/handle.html [deleted file]
views/tabs/templates/main.html [deleted file]

index 8b7469e..55ee1c8 100644 (file)
@@ -4,8 +4,8 @@ module.exports = function(grunt) {
         requirejs: {
           compile: {
             options: {
         requirejs: {
           compile: {
             options: {
-              baseUrl: '',
-              mainConfigFile: 'entrypoint.js',
+              baseUrl: 'src/editor',
+              mainConfigFile: 'src/editor/entrypoint.js',
               out: 'build/rng.js',
               name: 'entrypoint',
               include: ['libs/require']
               out: 'build/rng.js',
               name: 'entrypoint',
               include: ['libs/require']
@@ -19,12 +19,12 @@ module.exports = function(grunt) {
                     yuicompress: true
                 },
                 files: {
                     yuicompress: true
                 },
                 files: {
-                    'build/rng.css': 'styles/main.less'
+                    'build/rng.css': 'src/editor/styles/main.less'
                 },
             },
         },
         jshint: {
                 },
             },
         },
         jshint: {
-            all: ['Gruntfile.js', 'modules/**/*.js', 'views/**/*.js', 'fnpjs/**/*.js'],
+            all: ['Gruntfile.js', 'src/**/*.js'],
             options: {
                 jshintrc: '.jshintrc'
             }
             options: {
                 jshintrc: '.jshintrc'
             }
diff --git a/entrypoint.js b/entrypoint.js
deleted file mode 100644 (file)
index 251151e..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-(function() {
-    'use strict';
-    
-    requirejs.config({
-        baseUrl: '/static/editor',
-        
-        map: {
-            '*':
-                {
-                    'libs/jquery': 'libs/jquery-1.9.1.min',
-                    'libs/underscore': 'libs/underscore-min',
-                    'libs/bootstrap': 'libs/bootstrap/js/bootstrap.min',
-                    'libs/backbone': 'libs/backbone-min'
-                }
-        },
-
-        shim: {
-            'libs/jquery-1.9.1.min': {
-                exports: '$',
-            },
-            'libs/underscore-min': {
-                exports: '_'
-            },
-            'libs/bootstrap/js/bootstrap.min': {
-                deps: ['libs/jquery']
-            },
-            'libs/backbone-min': {
-                exports: 'Backbone',
-                deps: ['libs/jquery', 'libs/underscore']
-            }
-        }
-
-    });
-    
-    requirejs([
-        'libs/jquery',
-        'fnpjs/runner',
-        'rng',
-        './modules',
-        'libs/bootstrap'
-    ], function($, runner, rng, modules) {
-        $(function() {
-            var app = new runner.Runner(rng, modules);
-            app.setBootstrappedData('data', RNG_BOOTSTRAP_DATA);
-            app.start({rootSelector:'#editor_root'});
-        });
-    });
-
-
-})();
\ No newline at end of file
diff --git a/fnpjs/layout.js b/fnpjs/layout.js
deleted file mode 100644 (file)
index c1d110e..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-define(['libs/jquery', 'libs/underscore'], function($ ,_) {
-    'use strict';
-      
-    var Layout = function(template) {
-        var layout = this;
-        this.dom = $(_.template(template)());
-        this.views = {};
-        
-        this.dom.onShow = function() {
-            _.values(layout.views).forEach(function(view) {
-                if(view.onShow)
-                    view.onShow();
-            });
-        };
-        this.dom.onHide = function() {
-            _.values(layout.views).forEach(function(view) {
-                if(view.onHide)
-                    view.onHide();
-            });
-        };
-        
-    };
-    
-    Layout.prototype.setView = function(place, view) {
-        this.dom.find('[fnpjs-place=' + place + ']').append(view);
-        this.views[place] = view;
-        if(this.dom.is(':visible') && view.onShow) {
-            view.onShow();
-        }
-    };
-    
-    Layout.prototype.getAsView = function() {
-        return this.dom;
-    };
-    
-    return {Layout: Layout};
-});
\ No newline at end of file
diff --git a/fnpjs/runner.js b/fnpjs/runner.js
deleted file mode 100644 (file)
index 66e0b68..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-define(['libs/jquery', 'libs/underscore'], function($, _) {
-
-var Runner = function(app, modules) {
-
-    function getModuleInstance(moduleName) {
-        var module = moduleInstances[moduleName] = (moduleInstances[moduleName] || modules[moduleName](new Sandbox(moduleName)));
-        return module;
-    }
-
-    var bootstrappedData = {},
-        options = {},
-        moduleInstances = {},
-        eventListeners = [];
-        
-    _.each(_.keys(modules || {}), function(moduleName) {
-        if(_.contains(app.permissions[moduleName] || [], 'handleEvents')) {
-            eventListeners.push(moduleName);
-        }
-    });
-
-    
-        
-    var Sandbox = function(moduleName) {
-        this.$ = $;
-        this._ = _;
-        
-        this.getBootstrappedData = function() {
-            return bootstrappedData[moduleName];
-        };
-        
-        this.getTemplate = function(templateName) {
-            return _.template($('[data-template-name="' + moduleName + '.' + templateName + '"]').html().trim());
-        };
-        
-        this.publish = function(eventName) {
-            console.log(moduleName + ': ' + eventName);
-            var eventArgs = Array.prototype.slice.call(arguments, 1);
-            _.each(eventListeners, function(listenerModuleName) {
-                var listener = moduleInstances[listenerModuleName];
-                if(listener) {
-                    listener.handleEvent(moduleName, eventName, eventArgs);
-                }
-            });
-        };
-        
-        var permissions = app.permissions[moduleName];
-        
-        this.getModule = _.contains(permissions, 'getModule') ? function(requestedModuleName) {
-            return getModuleInstance(requestedModuleName);
-        } : undefined;
-        
-        this.getDOM = _.contains(permissions, 'getDOM') ? function() {
-            return $(options.rootSelector);
-        } : undefined;
-        
-    };
-    
-    
-    this.setBootstrappedData = function(moduleName, data) {
-        bootstrappedData[moduleName] = data;
-    };
-    
-    this.start = function(_options) {
-        options = _.extend({
-            rootSelector: 'body'
-        }, _options);
-        app.initModules.forEach(function(moduleName) {
-            getModuleInstance(moduleName).start();
-        });
-    };
-};
-
-return {
-    Runner: Runner
-};
-
-});
\ No newline at end of file
diff --git a/fnpjs/vbox.js b/fnpjs/vbox.js
deleted file mode 100644 (file)
index dfb5a9f..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-define(['libs/jquery', './layout'], function($, layout) {
-    
-    var VBox = function() {};
-    
-    VBox.prototype = new layout.Layout('<div class="fnpjs-vbox"></div>');
-    VBox.prototype.appendView = function(view) {
-        var item = $('<div>').addClass('fnpjs-vbox-item').append(view);
-        this.dom.append(item);
-    };
-    
-    return {VBox: VBox};
-    
-});
\ No newline at end of file
index ea3a375..e498786 100644 (file)
@@ -8,14 +8,8 @@ files = [
   
   'vkbeautify.js',
   {pattern: 'libs/*.js', included: false},
   
   'vkbeautify.js',
   {pattern: 'libs/*.js', included: false},
-  {pattern: 'fnpjs/**/*.js', included: false},
-  {pattern: 'modules/**/*.js', included: false},
-  {pattern: 'utils/**/*.js', included: false},
-  {pattern: 'views/**/*.js', included: false},
-  {pattern: 'fnpjs/**/*.html', included: false},
-  {pattern: 'modules/**/*.html', included: false},
-  {pattern: 'views/**/*.html', included: false},
-
+  {pattern: 'src/**/*.js', included: false},
+  {pattern: 'src/**/*.html', included: false},
   'tests/main.js',
 ];
 
   'tests/main.js',
 ];
 
diff --git a/modules.js b/modules.js
deleted file mode 100644 (file)
index bb4d439..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-define(function(require) {
-    /*
-       Each module must be required explicitly by apropriate 'require' function call
-       in order for requirejs optimizer to work.
-    */
-    
-    'use strict';
-    
-    return {
-        data: require('modules/data/data'),
-        rng: require('modules/rng/rng'),
-        mainBar: require('modules/mainBar/mainBar'),
-        indicator: require('modules/indicator/indicator'),
-        
-        sourceEditor: require('modules/sourceEditor/sourceEditor'),
-        
-        documentCanvas: require('modules/documentCanvas/documentCanvas'),
-        documentToolbar: require('modules/documentToolbar/documentToolbar'),
-        nodePane: require('modules/nodePane/nodePane'),
-        metadataEditor: require('modules/metadataEditor/metadataEditor'),
-        nodeFamilyTree: require('modules/nodeFamilyTree/nodeFamilyTree'),
-        nodeBreadCrumbs: require('modules/nodeBreadCrumbs/nodeBreadCrumbs'),
-        
-        documentHistory: require('modules/documentHistory/documentHistory'),
-        diffViewer: require('modules/diffViewer/diffViewer')
-        
-    }
-});
\ No newline at end of file
diff --git a/modules/data/data.js b/modules/data/data.js
deleted file mode 100644 (file)
index 9b2f163..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-define(['./saveDialog'], function(saveDialog) {
-
-'use strict';
-
-return function(sandbox) {
-
-    var doc = sandbox.getBootstrappedData().document;
-    var document_id = sandbox.getBootstrappedData().document_id;
-    var document_version = sandbox.getBootstrappedData().version;
-    var history = sandbox.getBootstrappedData().history;
-
-    
-    if(doc === '') {
-        doc = '<section\n\
-        xmlns="http://nowoczesnapolska.org.pl/sst#"\n\
-        xmlns:xlink="http://www.w3.org/1999/xlink"\n\
-        xmlns:dc="http://purl.org/dc/elements/1.1/"\n\
-        xmlns:dcterms="http://purl.org/dc/terms/"\n\
-    >\n\
-        <metadata>\n\
-        </metadata>\n\
-        <div class="p"></div>\n\
-    </section>';
-    }
-    
-    
-    function readCookie(name) {
-        var nameEQ = escape(name) + "=";
-        var ca = document.cookie.split(';');
-        for (var i = 0; i < ca.length; i++) {
-            var c = ca[i];
-            while (c.charAt(0) == ' ') c = c.substring(1, c.length);
-            if (c.indexOf(nameEQ) === 0) return unescape(c.substring(nameEQ.length, c.length));
-        }
-        return null;
-    }
-    
-    $.ajaxSetup({
-        crossDomain: false,
-        beforeSend: function(xhr, settings) {
-            if (!(/^(GET|HEAD|OPTIONS|TRACE)$/.test(settings.type))) {
-                xhr.setRequestHeader("X-CSRFToken", readCookie('csrftoken'));
-            }
-        }
-    });
-    
-    var reloadHistory = function() {
-        $.ajax({
-            method: 'get',
-            url: '/' + gettext('editor') + '/' + document_id + '/history',
-            success: function(data) {
-                history = data; 
-                sandbox.publish('historyItemAdded', data.slice(-1)[0]);
-            },
-        });
-    };
-    
-    return {
-        start: function() {
-            sandbox.publish('ready');
-        },
-        getDocument: function() {
-            return doc;
-        },
-        commitDocument: function(newDocument, reason) {
-            doc = newDocument;
-            sandbox.publish('documentChanged', doc, reason);
-        },
-        saveDocument: function() {
-
-            var dialog = saveDialog.create();
-            dialog.on('save', function(event) {
-                sandbox.publish('savingStarted');
-                dialog.toggleButtons(false);
-                $.ajax({
-                    method: 'post',
-                    url: '/' + gettext('editor') + '/' + document_id,
-                    data: JSON.stringify({document:doc, description: event.data.description}),
-                    success: function() {
-                        event.success();
-                        sandbox.publish('savingEnded', 'success');
-                        reloadHistory();
-                    },
-                    error: function() {event.error(); sandbox.publish('savingEnded', 'error');}
-                });
-                console.log('save');
-            });
-            dialog.on('cancel', function() {
-            });
-            dialog.show();
-            
-
-        },
-        getHistory: function() {
-            return history;
-        },
-        fetchDiff: function(ver1, ver2) {
-            $.ajax({
-                method: 'get',
-                url: '/' + gettext('editor') + '/' + document_id + '/diff',
-                data: {from: ver1, to: ver2},
-                success: function(data) {
-                    sandbox.publish('diffFetched', {table: data, ver1: ver1, ver2: ver2});
-                },
-            });
-        },
-        restoreVersion: function(options) {
-            if(options.version && options.description) {
-                sandbox.publish('restoringStarted', {version: options.version});
-                $.ajax({
-                    method: 'post',
-                    dataType: 'json',
-                    url: '/' + gettext('editor') + '/' + document_id + '/revert',
-                    data: JSON.stringify(options),
-                    success: function(data) {
-                        doc = data.document;
-                        document_version = data.version;
-                        reloadHistory();
-                        sandbox.publish('documentReverted', data);
-                    },
-                }); 
-            }
-        },
-        getDocumentId: function() {
-            return document_id;
-        },
-        getDocumentVersion: function() {
-            return document_version;
-        }
-    };
-};
-
-});
\ No newline at end of file
diff --git a/modules/data/data.less b/modules/data/data.less
deleted file mode 100644 (file)
index 1beb090..0000000
+++ /dev/null
@@ -1 +0,0 @@
-@import 'saveDialog.less';
\ No newline at end of file
diff --git a/modules/data/saveDialog.html b/modules/data/saveDialog.html
deleted file mode 100644 (file)
index 0846910..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<div class="rng-module-data-saveDialog modal hide static">
-    <div class="modal-header">
-        <button type="button" class="close">&times;</button>
-        <h1>Zapisz dokument</h1>
-    </div>
-    <div class="modal-body">
-        <label>Opisz swoje zmiany</label>
-        <textarea rows="5"></textarea>
-    </div>
-    <div class="modal-footer">
-        <a href="#" class="btn btn-info btn-mini save-btn">Zapisz</a>
-        <a href="#" class="btn btn-danger btn-mini cancel-btn">Anuluj</a>
-    </div>
-</div>
\ No newline at end of file
diff --git a/modules/data/saveDialog.js b/modules/data/saveDialog.js
deleted file mode 100644 (file)
index 90832e6..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-define([
-'libs/text!./saveDialog.html',
-'libs/underscore',
-'libs/backbone',
-'libs/jquery'
-], function(saveDialogTemplate, _, Backbone, $) {
-
-    var DialogView = Backbone.View.extend({
-        template: _.template(saveDialogTemplate),
-        events: {
-            'click .save-btn': 'onSave',
-            'click .cancel-btn': 'close',
-            'click .close': 'close'
-        },
-        initialize: function() {
-            _.bindAll(this);
-            this.actionsDisabled = false;
-        },
-        show: function() {           
-            this.setElement(this.template());
-            this.$el.modal({backdrop: 'static'});
-            this.$el.modal('show');
-            this.$('textarea').focus();
-
-        },
-        onSave: function(e) {
-            e.preventDefault();
-            var view = this;
-            this.trigger('save', {
-                data: {description: view.$el.find('textarea').val()},
-                success: function() { view.actionsDisabled = false; view.close(); },
-                error: function() { view.actionsDisabled = false; view.close(); },
-            });
-        },
-        close: function(e) {
-            if(e)
-                e.preventDefault();
-            if(!this.actionsDisabled) {
-                this.$el.modal('hide');
-                this.$el.remove();
-            }
-        },
-        toggleButtons: function(toggle) {
-            this.$('.btn, button').toggleClass('disabled', !toggle);
-            this.$('textarea').attr('disabled', !toggle);
-            this.actionsDisabled = !toggle;
-        }
-    });
-
-    return {
-        create: function() {
-            return new DialogView();
-        }
-    };
-
-});
\ No newline at end of file
diff --git a/modules/data/saveDialog.less b/modules/data/saveDialog.less
deleted file mode 100644 (file)
index e863e93..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-.rng-module-data-saveDialog {
-    textarea {
-        padding: 3px 3px;
-        margin: 5px auto;
-        width: 95%;
-        display: block;
-    }
-    
-    h1, label {
-        font-size: 12px;
-        line-height: 12px;
-
-    }
-    
-    h1 {
-        margin: 2px 5px;
-        font-weight: bold;
-    }
-}
\ No newline at end of file
diff --git a/modules/diffViewer/diff.html b/modules/diffViewer/diff.html
deleted file mode 100644 (file)
index cbf9b54..0000000
+++ /dev/null
@@ -1 +0,0 @@
-<div class="rng-module-diffViewer-diffView"></div>
\ No newline at end of file
diff --git a/modules/diffViewer/diffViewer.js b/modules/diffViewer/diffViewer.js
deleted file mode 100644 (file)
index e792545..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-define([
-'libs/jquery',
-'libs/underscore',
-'views/tabs/tabs',
-'libs/text!./diff.html'
-], function($, _, tabs, diffTemplateSrc) {
-
-'use strict';
-
-return function(sandbox) {
-    
-    var dom = $('<div>').addClass('rng-module-diffViewer');
-    var tabsView = (new tabs.View({position: 'right'})).render();
-    dom.append(tabsView.getAsView());
-    
-    var DiffView = function() {
-        this.dom = $(diffTemplateSrc);
-    };
-    
-    DiffView.prototype.setTable = function(table) {
-        this.dom.append(table);
-    };
-    
-
-    return {
-        start: function() {sandbox.publish('ready');},
-        getView: function() {return dom;},
-        setDiff: function(diff) {
-            var diffView = new DiffView();
-            diffView.setTable(diff.table);
-            var slug = diff.ver1 + '-' + diff.ver2;
-            tabsView.addTab(diff.ver1 + '->' + diff.ver2, slug, diffView.dom);
-            tabsView.selectTab(slug);
-        }
-    };
-};
-
-});
\ No newline at end of file
diff --git a/modules/diffViewer/diffViewer.less b/modules/diffViewer/diffViewer.less
deleted file mode 100644 (file)
index 34ab240..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-.rng-module-diffViewer {
-    .nav-tabs > li > a {
-        min-width: 0;
-        font-size: 0.9em;
-        padding: 4px 6px;
-    }
-    
-    .tab-content {
-        position: absolute;
-        top:0;
-        bottom:0;
-        left:0;
-        right:60px;
-        overflow-y: scroll;
-        &::-webkit-scrollbar {
-            .rng-mixin-scrollbar;
-        }
-        &::-webkit-scrollbar-track {
-            .rng-mixin-scrollbar-track;
-        }
-        &::-webkit-scrollbar-thumb {
-            .rng-mixin-scrollbar-thumb;
-        }
-        &::-webkit-scrollbar-thumb:window-inactive {
-            .rng-mixin-scrollbar-thumb-window-inactive;
-        }
-    }
-    
-    .diff_table {
-        border-width: 1px 0 1px 1px;
-        border-style: solid;
-        border-color: #ddd;
-        empty-cells: show;
-        border-spacing: 0px;
-    }
-
-    .diff_table td {
-        border-width: 0px 1px 1px 0px;
-        border-style: dotted;
-        border-color: grey;
-        font-size: 10px;
-        line-height: 20px;
-        font-family: monospace;
-        padding: 0px;
-        white-space: pre-line;
-        /*word-wrap:break-word;
-         word-break:break-all; */
-    }
-
-    .diff_table th {
-        border-width: 0px 1px 1px 0px;
-        border-style: solid;
-        border-color: #ddd;
-        background: #e5ffe5;
-    }
-
-    .diff_table tr.change {
-        background-color: #dcdcdc;
-    }
-
-    .diff_mark {
-        display: inline-block;
-        padding: 2px;
-    }
-
-    .diff_mark_removed {
-        background-color: #ff9c94;
-    }
-
-    .diff_mark_added {
-        background-color: #90ee90;
-    }
-
-    .diff_mark_changed {
-        background-color: yellow;
-    }
-}
\ No newline at end of file
diff --git a/modules/documentCanvas/canvas/canvas.js b/modules/documentCanvas/canvas/canvas.js
deleted file mode 100644 (file)
index d8db2bc..0000000
+++ /dev/null
@@ -1,657 +0,0 @@
-define([
-'libs/jquery',
-'libs/underscore',
-'libs/backbone',
-'modules/documentCanvas/canvas/documentElement',
-'modules/documentCanvas/canvas/keyboard',
-'modules/documentCanvas/canvas/utils'
-], function($, _, Backbone, documentElement, keyboard, utils) {
-    
-'use strict';
-
-var Canvas = function(wlxml, publisher) {
-    this.eventBus = _.extend({}, Backbone.Events);
-    this.loadWlxml(wlxml);
-    this.publisher = publisher ? publisher : function() {};
-};
-
-$.extend(Canvas.prototype, {
-
-    loadWlxml: function(wlxml) {
-        var d = wlxml ? $($.trim(wlxml)) : null;
-        if(d) {
-            this.wrapper = $('<div>').addClass('canvas-wrapper').attr('contenteditable', true);
-            this.wrapper.append(d);
-            var canvas = this;
-
-            this.wrapper.find('*').replaceWith(function() {
-                var currentTag = $(this);
-                if(currentTag.attr('wlxml-tag'))
-                    return;
-
-                var meta = {}, others = {};
-                for(var i = 0; i < this.attributes.length; i++) {
-                    var attr = this.attributes[i];
-                    if(attr.name.substr(0, 5) === 'meta-')
-                        meta[attr.name.substr(5)] = attr.value;
-                    else if(attr.name !== 'class')
-                        others[attr.name] = attr.value;
-                }
-
-                var element = canvas.createNodeElement({
-                    tag: currentTag.prop('tagName').toLowerCase(),
-                    klass: currentTag.attr('class'),
-                    meta: meta,
-                    others: others,
-                    rawChildren: currentTag.contents(),
-                    prepopulateOnEmpty: true
-                });
-
-                ['orig-before', 'orig-after', 'orig-begin', 'orig-end'].forEach(function(attr) {
-                    element.data(attr, '');
-                });
-                return element.dom();
-            });
-
-            var FIRST_CONTENT_INDEX = 0;
-
-            // @@ TODO - refactor!
-            var getNode = function(element) {
-                return element.children('[document-element-content]');
-            }
-
-            this.wrapper.find(':not(iframe)').addBack().contents()
-                .filter(function() {return this.nodeType === Node.TEXT_NODE})
-                .each(function() {
-
-                    // TODO: use DocumentElement API
-
-                    var el = $(this),
-                        text = {original: el.text(), trimmed: $.trim(el.text())},
-                        elParent = el.parent(),
-                        hasSpanParent = elParent.attr('wlxml-tag') === 'span',
-                        hasSpanBefore = el.prev().length > 0  && getNode($(el.prev()[0])).attr('wlxml-tag') === 'span',
-                        hasSpanAfter = el.next().length > 0 && getNode($(el.next()[0])).attr('wlxml-tag') === 'span';
-
-                    if(el.parent().hasClass('canvas-widget') || elParent.attr('document-text-element') !== undefined)
-                        return true; // continue
-
-                    var addInfo = function(toAdd, where) {
-                        var parentContents = elParent.contents(),
-                            idx = parentContents.index(el[0]),
-                            prev = idx > FIRST_CONTENT_INDEX ? parentContents[idx-1] : null,
-                            next = idx < parentContents.length - 1 ? parentContents[idx+1] : null,
-                            target, key;
-
-                        if(where === 'above') {
-                            target = prev ? $(prev) : elParent.parent();
-                            key = prev ? 'orig-after' : 'orig-begin';
-                        } else if(where === 'below') {
-                            target = next ? $(next) : elParent.parent();
-                            key = next ? 'orig-before' : 'orig-end';
-                        } else { throw new Object;}
-
-                        target.data(key, toAdd);
-                    }
-
-                    text.transformed = text.trimmed;
-                    
-                    if(hasSpanParent || hasSpanBefore || hasSpanAfter) {
-                        var startSpace = /\s/g.test(text.original.substr(0,1)),
-                            endSpace = /\s/g.test(text.original.substr(-1)) && text.original.length > 1;
-                        text.transformed = (startSpace && (hasSpanParent || hasSpanBefore) ? ' ' : '')
-                                    + text.trimmed
-                                    + (endSpace && (hasSpanParent || hasSpanAfter) ? ' ' : '');
-                    } else {
-                        if(text.trimmed.length === 0 && text.original.length > 0 && elParent.contents().length === 1)
-                            text.transformed = ' ';
-                    }
-
-                    if(!text.transformed) {
-                        addInfo(text.original, 'below');
-                        el.remove();
-                        return true; // continue
-                    }
-
-                    if(text.transformed !== text.original) {
-                        if(!text.trimmed) {
-                            addInfo(text.original, 'below');
-                        } else {
-                            var startingMatch = text.original.match(/^\s+/g),
-                                endingMatch = text.original.match(/\s+$/g),
-                                startingWhiteSpace = startingMatch ? startingMatch[0] : null,
-                                endingWhiteSpace = endingMatch ? endingMatch[0] : null;
-
-                            if(endingWhiteSpace) {
-                                if(text.transformed[text.transformed.length - 1] === ' ' && endingWhiteSpace[0] === ' ')
-                                    endingWhiteSpace = endingWhiteSpace.substr(1);
-                                addInfo(endingWhiteSpace, 'below');
-                            }
-
-                            if(startingWhiteSpace) {
-                                if(text.transformed[0] === ' ' && startingWhiteSpace[startingWhiteSpace.length-1] === ' ')
-                                    startingWhiteSpace = startingWhiteSpace.substr(0, startingWhiteSpace.length -1);
-                                addInfo(startingWhiteSpace, 'above');
-                            }
-                        }
-                    }
-
-                    var element = documentElement.DocumentTextElement.create({text: text.transformed});
-                    el.replaceWith(element.dom());
-                });
-            
-            this.d = this.wrapper.children(0);
-
-            this.wrapper.on('keyup keydown keypress', function(e) {
-                keyboard.handleKey(e, this);
-            }.bind(this));
-
-            this.wrapper.on('click', '[document-node-element], [document-text-element]', function(e) {
-                e.stopPropagation();
-                canvas.setCurrentElement(canvas.getDocumentElement(e.currentTarget), {caretTo: false});
-            });
-
-            var observer = new MutationObserver(function(mutations) {
-                mutations.forEach(function(mutation) {
-                    if(documentElement.DocumentTextElement.isContentContainer(mutation.target)) {
-                        observer.disconnect();
-                        if(mutation.target.data === '')
-                            mutation.target.data = utils.unicode.ZWS;
-                        else if(mutation.oldValue === utils.unicode.ZWS) {
-                            mutation.target.data = mutation.target.data.replace(utils.unicode.ZWS, '');
-                            canvas._moveCaretToTextElement(canvas.getDocumentElement(mutation.target), 'end');
-                        }
-                        observer.observe(canvas.d[0], config);
-                        canvas.publisher('contentChanged');
-                    }
-                });
-            });
-            var config = { attributes: false, childList: false, characterData: true, subtree: true, characterDataOldValue: true};
-            observer.observe(this.d[0], config);
-
-
-            this.wrapper.on('mouseover', '[document-node-element], [document-text-element]', function(e) {
-                var el = canvas.getDocumentElement(e.currentTarget);
-                if(!el)
-                    return;
-                e.stopPropagation();
-                if(el instanceof documentElement.DocumentTextElement)
-                    el = el.parent();
-                el.toggleLabel(true);
-            });
-            this.wrapper.on('mouseout', '[document-node-element], [document-text-element]', function(e) {
-                var el = canvas.getDocumentElement(e.currentTarget);
-                if(!el)
-                    return;
-                e.stopPropagation();
-                if(el instanceof documentElement.DocumentTextElement)
-                    el = el.parent();
-                el.toggleLabel(false);
-            });
-
-            this.eventBus.on('elementToggled', function(toggle, element) {
-                if(!toggle) {
-                    canvas.setCurrentElement(element.getPreviousTextElement());
-                }
-            })
-
-        } else {
-            this.d = null;
-        }
-    },
-
-    view: function() {
-        return this.wrapper;
-    },
-
-    doc: function() {
-        if(this.d === null)
-            return null;
-        return documentElement.DocumentNodeElement.fromHTMLElement(this.d.get(0), this); //{wlxmlTag: this.d.prop('tagName')};
-    },
-
-    createNodeElement: function(params) {
-        return documentElement.DocumentNodeElement.create(params, this);
-    },
-
-    wrapText: function(params) {
-        params = _.extend({textNodeIdx: 0}, params);
-        if(typeof params.textNodeIdx === 'number')
-            params.textNodeIdx = [params.textNodeIdx];
-        
-        var childrenInside = params.inside.children(),
-            idx1 = Math.min.apply(Math, params.textNodeIdx),
-            idx2 = Math.max.apply(Math, params.textNodeIdx),
-            textNode1 = childrenInside[idx1],
-            textNode2 = childrenInside[idx2],
-            sameNode = textNode1.sameNode(textNode2),
-            prefixOutside = textNode1.getText().substr(0, params.offsetStart),
-            prefixInside = textNode1.getText().substr(params.offsetStart),
-            suffixInside = textNode2.getText().substr(0, params.offsetEnd),
-            suffixOutside = textNode2.getText().substr(params.offsetEnd)
-        ;
-        
-        var wrapperElement = this.createNodeElement({tag: params._with.tag, klass: params._with.klass});
-        textNode1.after(wrapperElement);
-        textNode1.detach();
-        
-        if(prefixOutside.length > 0)
-            wrapperElement.before({text:prefixOutside});
-        if(sameNode) {
-            var core = textNode1.getText().substr(params.offsetStart, params.offsetEnd - params.offsetStart);
-            wrapperElement.append({text: core});
-        } else {
-            textNode2.detach();
-            if(prefixInside.length > 0)
-                wrapperElement.append({text: prefixInside});
-            for(var i = idx1 + 1; i < idx2; i++) {
-                wrapperElement.append(childrenInside[i]);
-            }
-            if(suffixInside.length > 0)
-                wrapperElement.append({text: suffixInside});
-        }
-        if(suffixOutside.length > 0)
-            wrapperElement.after({text: suffixOutside});
-        return wrapperElement;
-    },
-
-    wrapElements: function(params) {
-        if(!(params.element1.parent().sameNode(params.element2.parent())))
-            return;
-
-        var parent = params.element1.parent(),
-            parentChildren = parent.children(),
-            wrapper = this.createNodeElement({
-                tag: params._with.tag,
-                klass: params._with.klass}),
-            idx1 = parent.childIndex(params.element1),
-            idx2 = parent.childIndex(params.element2);
-
-        if(idx1 > idx2) {
-            var tmp = idx1;
-            idx1 = idx2;
-            idx2 = tmp;
-        }
-
-        var insertingMethod, insertingTarget;
-        if(idx1 === 0) {
-            insertingMethod = 'prepend';
-            insertingTarget = parent;
-        } else {
-            insertingMethod = 'after';
-            insertingTarget = parentChildren[idx1-1];
-        }
-
-        for(var i = idx1; i <= idx2; i++) {
-            wrapper.append(parentChildren[i].detach());
-        }
-
-        insertingTarget[insertingMethod](wrapper);
-        return wrapper;
-    },
-
-    getSiblingParents: function(params) {
-        var parents1 = [params.element1].concat(params.element1.parents()).reverse(),
-            parents2 = [params.element2].concat(params.element2.parents()).reverse(),
-            noSiblingParents = null;
-
-        if(parents1.length === 0 || parents2.length === 0 || !(parents1[0].sameNode(parents2[0])))
-            return noSiblingParents;
-
-        var i;
-        for(i = 0; i < Math.min(parents1.length, parents2.length); i++) {
-            if(parents1[i].sameNode(parents2[i]))
-                continue;
-            break;
-        }
-        return {element1: parents1[i], element2: parents2[i]};
-    },
-
-    getDocumentElement: function(from) {
-        if(from instanceof HTMLElement || from instanceof Text) {
-           return documentElement.DocumentElement.fromHTMLElement(from, this);
-        }
-    },
-    getCursor: function() {
-        return new Cursor(this);
-    },
-
-    list: {},
-
-    
-    getCurrentNodeElement: function() {
-        return this.getDocumentElement(this.wrapper.find('.current-node-element').parent()[0]);
-    },
-
-    getCurrentTextElement: function() {
-        return this.getDocumentElement(this.wrapper.find('.current-text-element')[0]);
-    },
-
-
-
-    setCurrentElement: function(element, params) {
-        params = _.extend({caretTo: 'end'}, params);
-        var findFirstDirectTextChild = function(e, nodeToLand) {
-            var byBrowser = this.getCursor().getPosition().element;
-            if(byBrowser && byBrowser.parent().sameNode(nodeToLand))
-                return byBrowser;
-            var children = e.children();
-            for(var i = 0; i < children.length; i++) {
-                if(children[i] instanceof documentElement.DocumentTextElement)
-                    return children[i];
-            }
-            return null;
-        }.bind(this);
-        var _markAsCurrent = function(element) {
-            if(element instanceof documentElement.DocumentTextElement) {
-                this.wrapper.find('.current-text-element').removeClass('current-text-element');
-                element.dom().addClass('current-text-element');
-            } else {
-                this.wrapper.find('.current-node-element').removeClass('current-node-element')
-                element._container().addClass('current-node-element');
-                this.publisher('currentElementChanged', element);
-            }
-        }.bind(this);
-
-
-        var isTextElement = element instanceof documentElement.DocumentTextElement,
-            nodeElementToLand = isTextElement ? element.parent() : element,
-            textElementToLand = isTextElement ? element : findFirstDirectTextChild(element, nodeElementToLand),
-            currentTextElement = this.getCurrentTextElement(),
-            currentNodeElement = this.getCurrentNodeElement();
-
-        if(currentTextElement && !(currentTextElement.sameNode(textElementToLand)))
-            this.wrapper.find('.current-text-element').removeClass('current-text-element');
-
-        if(textElementToLand) {
-            _markAsCurrent(textElementToLand);
-            if(params.caretTo || !textElementToLand.sameNode(this.getCursor().getPosition().element))
-                this._moveCaretToTextElement(textElementToLand, params.caretTo); // as method on element?
-            if(!(textElementToLand.sameNode(currentTextElement)))
-                this.publisher('currentTextElementSet', textElementToLand);
-        } else {
-            document.getSelection().removeAllRanges();
-        }
-
-        if(!(currentNodeElement && currentNodeElement.sameNode(nodeElementToLand))) {
-            _markAsCurrent(nodeElementToLand);
-
-            this.publisher('currentNodeElementSet', nodeElementToLand);
-        }
-    },
-
-    _moveCaretToTextElement: function(element, where) {
-        var range = document.createRange(),
-            node = element.dom().contents()[0];
-
-        if(typeof where !== 'number') {
-            range.selectNodeContents(node);
-        } else {
-            range.setStart(node, where);
-        }
-        
-        var collapseArg = true;
-        if(where === 'end')
-            collapseArg = false;
-        range.collapse(collapseArg);
-        
-        var selection = document.getSelection();
-
-        selection.removeAllRanges();
-        selection.addRange(range);
-        this.wrapper.focus(); // FF requires this for caret to be put where range colllapses, Chrome doesn't.
-    },
-
-    setCursorPosition: function(position) {
-        if(position.element)
-            this._moveCaretToTextElement(position.element, position.offset);
-    },
-
-    toXML: function() {
-        var parent = $('<div>'),
-            parts = this.doc().toXML(0);
-        parent.append(parts);
-        return parent.html();
-    }
-});
-
-$.extend(Canvas.prototype.list, {
-    create: function(params) {
-        if(!(params.element1.parent().sameNode(params.element2.parent())))
-            return false;
-            
-        var parent = params.element1.parent(),
-            canvas = params.element1.canvas;
-        
-        if(parent.childIndex(params.element1) > parent.childIndex(params.element2)) {
-            var tmp = params.element1;
-            params.element1 = params.element2;
-            params.element2 = tmp;
-        }
-        
-        var elementsToWrap = [];
-        
-        var place = 'before';
-        parent.children().some(function(element) {
-            var _e = element;
-            if(element.sameNode(params.element1))
-                place = 'inside';
-            if(place === 'inside') {
-                if(element instanceof documentElement.DocumentTextElement) {
-                    element = element.wrapWithNodeElement({tag: 'div', klass: 'list.item'});
-                    if(element.children()[0].sameNode(params.element1))
-                        params.element1 = element;
-                }
-                element.setWlxmlClass('item');
-                elementsToWrap.push(element);
-            }
-            if(_e.sameNode(params.element2))
-                return true;
-        });
-        
-        var listElement = canvas.createNodeElement({tag: 'div', klass: 'list-items' + (params.type === 'enum' ? '-enum' : '')});
-        var toret;
-        if(parent.is('list')) {
-            var item = listElement.wrapWithNodeElement({tag: 'div', klass: 'item'});
-            item.exec('toggleBullet', false);
-            toret = listElement.parent();
-        } else {
-            toret = listElement;
-        }  
-        
-        params.element1.before(toret);
-        
-        elementsToWrap.forEach(function(element) {
-            element.detach();
-            listElement.append(element);
-        });
-    },
-    extractItems: function(params) {
-        params = _.extend({merge: true}, params);
-        var list = params.element1.parent();
-        if(!list.is('list') || !(list.sameNode(params.element2.parent())))
-            return false;
-
-        var idx1 = list.childIndex(params.element1),
-            idx2 = list.childIndex(params.element2),
-            precedingItems = [],
-            extractedItems = [],
-            succeedingItems = [],
-            items = list.children(),
-            listIsNested = list.parent().getWlxmlClass() === 'item',
-            canvas = params.element1.canvas,
-            i;
-
-        if(idx1 > idx2) {
-            var tmp = idx1; idx1 = idx2; idx2 = tmp;
-        }
-
-        items.forEach(function(item, idx) {
-            if(idx < idx1)
-                precedingItems.push(item);
-            else if(idx >= idx1 && idx <= idx2) {
-                extractedItems.push(item);
-            }
-            else {
-                succeedingItems.push(item);
-            }
-        });
-
-        var reference = listIsNested ? list.parent() : list;
-        if(succeedingItems.length === 0) {
-            var reference_orig = reference;
-            extractedItems.forEach(function(item) {
-                reference.after(item);
-                reference = item;
-                if(!listIsNested)
-                    item.setWlxmlClass(null);
-            });
-            if(precedingItems.length === 0)
-                reference_orig.detach();
-        } else if(precedingItems.length === 0) {
-            extractedItems.forEach(function(item) {
-                reference.before(item);
-                if(!listIsNested)
-                    item.setWlxmlClass(null);
-            });
-        } else {
-            extractedItems.forEach(function(item) {
-                reference.after(item);
-                if(!listIsNested)
-                    item.setWlxmlClass(null);
-                reference = item;
-            });
-            var secondList = canvas.createNodeElement({tag: 'div', klass:'list-items'}),
-                toAdd = secondList;
-            
-            if(listIsNested) {
-                toAdd = secondList.wrapWithNodeElement({tag: 'div', klass:'item'});
-            }
-            succeedingItems.forEach(function(item) {
-                secondList.append(item);
-            });
-
-            reference.after(toAdd);
-        }
-        if(!params.merge && listIsNested) {
-            return this.extractItems({element1: extractedItems[0], element2: extractedItems[extractedItems.length-1]});
-        }
-        return true;
-    },
-    areItemsOfTheSameList: function(params) {
-        var e1 = params.element1,
-            e2 = params.element2;
-        return e1.parent().sameNode(e2.parent())
-            && e1.parent().is('list');
-    }
-});
-
-
-var Cursor = function(canvas) {
-    this.canvas = canvas;
-};
-
-$.extend(Cursor.prototype, {
-    isSelecting: function() {
-        var selection = window.getSelection();
-        return !selection.isCollapsed;
-    },
-    isSelectingWithinElement: function() {
-        return this.isSelecting() && this.getSelectionStart().element.sameNode(this.getSelectionEnd().element);
-    },
-    isSelectingSiblings: function() {
-        return this.isSelecting() && this.getSelectionStart().element.parent().sameNode(this.getSelectionEnd().element.parent());
-    },
-    getPosition: function() {
-        return this.getSelectionAnchor();
-    },
-    getSelectionStart: function() {
-        return this.getSelectionBoundry('start');
-    },
-    getSelectionEnd: function() {
-        return this.getSelectionBoundry('end');
-    },
-    getSelectionAnchor: function() {
-        return this.getSelectionBoundry('anchor');
-    },
-    getSelectionFocus: function() {
-        return this.getSelectionBoundry('focus');
-    },
-    getSelectionBoundry: function(which) {
-        var selection = window.getSelection(),
-            anchorElement = this.canvas.getDocumentElement(selection.anchorNode),
-            focusElement = this.canvas.getDocumentElement(selection.focusNode);
-        
-        if((!anchorElement) || (anchorElement instanceof documentElement.DocumentNodeElement) || (!focusElement) || focusElement instanceof documentElement.DocumentNodeElement)
-            return {};
-
-        if(which === 'anchor') {
-            return {
-                element: anchorElement,
-                offset: selection.anchorOffset,
-                offsetAtBeginning: selection.anchorOffset === 0,
-                offsetAtEnd: selection.anchorNode.data.length === selection.anchorOffset
-            };
-        }
-        if(which === 'focus') {
-            return {
-                element: focusElement,
-                offset: selection.focusOffset,
-                offsetAtBeginning: selection.focusOffset === 0,
-                offsetAtEnd: selection.focusNode.data.length === selection.focusOffset
-            };
-        }
-        
-        var element,
-            offset;
-
-        if(anchorElement.parent().sameNode(focusElement.parent())) {
-            var parent = anchorElement.parent(),
-                anchorFirst = parent.childIndex(anchorElement) < parent.childIndex(focusElement);
-            if(anchorFirst) {
-                if(which === 'start') {
-                    element = anchorElement;
-                    offset = selection.anchorOffset
-                }
-                else if(which === 'end') {
-                    element = focusElement,
-                    offset = selection.focusOffset
-                }
-            } else {
-                if(which === 'start') {
-                    element = focusElement,
-                    offset = selection.focusOffset
-                }
-                else if(which === 'end') {
-                    element = anchorElement;
-                    offset = selection.anchorOffset
-                }
-            }
-        } else {
-            // TODO: Handle order via https://developer.mozilla.org/en-US/docs/Web/API/Node.compareDocumentPosition
-            if(which === 'start') {
-                element = anchorElement;
-                offset = selection.anchorOffset
-            } else {
-                element = focusElement;
-                offset = selection.focusOffset
-            }
-        }
-
-        var nodeLen = (element.sameNode(focusElement) ? selection.focusNode : selection.anchorNode).length;
-        return {
-            element: element,
-            offset: offset,
-            offsetAtBeginning: offset === 0,
-            offsetAtEnd: nodeLen === offset
-        }
-    }
-})
-
-return {
-    fromXML: function(xml, publisher) {
-        return new Canvas(xml, publisher);
-    }
-};
-
-});
\ No newline at end of file
diff --git a/modules/documentCanvas/canvas/canvas.test.js b/modules/documentCanvas/canvas/canvas.test.js
deleted file mode 100644 (file)
index 2dc24eb..0000000
+++ /dev/null
@@ -1,1553 +0,0 @@
-define([
-'libs/chai',
-'libs/sinon',
-'modules/documentCanvas/canvas/canvas',
-'modules/documentCanvas/canvas/documentElement',
-'modules/documentCanvas/canvas/utils'
-], function(chai, sinon, canvas, documentElement, utils) {
-    
-'use strict';
-
-var expect = chai.expect;
-
-
-describe('Canvas', function() {
-
-
-
-    describe('ZWS', function() {
-        var view, section, textElement;
-        
-        beforeEach(function() {
-            var c = canvas.fromXML('<section></section>');
-
-            section = c.doc();
-            textElement = section.children()[0];
-            view = c.view()[0];
-            document.getElementsByTagName('body')[0].appendChild(view);
-        });
-
-        afterEach(function() {
-            view.parentNode.removeChild(view);
-        });
-
-        var getTextContainerNode = function(textElement) {
-            return textElement.dom().contents()[0];
-        }
-
-        it('is set automatically on all empty DocumentTextElements', function() {
-            expect(getTextContainerNode(textElement).data).to.equal(utils.unicode.ZWS);
-
-            var header = section.append({tag: 'header'}),
-                newText = header.append({text: ''}),
-                textNode = getTextContainerNode(textElement);
-            
-            expect(textNode.data).to.equal(utils.unicode.ZWS);
-        });
-
-        it('is added automatically when whole text gets deleted', function() {
-            getTextContainerNode(textElement).data = '';
-            
-            window.setTimeout(function() {
-                expect(getTextContainerNode(textElement).data).to.equal(utils.unicode.ZWS);
-            }, 0)
-            
-            var header = section.append({tag: 'header'}),
-                newText = header.append({text: 'Alice'}),
-                textNode = getTextContainerNode(newText);
-
-            expect(textNode.data).to.have.length('Alice'.length);
-            textNode.data = '';
-
-            window.setTimeout(function() {
-                expect(textNode.data).to.equal(utils.unicode.ZWS);
-            }, 0)
-        });
-    });
-
-    describe('Internal HTML representation of a DocumentNodeElement', function() {
-        it('is always a div tag', function() {
-            ['section', 'header', 'span', 'aside', 'figure'].forEach(function(tagName) {
-                var dom = canvas.fromXML('<' + tagName + '></' + tagName + '>').doc().dom();
-                expect(dom.prop('tagName')).to.equal('DIV', tagName + ' is represented as div');
-            });
-        });
-        it('has wlxml tag put into wlxml-tag attribute of its internal container', function() {
-            var dom = canvas.fromXML('<section></section>').doc().dom();
-            expect(dom.children('[document-element-content]').attr('wlxml-tag')).to.equal('section');
-        });
-        it('has wlxml class put into wlxml-class attribute of its internal containr, dots replaced with dashes', function() {
-            var dom = canvas.fromXML('<section class="some.class"></section>').doc().dom();
-            expect(dom.children('[document-element-content]').attr('wlxml-class')).to.equal('some-class');
-        });
-    });
-
-    describe('Internal HTML representation of a DocumentTextElement', function() {
-        it('is text node wrapped in a div with document-text-element attribute set', function() {
-            var dom = canvas.fromXML('<section>Alice</section>').doc().children()[0].dom();
-            expect(dom.prop('tagName')).to.equal('DIV');
-            expect(dom.attr('document-text-element')).to.equal('');
-            expect(dom.contents().length).to.equal(1);
-            expect(dom.contents()[0].nodeType).to.equal(Node.TEXT_NODE);
-            expect($(dom.contents()[0]).text()).to.equal('Alice');
-        });
-    });
-
-    describe('basic properties', function() {
-        it('renders empty document when canvas created from empty XML', function() {
-            var c = canvas.fromXML('');
-            expect(c.doc()).to.equal(null);
-        });
-
-        it('gives access to its document root node', function() {
-            var c = canvas.fromXML('<section></section>');
-            expect(c.doc().getWlxmlTag()).to.equal('section');
-        });
-
-        describe('root element', function() {
-            it('has no parent', function() {
-                var c = canvas.fromXML('<section></section>');
-                expect(c.doc().parent()).to.be.null;
-            });
-        });
-
-        describe('DocumentTextElement', function() {
-            it('can have its content set', function() {
-                var c = canvas.fromXML('<section>Alice</section>'),
-                    root = c.doc(),
-                    text = root.children()[0];
-                
-                text.setText('a cat');
-                expect(root.children()[0].getText()).to.equal('a cat');
-            });
-        });
-
-        describe('DocumentNodeElement', function() {
-            it('knows index of its child', function() {
-                var c = canvas.fromXML('<section><div></div><header></header><span></span></section>'),
-                    root = c.doc(),
-                    child = root.children()[1];
-                expect(root.childIndex(child)).to.equal(1);
-            });
-
-            it('knows WLXML tag it renders', function(){
-                var c = canvas.fromXML('<section></section>'),
-                    section = c.doc();
-                expect(section.getWlxmlTag()).to.equal('section', 'initial tag is section');
-                section.setWlxmlTag('header');
-                expect(section.getWlxmlTag()).to.equal('header', 'tag is changed to header');
-            });
-
-            it('knows WLXML class of a WLXML tag it renders', function(){
-                var c = canvas.fromXML('<section class="some.class.A"></section>'),
-                    section = c.doc();
-                expect(section.getWlxmlClass()).to.equal('some.class.A');
-                section.setWlxmlClass('some.class.B');
-                expect(section.getWlxmlClass()).to.equal('some.class.B');
-                section.setWlxmlClass(null);
-                expect(section.getWlxmlClass()).to.be.undefined;
-            });
-
-
-
-            describe('element has meta attributes', function() {
-                it('can change its meta attributes', function() {
-                    var c = canvas.fromXML('<section><span class="uri" meta-uri="someuri"></span></section>'),
-                    span = c.doc().children()[0];
-                    
-                    expect(span.getWlxmlMetaAttr('uri')).to.equal('someuri');
-                    span.setWlxmlMetaAttr('uri', 'otheruri');
-                    expect(span.getWlxmlMetaAttr('uri')).to.equal('otheruri');
-                });
-
-                it('changes its meta attributes with class change', function() {
-                    var c = canvas.fromXML('<section><span class="uri" meta-uri="someuri"></span></section>'),
-                    span = c.doc().children()[0];
-                    
-                    expect(span.getWlxmlMetaAttr('uri')).to.equal('someuri');
-                    span.setWlxmlClass('author');
-                    expect(span.getWlxmlMetaAttr('uri')).to.be.undefined;
-                });
-
-                it('keeps meta attribute value on class change if a new class has this attribute', function() {
-                    var c = canvas.fromXML('<section><span class="uri" meta-uri="someuri"></span></section>'),
-                    span = c.doc().children()[0];
-                    span.setWlxmlClass('uri.some.subclass');
-                    expect(span.getWlxmlMetaAttr('uri')).to.equal('someuri');
-                });
-            });
-        });
-
-        it('returns DocumentNodeElement instance from HTMLElement', function() {
-            var c = canvas.fromXML('<section></section>'),
-                htmlElement = c.doc().dom().get(0),
-                element = c.getDocumentElement(htmlElement);
-            expect(element).to.be.instanceOf(documentElement.DocumentNodeElement);
-            expect(element.sameNode(c.doc()));
-        });
-        
-        it('returns DocumentTextElement instance from Text Node', function() {
-            var c = canvas.fromXML('<section>Alice</section>'),
-                aliceElement = c.doc().children()[0],
-                textNode = aliceElement.dom().contents()[0],
-                element = c.getDocumentElement(textNode);
-
-            expect(textNode.nodeType).to.equal(Node.TEXT_NODE, 'text node selected');
-            expect($(textNode).text()).to.equal('Alice');
-
-            expect(element).to.be.instanceOf(documentElement.DocumentTextElement);
-            expect(element.sameNode(c.doc().children()[0]));
-        });
-    });
-
-
-
-    describe('document representation api', function() {
-        describe('document root element', function() {
-            var c = canvas.fromXML('<section></section>');
-            it('exists', function() {
-                expect(c.doc()).to.be.instanceOf(documentElement.DocumentElement);
-            });
-            it('is of type DocumentNodeElement', function() {
-                expect(c.doc()).to.be.instanceOf(documentElement.DocumentNodeElement);
-            });
-        });
-
-        describe('DocumentElements comparison', function() {
-            it('reports dwo DocumentElements to be the same when they represent the same wlxml document element', function() {
-                var c = canvas.fromXML('<section><div></div><div></div></section>'),
-                    first_div1 = c.doc().children()[0],
-                    first_div2 = c.doc().children()[0],
-                    second_div = c.doc().children()[1];
-                expect(first_div1.sameNode(first_div1)).to.be.true;
-                expect(first_div1.sameNode(first_div2)).to.be.true;
-                expect(first_div1.sameNode(second_div)).to.be.false;
-            });
-        });
-
-        describe('traversing', function() {
-            it('reports element nodes', function() {
-                var c = canvas.fromXML('<section><div></div></section>'),
-                    children = c.doc().children();
-                expect(children.length).to.equal(1);
-                expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
-
-                c = canvas.fromXML('<section><div></div><div></div></section>'),
-                    children = c.doc().children();
-                expect(children.length).to.equal(2);
-                expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
-                expect(children[1]).to.be.instanceOf(documentElement.DocumentNodeElement);
-            });
-            
-            it('reports text nodes', function() {
-                var c = canvas.fromXML('<section>Alice</section>'),
-                    children = c.doc().children();
-                expect(children.length).to.equal(1);
-                expect(children[0]).to.be.instanceOf(documentElement.DocumentTextElement);
-            });
-
-            describe('accessing parents', function() {
-                it('returns DocumentNodeElement representing parent in wlxml document as DocumentNodeElement parent', function() {
-                    var c = canvas.fromXML('<section><div></div></section>'),
-                        div = c.doc().children()[0];
-                    expect(div.parent().sameNode(c.doc())).to.be.true;
-                });
-                it('returns DocumentNodeElement representing parent in wlxml document as DocumentTextElement parent', function() {
-                    var c = canvas.fromXML('<section>Alice</section>'),
-                        text = c.doc().children()[0];
-                    expect(text.parent().sameNode(c.doc())).to.be.true;
-                });
-            });
-
-            describe('accessing sibling parents of two elements', function() {
-                it('returns elements themself if they have direct common parent', function() {
-                    var c = canvas.fromXML('<section>\
-                        <div>\
-                            <div>A</div>\
-                            <div>B</div>\
-                        </div>\
-                    </section>'),
-                        section = c.doc(),
-                        wrappingDiv = c.doc().children()[0],
-                        divA = wrappingDiv.children()[0],
-                        divB = wrappingDiv.children()[1];
-
-                    var siblingParents = c.getSiblingParents({element1: divA, element2: divB});
-
-                    expect(siblingParents.element1.sameNode(divA)).to.equal(true, 'divA');
-                    expect(siblingParents.element2.sameNode(divB)).to.equal(true, 'divB');
-                });
-
-                it('returns sibling parents - example 1', function() {
-                    var c = canvas.fromXML('<section>Alice <span>has a cat</span></section>'),
-                        section = c.doc(),
-                        aliceText = section.children()[0],
-                        span = section.children()[1],
-                        spanText = span.children()[0];
-
-                    var siblingParents = c.getSiblingParents({element1: aliceText, element2: spanText});
-
-                    expect(siblingParents.element1.sameNode(aliceText)).to.equal(true, 'aliceText');
-                    expect(siblingParents.element2.sameNode(span)).to.equal(true, 'span');
-                });
-            })
-
-            describe('free text handling', function() {
-                    it('sees free text', function() {
-                        var c = canvas.fromXML('<section>Alice <span>has</span> a cat</section>'),
-                            children = c.doc().children();
-                        expect(children.length).to.equal(3);
-                        expect(children[0]).to.be.instanceOf(documentElement.DocumentTextElement);
-                        expect(children[1]).to.be.instanceOf(documentElement.DocumentNodeElement);
-                        expect(children[2]).to.be.instanceOf(documentElement.DocumentTextElement);
-                    });
-            });
-
-            describe('empty nodes handling', function() {
-                it('says empty element node from XML source has one empty DocumentTextElement', function() {
-                    var c = canvas.fromXML('<section></section>');
-                    expect(c.doc().children()).to.have.length(1);
-                    expect(c.doc().children()[0].getText()).to.equal('');
-                });
-
-                it('allows creation of an empty element node', function() {
-                    var c = canvas.fromXML('<section></section>'),
-                        section = c.doc(),
-                        header = section.append({tag: 'header'});
-                    expect(header.children()).to.have.length(0);
-                });
-            });
-            
-            describe('white characters handling', function() {
-
-                it('says element node with one space has one DocumentTextElement', function() {
-                    var c = canvas.fromXML('<section> </section>');
-                    expect(c.doc().children().length).to.equal(1);
-                    expect(c.doc().children()[0]).to.be.instanceOf(documentElement.DocumentTextElement);
-                    expect(c.doc().children()[0].getText()).to.equal(' ');
-                });
-                it('ignores white space surrounding block elements', function() {
-                    var c = canvas.fromXML('<section> <div></div> </section>');
-                    var children = c.doc().children();
-                    expect(children.length).to.equal(1);
-                    expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
-                });
-                it('ignores white space between block elements', function() {
-                    var c = canvas.fromXML('<section><div></div> <div></div></section>');
-                    var children = c.doc().children();
-                    expect(children.length === 2);
-                    [0,1].forEach(function(idx) {
-                        expect(children[idx]).to.be.instanceOf(documentElement.DocumentNodeElement);
-                    });
-                });
-
-                it('trims white space from the beginning and the end of the block elements', function() {
-                    var c = canvas.fromXML('<section> Alice <span>has</span> a cat </section>');
-                    expect(c.doc().children()[0].getText()).to.equal('Alice ');
-                    expect(c.doc().children()[2].getText()).to.equal(' a cat');
-                });
-
-                it('normalizes string of white characters to one space at the inline element boundries', function() {
-                    var c = canvas.fromXML('<span>   Alice has a cat   </span>');
-                    expect(c.doc().children()[0].getText()).to.equal(' Alice has a cat ');
-                });
-
-                it('normalizes string of white characters to one space before inline element', function() {
-                    var c = canvas.fromXML('<div>Alice has  <span>a cat</span></div>');
-                    expect(c.doc().children()[0].getText()).to.equal('Alice has ');
-                });
-                
-                it('normalizes string of white characters to one space after inline element', function() {
-                    var c = canvas.fromXML('<div>Alice has <span>a</span>  cat</div>');
-                    expect(c.doc().children()[2].getText()).to.equal(' cat');
-                });
-            });
-
-            describe('getting vertically first text element', function() {
-                it('returns the first child if it\'s text element, ignores metadata', function() {
-                    var c = canvas.fromXML('<section><metadata><dc:author>author</dc:author></metadata>Alice<div>has</div>a cat</section>'),
-                        first = c.doc().getVerticallyFirstTextElement();
-
-                    expect(first.sameNode(c.doc().children()[1])).to.be.true;
-                });
-
-                it('looks recursively inside node elements if they precede text element', function() {
-                    var c = canvas.fromXML('\
-                            <section>\
-                                <div>\
-                                    <div>\
-                                        Alice\
-                                    </div>\
-                                </div>\
-                                Some text\
-                            </section>'),
-                        textAlice = c.doc().children()[0].children()[0].children()[0],
-                        first = c.doc().getVerticallyFirstTextElement();
-
-                    expect(textAlice).to.be.instanceOf(documentElement.DocumentTextElement);
-                    expect(first.sameNode(textAlice)).to.be.true;
-                });
-            });
-        });
-
-        describe('manipulation api', function() {
-
-            describe('Basic Element inserting', function() {
-                it('can put new NodeElement at the end', function() {
-                    var c = canvas.fromXML('<section><div></div></section>'),
-                        appended = c.doc().append({tag: 'header', klass: 'some.class'}),
-                        children = c.doc().children();
-
-                    expect(children.length).to.equal(2);
-                    expect(children[1].sameNode(appended)).to.be.true;
-                });
-
-                it('can put new TextElement at the end', function() {
-                    var c = canvas.fromXML('<section><div><div></section>'),
-                        appended = c.doc().append({text: 'Alice'}),
-                        children = c.doc().children();
-
-                    expect(children.length).to.equal(2);
-                    expect(children[1].sameNode(appended)).to.be.true;
-                    expect(children[1].getText()).to.equal('Alice');
-                });
-
-                it('can put new NodeElement at the beginning', function() {
-                    var c = canvas.fromXML('<section><div></div></section>'),
-                        prepended = c.doc().prepend({tag: 'header', klass: 'some.class'}),
-                        children = c.doc().children();
-
-                    expect(children).to.have.length(2);
-                    expect(children[0].sameNode(prepended)).to.be.true;
-                });
-
-                it('can put new TextElement at the beginning', function() {
-                    var c = canvas.fromXML('<section><div></div></section>'),
-                        prepended = c.doc().prepend({text: 'Alice'}),
-                        children = c.doc().children();
-
-                    expect(children).to.have.length(2)
-                    expect(children[0].sameNode(prepended)).to.be.true;
-                    expect(children[0].getText()).to.equal('Alice');
-                });
-
-                it('can put new NodeElement after another NodeElement', function() {
-                    var c = canvas.fromXML('<section><div></div></section>'),
-                        div = c.doc().children()[0],
-                        added = div.after({tag: 'header', klass: 'some.class'}),
-                        children = c.doc().children();
-                    expect(children.length).to.equal(2);
-                    expect(children[1].sameNode(added)).to.be.true;
-                });
-
-                it('can put new Nodeelement before another element', function() {
-                    var c = canvas.fromXML('<section><div></div></section>'),
-                        div = c.doc().children()[0],
-                        added = div.before({tag: 'header', klass: 'some.class'}),
-                        children = c.doc().children();
-                    expect(children.length).to.equal(2);
-                    expect(children[0].sameNode(added)).to.be.true;
-                });
-
-                it('can put new DocumentNodeElement after DocumentTextElement', function() {
-                    var c = canvas.fromXML('<section>Alice</section>'),
-                        text = c.doc().children()[0],
-                        added = text.after({tag: 'p'}),
-                        children = c.doc().children();
-
-                    expect(children.length).to.equal(2);
-                    expect(children[0]).to.be.instanceOf(documentElement.DocumentTextElement);
-                    expect(children[0].getText()).to.equal('Alice');
-                    expect(children[1]).to.be.instanceOf(documentElement.DocumentNodeElement);
-                    expect(children[1].sameNode(added)).to.be.true;
-                });
-                it('can put new DocumentNodeElement before DocumentTextElement', function() {
-                    var c = canvas.fromXML('<section>Alice</section>'),
-                        text = c.doc().children()[0],
-                        added = text.before({tag: 'p'}),
-                        children = c.doc().children();
-
-                    expect(children.length).to.equal(2);
-                    expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
-                    expect(children[0].sameNode(added)).to.be.true;
-                    expect(children[1]).to.be.instanceOf(documentElement.DocumentTextElement);
-                    expect(children[1].getText()).to.equal('Alice');
-                });
-
-                it('can divide DocumentTextElement with a new DocumentNodeElement', function() {
-                    var c = canvas.fromXML('<section>Alice has a cat</section>'),
-                        section = c.doc(),
-                        text = section.children()[0];
-
-                    var returned = text.divide({tag: 'aside', klass: 'footnote', offset: 5}),
-                        sectionChildren = section.children(),
-                        lhsText = sectionChildren[0],
-                        rhsText = sectionChildren[2];
-
-                    expect(lhsText.getText()).to.equal('Alice');
-                    expect(returned.sameNode(sectionChildren[1]));
-                    expect(rhsText.getText()).to.equal(' has a cat');
-                });
-
-                it('treats dividing DocumentTextElement at the very end as appending after it', function() {
-                    var c = canvas.fromXML('<section>Alice has a cat</section>'),
-                        section = c.doc(),
-                        text = section.children()[0];
-
-                    var returned = text.divide({tag: 'aside', klass: 'footnote', offset: 15}),
-                        sectionChildren = section.children(),
-                        textElement = sectionChildren[0],
-                        nodeElement = sectionChildren[1];
-
-                    expect(sectionChildren.length).to.equal(2);
-                    expect(textElement.getText()).to.equal('Alice has a cat');
-                    expect(returned.sameNode(nodeElement)).to.be.true;
-                    expect(nodeElement.getWlxmlTag()).to.equal('aside');
-                });
-
-                it('treats dividing DocumentTextElement at the very beginning as appending before it', function() {
-                    var c = canvas.fromXML('<section>Alice has a cat</section>'),
-                        section = c.doc(),
-                        text = section.children()[0];
-
-                    var returned = text.divide({tag: 'aside', klass: 'footnote', offset: 0}),
-                        sectionChildren = section.children(),
-                        nodeElement = sectionChildren[0],
-                        textElement = sectionChildren[1];
-                        
-                    expect(sectionChildren.length).to.equal(2);
-                    expect(textElement.getText()).to.equal('Alice has a cat');
-                    expect(returned.sameNode(nodeElement)).to.be.true;
-                    expect(nodeElement.getWlxmlTag()).to.equal('aside');
-                });
-            });
-
-            describe('Removing elements', function() {
-                it('merges left and right DocumentTextElement sibling of a detached DocumentNodeElement', function() {
-                    var c = canvas.fromXML('<section>Alice<div>has</div>a cat</section>'),
-                        section = c.doc(),
-                        div = section.children()[1];
-
-                    div.detach();
-
-                    var sectionChildren = section.children(),
-                        textElement = sectionChildren[0];
-
-                    expect(sectionChildren).to.have.length(1);
-                    expect(textElement.getText()).to.equal('Alicea cat');
-                });
-            });
-
-            describe('Splitting text', function() {
-                
-                it('splits DocumentTextElement\'s parent into two DocumentNodeElements of the same type', function() {
-                    var c = canvas.fromXML('<section><header>Some header</header></section>'),
-                        section = c.doc(),
-                        text = section.children()[0].children()[0];
-
-                    var returnedValue = text.split({offset: 5});
-                    expect(section.children().length).to.equal(2, 'section has two children');
-                    
-                    var header1 = section.children()[0];
-                    var header2 = section.children()[1];
-
-                    expect(header1.getWlxmlTag()).to.equal('header', 'first section child represents wlxml header');
-                    expect(header1.children().length).to.equal(1, 'first header has one text child');
-                    expect(header1.children()[0].getText()).to.equal('Some ', 'first header has correct content');
-                    expect(header2.getWlxmlTag()).to.equal('header', 'second section child represents wlxml header');
-                    expect(header2.children().length).to.equal(1, 'second header has one text child');
-                    expect(header2.children()[0].getText()).to.equal('header', 'second header has correct content');
-
-                    expect(returnedValue.first.sameNode(header1)).to.equal(true, 'first node returnde');
-                    expect(returnedValue.second.sameNode(header2)).to.equal(true, 'second node returned');
-                });
-
-                it('leaves empty copy of DocumentNodeElement if splitting at the very beginning', function() {
-                        var c = canvas.fromXML('<section><header>Some header</header></section>'),
-                        section = c.doc(),
-                        text = section.children()[0].children()[0];
-
-                        text.split({offset: 0});
-                        
-                        var header1 = section.children()[0];
-                        var header2 = section.children()[1];
-
-                        expect(header1.children().length).to.equal(0);
-                        expect(header2.children()[0].getText()).to.equal('Some header');
-                });
-
-                it('leaves empty copy of DocumentNodeElement if splitting at the very end', function() {
-                        var c = canvas.fromXML('<section><header>Some header</header></section>'),
-                        section = c.doc(),
-                        text = section.children()[0].children()[0];
-
-                        text.split({offset: 11});
-                        
-                        var header1 = section.children()[0];
-                        var header2 = section.children()[1];
-
-                        expect(header1.children()[0].getText()).to.equal('Some header');
-                        expect(header2.children().length).to.equal(0);
-                });
-
-                it('keeps DocumentTextElement\'s parent\'s children elements intact', function() {
-                    var c = canvas.fromXML('\
-                            <section>\
-                                <header>\
-                                    A <span>fancy</span> and <span>nice</span> header\
-                                </header>\
-                            </section>'),
-                        section = c.doc(),
-                        header = section.children()[0],
-                        textAnd = header.children()[2];
-
-                    textAnd.split({offset: 2});
-                    
-                    var sectionChildren = section.children();
-                    expect(sectionChildren.length).to.equal(2, 'Section has two children');
-                    expect(sectionChildren[0].getWlxmlTag()).to.equal('header', 'First section element is a wlxml header');
-                    expect(sectionChildren[1].getWlxmlTag()).to.equal('header', 'Second section element is a wlxml header');
-
-                    var firstHeaderChildren = sectionChildren[0].children();
-                    expect(firstHeaderChildren.length).to.equal(3, 'First header has three children');
-                    expect(firstHeaderChildren[0].getText()).to.equal('A ', 'First header starts with a text');
-                    expect(firstHeaderChildren[1].getWlxmlTag()).to.equal('span', 'First header has span in the middle');
-                    expect(firstHeaderChildren[2].getText()).to.equal(' a', 'First header ends with text');
-
-                    var secondHeaderChildren = sectionChildren[1].children();
-                    expect(secondHeaderChildren.length).to.equal(3, 'Second header has three children');
-                    expect(secondHeaderChildren[0].getText()).to.equal('nd ', 'Second header starts with text');
-                    expect(secondHeaderChildren[1].getWlxmlTag()).to.equal('span', 'Second header has span in the middle');
-                    expect(secondHeaderChildren[2].getText()).to.equal(' header', 'Second header ends with text');
-                });
-            });
-
-            describe('wrapping', function() {
-                it('wraps DocumentNodeElement', function() {
-                    var c = canvas.fromXML('<section><div></div></section>'),
-                        div = c.doc().children()[0];
-                    
-                    var returned = div.wrapWithNodeElement({tag: 'header', klass: 'some.class'}),
-                        parent = div.parent(),
-                        parent2 = c.doc().children()[0];
-
-                    expect(returned.sameNode(parent)).to.be.true;
-                    expect(returned.sameNode(parent2)).to.be.true;
-                    expect(returned.getWlxmlTag()).to.equal('header');
-                    expect(returned.getWlxmlClass()).to.equal('some.class');
-                });
-                it('wraps DocumentTextElement', function() {
-                    var c = canvas.fromXML('<section>Alice</section>'),
-                        text = c.doc().children()[0];
-                    
-                    var returned = text.wrapWithNodeElement({tag: 'header', klass: 'some.class'}),
-                        parent = text.parent(),
-                        parent2 = c.doc().children()[0];
-
-                    expect(returned.sameNode(parent)).to.be.true;
-                    expect(returned.sameNode(parent2)).to.be.true;
-                    expect(returned.getWlxmlTag()).to.equal('header');
-                    expect(returned.getWlxmlClass()).to.equal('some.class');
-                });
-                
-                describe('wrapping part of DocumentTextElement', function() {
-                    [{start: 5, end: 12}, {start: 12, end: 5}].forEach(function(offsets) {
-                        it('wraps in the middle ' + offsets.start + '/' + offsets.end, function() {
-                            var c = canvas.fromXML('<section>Alice has a cat</section>'),
-                                text = c.doc().children()[0];
-                            
-                            var returned = text.wrapWithNodeElement({tag: 'header', klass: 'some.class', start: offsets.start, end: offsets.end}),
-                                children = c.doc().children();
-
-                            expect(children.length).to.equal(3);
-                            
-                            expect(children[0]).to.be.instanceOf(documentElement.DocumentTextElement);
-                            expect(children[0].getText()).to.equal('Alice');
-
-                            expect(children[1].sameNode(returned)).to.be.true;
-                            expect(returned.getWlxmlTag()).to.equal('header');
-                            expect(returned.getWlxmlClass()).to.equal('some.class');
-                            expect(children[1].children().length).to.equal(1);
-                            expect(children[1].children()[0].getText()).to.equal(' has a ');
-
-                            expect(children[2]).to.be.instanceOf(documentElement.DocumentTextElement);
-                            expect(children[2].getText()).to.equal('cat');
-                        });
-                    });
-
-                    it('wraps whole text inside DocumentTextElement if offsets span entire content', function() {
-                         var c = canvas.fromXML('<section>Alice has a cat</section>'),
-                             text = c.doc().children()[0];
-                         
-                         var returned = text.wrapWithNodeElement({tag: 'header', klass: 'some.class', start: 0, end: 15}),
-                             children = c.doc().children();
-
-                         expect(children.length).to.equal(1);
-                         expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
-                         expect(children[0].children()[0].getText()).to.equal('Alice has a cat');
-                    });
-                });
-
-                it('wraps text spanning multiple sibling DocumentTextNodes', function() {
-                    var c = canvas.fromXML('<section>Alice has a <span>small</span> cat</section>'),
-                        section = c.doc(),
-                        wrapper = c.wrapText({
-                            inside: section, 
-                            _with: {tag: 'span', klass: 'some.class'},
-                            offsetStart: 6,
-                            offsetEnd: 4,
-                            textNodeIdx: [0,2]
-                        });
-
-                    expect(section.children().length).to.equal(2);
-                    expect(section.children()[0]).to.be.instanceOf(documentElement.DocumentTextElement);
-                    expect(section.children()[0].getText()).to.equal('Alice ');
-
-                    var wrapper2 = section.children()[1];
-                    expect(wrapper2.sameNode(wrapper)).to.be.true;
-
-                    var wrapperChildren = wrapper.children();
-                    expect(wrapperChildren.length).to.equal(3);
-                    expect(wrapperChildren[0].getText()).to.equal('has a ');
-
-                    expect(wrapperChildren[1]).to.be.instanceOf(documentElement.DocumentNodeElement);
-                    expect(wrapperChildren[1].children().length).to.equal(1);
-                    expect(wrapperChildren[1].children()[0].getText()).to.equal('small');
-
-                    expect(wrapperChildren[2].getText()).to.equal(' cat');
-                });
-
-                it('wraps multiple sibling Elements', function() {
-                    var c = canvas.fromXML('<section>Alice<div>has</div><div>a cat</div></section>'),
-                        section = c.doc(),
-                        aliceText = section.children()[0],
-                        firstDiv = section.children()[1],
-                        lastDiv = section.children()[section.children().length -1];
-
-                    var returned = c.wrapElements({
-                            element1: aliceText,
-                            element2: lastDiv,
-                            _with: {tag: 'header'}
-                        });
-
-                    var sectionChildren = section.children(),
-                        header = sectionChildren[0],
-                        headerChildren = header.children();
-
-                    expect(sectionChildren).to.have.length(1);
-                    expect(header.sameNode(returned)).to.equal(true, 'wrapper returned');
-                    expect(headerChildren).to.have.length(3);
-                    expect(headerChildren[0].sameNode(aliceText)).to.equal(true, 'first node wrapped');
-                    expect(headerChildren[1].sameNode(firstDiv)).to.equal(true, 'second node wrapped');
-                    expect(headerChildren[2].sameNode(lastDiv)).to.equal(true, 'third node wrapped');
-                });
-                it('wraps multiple sibling Elements - middle case', function() {
-                    var c = canvas.fromXML('<section><div></div>div></div><div></div><div></div></section>'),
-                        section = c.doc(),
-                        div1 = section.children()[0],
-                        div2 = section.children()[1],
-                        div3 = section.children()[2],
-                        div4 = section.children()[3];
-
-                    var returned = c.wrapElements({
-                            element1: div2,
-                            element2: div3,
-                            _with: {tag: 'header'}
-                        });
-
-                    var sectionChildren = section.children(),
-                        header = sectionChildren[1],
-                        headerChildren = header.children();
-
-                    expect(sectionChildren).to.have.length(3);
-                    expect(headerChildren).to.have.length(2);
-                    expect(headerChildren[0].sameNode(div2)).to.equal(true, 'first node wrapped');
-                    expect(headerChildren[1].sameNode(div3)).to.equal(true, 'second node wrapped');
-                });
-            });
-
-            describe('unwrapping DocumentTextElement from its parent DocumentNodeElement if it\'s its only child', function() {
-                it('unwraps text element from its parent and stays between its old parent siblings', function() {
-                    var c = canvas.fromXML('<section><div>Alice</div><div>has</div><div>a cat</div></section>'),
-                        section = c.doc(),
-                        sectionChildren = section.children(),
-                        divAlice = sectionChildren[0],
-                        divHas = sectionChildren[1],
-                        textHas = divHas.children()[0],
-                        divCat = sectionChildren[2];
-
-                    var newTextContainer = textHas.unwrap(),
-                        sectionChildren = section.children();
-
-                    expect(sectionChildren[0].sameNode(divAlice)).to.equal(true, 'divAlice ok');
-                    expect(newTextContainer.sameNode(section)).to.equal(true, 'unwrap returns new text parent DocumentNodeElement');
-                    expect(sectionChildren[1].getText()).to.equal('has');
-                    expect(sectionChildren[2].sameNode(divCat)).to.equal(true, 'divCat ok');
-
-                });
-                it('unwraps and join with its old parent adjacent text elements ', function() {
-                    var c = canvas.fromXML('<section>Alice <span>has a</span> cat</section>'),
-                    section = c.doc(),
-                    text = section.children()[1].children()[0];
-
-                    var newTextContainer = text.unwrap();
-
-                    expect(section.children().length).to.equal(1, 'section has one child');
-                    expect(section.children()[0].getText()).to.equal('Alice has a cat');
-                    expect(newTextContainer.sameNode(c.doc())).to.equal(true, 'unwrap returns new text parent DocumentNodeElement');
-                });
-
-                it('unwraps text element from its parent - first child case', function() {
-                    var c = canvas.fromXML('<section><span class="uri">Some</span>text</section>'),
-                        section = c.doc(),
-                        span = section.children()[0];
-
-                    span.children()[0].unwrap();
-
-                    var sectionChildren = section.children();
-
-                    expect(sectionChildren).to.have.length(1);
-                    expect(sectionChildren[0].getText()).to.equal('Sometext');
-                });
-            });
-
-            describe('unwrapping the whole content of a DocumentNodeElement', function() {
-                it('removes a DocumentNodeElement but keeps its content', function() {
-                    var c = canvas.fromXML('<section><div>Alice has<span>a</span> cat</div></section>'),
-                        section = c.doc(),
-                        div = c.doc().children()[0],
-                        span = div.children()[1];
-
-                    var range = div.unwrapContents(),
-                        sectionChildren = section.children();
-
-                    expect(sectionChildren).to.have.length(3);
-                    expect(sectionChildren[0].getText()).to.equal('Alice has');
-                    expect(sectionChildren[1].sameNode(span)).to.equal(true, 'span ok');
-                    expect(sectionChildren[2].getText()).to.equal(' cat');
-
-                    expect(range.element1.sameNode(sectionChildren[0])).to.equal(true, 'range start ok');
-                    expect(range.element2.sameNode(sectionChildren[2])).to.equal(true, 'range end ok');
-                });
-                it('merges text elements on the boundries', function() {
-                    var c = canvas.fromXML('<section>Alice<div>has a <span>cat</span>!</div>!!</section>'),
-                        section = c.doc(),
-                        div = c.doc().children()[1],
-                        span = div.children()[1];
-
-                    var range = div.unwrapContents(),
-                        sectionChildren = section.children();
-
-                    expect(sectionChildren).to.have.length(3);
-                    expect(sectionChildren[0].getText()).to.equal('Alicehas a ');
-                    expect(sectionChildren[1].sameNode(span)).to.equal(true, 'span ok');
-                    expect(sectionChildren[2].getText()).to.equal('!!!');
-
-                    expect(range.element1.sameNode(sectionChildren[0])).to.equal(true, 'range start ok');
-                    expect(range.element2.sameNode(sectionChildren[2])).to.equal(true, 'range end ok');
-                });
-
-                it('merges text elements on the boundries - single child case', function() {
-                    var c = canvas.fromXML('<section>Alice <span>has</span> a cat</section>'),
-                        section = c.doc(),
-                        span = section.children()[1];
-
-                    var range = span.unwrapContents(),
-                        sectionChildren = section.children();
-
-                    expect(sectionChildren).to.have.length(1);
-                    expect(sectionChildren[0].getText()).to.equal('Alice has a cat');
-                });
-            });
-            
-        });
-
-        describe('Lists api', function() {
-            describe('creating lists', function() {
-                it('allows creation of a list from existing sibling DocumentElements', function() {
-                    var c = canvas.fromXML('\
-                        <section>\
-                            Alice\
-                            <div>has</div>\
-                            a\
-                            <div>cat</div>\
-                        </section>'),
-                        section = c.doc(),
-                        textHas = section.children()[1],
-                        divA = section.children()[2]
-                    
-                    c.list.create({element1: textHas, element2: divA});
-
-                    expect(section.children().length).to.equal(3, 'section has three child elements');
-
-                    var child1 = section.children()[0],
-                        list = section.children()[1],
-                        child3 = section.children()[2];
-
-                    expect(child1.getText()).to.equal('Alice');
-                    expect(list.is('list')).to.equal(true, 'second child is a list');
-                    expect(list.children().length).to.equal(2, 'list contains two elements');
-                    list.children().forEach(function(child) {
-                        expect(child.getWlxmlClass()).to.equal('item', 'list childs have wlxml class of item');
-                    });
-                    expect(child3.children()[0].getText()).to.equal('cat');
-                });
-                
-                it('allows creating nested list from existing sibling list items', function() {
-                    var c = canvas.fromXML('\
-                        <section>\
-                            <div class="list-items">\
-                                <div class="item">A</div>\
-                                <div class="item">B</div>\
-                                <div class="item">C</div>\
-                                <div class="item">D</div>\
-                            </div>\
-                        </section>'),
-                        outerList = c.doc().children()[0],
-                        itemB = outerList.children()[1],
-                        itemC = outerList.children()[2];
-
-
-                        c.list.create({element1: itemB, element2: itemC});
-
-                    var outerListItems = outerList.children(),
-                        innerList = outerListItems[1].children()[0],
-                        innerListItems = innerList.children();
-
-                    expect(outerListItems.length).to.equal(3, 'outer list has three items');
-                    expect(outerListItems[0].children()[0].getText()).to.equal('A', 'first outer item ok');
-                    expect(outerListItems[1].getWlxmlClass()).to.equal('item', 'inner list is wrapped by item element');
-
-                    expect(innerList.is('list')).to.equal(true, 'inner list created');
-                    expect(innerListItems.length).to.equal(2, 'inner list has two items');
-                    expect(innerListItems[0].children()[0].getText()).to.equal('B', 'first inner item ok');
-                    expect(innerListItems[1].children()[0].getText()).to.equal('C', 'second inner item ok');
-
-                    expect(outerListItems[2].children()[0].getText()).to.equal('D', 'last outer item ok');
-
-                });
-
-            });
-
-            describe('extracting list items', function() {
-                it('creates two lists with extracted items in the middle if extracting from the middle of the list', function() {
-                    var c = canvas.fromXML('\
-                        <section>\
-                            <div class="list.items">\
-                                <div class="item">0</div>\
-                                <div class="item">1</div>\
-                                <div class="item">2</div>\
-                                <div class="item">3</div>\
-                            </div>\
-                        </section>'),
-                        list = c.doc().children()[0],
-                        item1 = list.children()[1],
-                        item2 = list.children()[2];
-
-                    c.list.extractItems({element1: item1, element2: item2});
-
-                    var section = c.doc(),
-                        list1 = section.children()[0],
-                        oldItem1 = section.children()[1],
-                        oldItem2 = section.children()[2],
-                        list2 = section.children()[3];
-
-                    expect(section.children().length).to.equal(4, 'section contains four children');
-                    
-                    expect(list1.is('list')).to.equal(true, 'first section child is a list');
-                    expect(list1.children().length).to.equal(1, 'first list has one child');
-                    expect(list1.children()[0].children()[0].getText()).to.equal('0', 'first item of the first list is a first item of the original list');
-
-                    expect(oldItem1.children()[0].getText()).to.equal('1', 'first item got extracted');
-                    expect(oldItem1.getWlxmlClass() === undefined).to.equal(true, 'first extracted element has no wlxml class');
-
-                    expect(oldItem2.children()[0].getText()).to.equal('2', 'second item got extracted');
-                    expect(oldItem2.getWlxmlClass() === undefined).to.equal(true, 'second extracted element has no wlxml class');
-
-                    expect(list2.is('list')).to.equal(true, 'last section child is a list');
-                    expect(list2.children().length).to.equal(1, 'second list has one child');
-                    expect(list2.children()[0].children()[0].getText()).to.equal('3', 'first item of the second list is a last item of the original list');
-                });
-
-                it('puts extracted items above the list if starting item is the first one', function() {
-                    var c = canvas.fromXML('\
-                        <section>\
-                            <div class="list.items">\
-                                <div class="item">0</div>\
-                                <div class="item">1</div>\
-                                <div class="item">2</div>\
-                            </div>\
-                        </section>'),
-                        list = c.doc().children()[0],
-                        item1 = list.children()[0],
-                        item2 = list.children()[1],
-                        item3 = list.children()[2];
-
-                    c.list.extractItems({element1: item1, element2: item2});
-
-                    var section = c.doc(),
-                        oldItem1 = section.children()[0],
-                        oldItem2 = section.children()[1],
-                        newList = section.children()[2];
-
-                    expect(section.children().length).to.equal(3, 'section has three children');
-                    expect(oldItem1.children()[0].getText()).to.equal('0', 'first item extracted');
-                    expect(oldItem2.children()[0].getText()).to.equal('1', 'second item extracted');
-                    expect(newList.is('list')).to.equal(true, 'list lies below extracted item');
-                    expect(newList.children().length).to.equal(1, 'list has now one child');
-                });
-
-                it('puts extracted items below the list if ending item is the last one', function() {
-                    var c = canvas.fromXML('\
-                        <section>\
-                            <div class="list.items">\
-                                <div class="item">0</div>\
-                                <div class="item">1</div>\
-                                <div class="item">2</div>\
-                            </div>\
-                        </section>'),
-                        list = c.doc().children()[0],
-                        item1 = list.children()[0],
-                        item2 = list.children()[1],
-                        item3 = list.children()[2];
-
-                    c.list.extractItems({element1: item2, element2: item3});
-
-                    var section = c.doc(),
-                        oldItem1 = section.children()[1],
-                        oldItem2 = section.children()[2],
-                        newList = section.children()[0];
-
-                    expect(section.children().length).to.equal(3, 'section has three children');
-                    expect(oldItem1.children()[0].getText()).to.equal('1', 'first item extracted');
-                    expect(oldItem2.children()[0].getText()).to.equal('2', 'second item extracted');
-                    expect(newList.is('list')).to.equal(true, 'list lies above extracted item');
-                    expect(newList.children().length).to.equal(1, 'list has now one child');
-                });
-
-                it('removes list if all its items are extracted', function() {
-                    var c = canvas.fromXML('\
-                        <section>\
-                            <div class="list.items">\
-                                <div class="item">some item</div>\
-                                <div class="item">some item 2</div>\
-                            </div>\
-                        </section>'),
-                        list = c.doc().children()[0],
-                        item1 = list.children()[0],
-                        item2 = list.children()[1];
-
-                    c.list.extractItems({element1: item1, element2: item2});
-
-                    var section = c.doc(),
-                        list1 = section.children()[0],
-                        oldItem1 = section.children()[0],
-                        oldItem2 = section.children()[1];
-
-                    expect(section.children().length).to.equal(2, 'section contains two children');
-                    expect(oldItem1.children()[0].getText()).to.equal('some item');
-                    expect(oldItem2.children()[0].getText()).to.equal('some item 2');
-                });
-
-                it('creates two lists with extracted items in the middle if extracting from the middle of the list - nested case' , function() {
-                    var c = canvas.fromXML('\
-                        <section>\
-                            <div class="list.items">\
-                                <div class="item">0</div>\
-                                <div class="item">\
-                                    <div class="list.items">\
-                                        <div class="item">1.1</div>\
-                                        <div class="item">1.2</div>\
-                                        <div class="item">1.3</div>\
-                                    </div>\
-                                </div>\
-                                <div class="item">2</div>\
-                            </div>\
-                        </section>'),
-                        list = c.doc().children()[0],
-                        nestedList = list.children()[1].children()[0],
-                        nestedListItem = nestedList.children()[1];
-
-                    c.list.extractItems({element1: nestedListItem, element2: nestedListItem});
-
-                    var section = c.doc(),
-                        list = section.children()[0],
-                        item1 = list.children()[0],
-                        item2 = list.children()[1], //
-                        item3 = list.children()[2],
-                        item4 = list.children()[3], //
-                        item5 = list.children()[4],
-                        nestedList1 = item2.children()[0],
-                        nestedList2 = item4.children()[0];
-
-                    expect(list.children().length).to.equal(5, 'top list has five items');
-                    
-                    expect(item1.children()[0].getText()).to.equal('0', 'first item ok');
-
-                    expect(item2.getWlxmlClass()).to.equal('item', 'first nested list is still wrapped in item element');
-                    expect(nestedList1.children().length).to.equal(1, 'first nested list is left with one child');
-                    expect(nestedList1.children()[0].children()[0].getText()).to.equal('1.1', 'first nested list item left alone');
-                    
-                    expect(item3.children()[0].getText()).to.equal('1.2', 'third item ok');
-
-                    expect(item4.getWlxmlClass()).to.equal('item', 'second nested list is still wrapped in item element');
-                    expect(nestedList2.children().length).to.equal(1, 'second nested list is left with one child');
-                    expect(nestedList2.children()[0].children()[0].getText()).to.equal('1.3', 'second nested list item left alone');
-
-                    expect(item5.children()[0].getText()).to.equal('2', 'last item ok');
-                });
-
-                it('puts extracted items below the list if ending item is the last one - nested case' , function() {
-                    var c = canvas.fromXML('\
-                        <section>\
-                            <div class="list.items">\
-                                <div class="item">0</div>\
-                                <div class="item">\
-                                    <div class="list.items">\
-                                        <div class="item">1.1</div>\
-                                        <div class="item">1.2</div>\
-                                        <div class="item">1.3</div>\
-                                    </div>\
-                                </div>\
-                                <div class="item">2</div>\
-                            </div>\
-                        </section>'),
-                        list = c.doc().children()[0],
-                        nestedList = list.children()[1].children()[0],
-                        nestedListItem1 = nestedList.children()[1],
-                        nestedListItem2 = nestedList.children()[2];
-
-                    c.list.extractItems({element1: nestedListItem1, element2: nestedListItem2});
-
-                    var section = c.doc(),
-                        list = section.children()[0],
-                        item1 = list.children()[0],
-                        item2 = list.children()[1],
-                        item3 = list.children()[2],
-                        item4 = list.children()[3],
-                        item5 = list.children()[4];
-                    nestedList = item2.children()[0];
-
-                    expect(list.children().length).to.equal(5, 'top list has five items');
-                    expect(item1.children()[0].getText()).to.equal('0', 'first item ok');
-                    expect(item2.getWlxmlClass()).to.equal('item', 'nested list is still wrapped in item element');
-                    expect(nestedList.children().length).to.equal(1, 'nested list is left with one child');
-                    expect(nestedList.children()[0].children()[0].getText()).to.equal('1.1', 'nested list item left alone');
-                    expect(item3.children()[0].getText()).to.equal('1.2', 'third item ok');
-                    expect(item4.children()[0].getText()).to.equal('1.3', 'fourth item ok');
-                    expect(item5.children()[0].getText()).to.equal('2', 'fifth item ok');
-                });
-
-                it('puts extracted items above the list if starting item is the first one - nested case' , function() {
-                    var c = canvas.fromXML('\
-                        <section>\
-                            <div class="list.items">\
-                                <div class="item">0</div>\
-                                <div class="item">\
-                                    <div class="list.items">\
-                                        <div class="item">1.1</div>\
-                                        <div class="item">1.2</div>\
-                                        <div class="item">1.3</div>\
-                                    </div>\
-                                </div>\
-                                <div class="item">2</div>\
-                            </div>\
-                        </section>'),
-                        list = c.doc().children()[0],
-                        nestedList = list.children()[1].children()[0],
-                        nestedListItem1 = nestedList.children()[0],
-                        nestedListItem2 = nestedList.children()[1];
-
-                    c.list.extractItems({element1: nestedListItem1, element2: nestedListItem2});
-
-                    var section = c.doc(),
-                        list = section.children()[0],
-                        item1 = list.children()[0],
-                        item2 = list.children()[1],
-                        item3 = list.children()[2],
-                        item4 = list.children()[3],
-                        item5 = list.children()[4];
-                    nestedList = item4.children()[0];
-
-                    expect(list.children().length).to.equal(5, 'top list has five items');
-                    expect(item1.children()[0].getText()).to.equal('0', 'first item ok');
-                    expect(item2.children()[0].getText()).to.equal('1.1', 'second item ok');
-                    expect(item3.children()[0].getText()).to.equal('1.2', 'third item ok');
-                    
-                    expect(item4.getWlxmlClass()).to.equal('item', 'nested list is still wrapped in item element');
-                    expect(nestedList.children().length).to.equal(1, 'nested list is left with one child');
-                    expect(nestedList.children()[0].children()[0].getText()).to.equal('1.3', 'nested list item left alone');
-                    expect(item5.children()[0].getText()).to.equal('2', 'fifth item ok');
-                });
-
-                it('removes list if all its items are extracted - nested case', function() {
-                    var c = canvas.fromXML('\
-                        <section>\
-                            <div class="list.items">\
-                                <div class="item">0</div>\
-                                <div class="item">\
-                                    <div class="list.items">\
-                                        <div class="item">1.1</div>\
-                                        <div class="item">1.2</div>\
-                                    </div>\
-                                </div>\
-                                <div class="item">2</div>\
-                            </div>\
-                        </section>'),
-                        list = c.doc().children()[0],
-                        nestedList = list.children()[1].children()[0],
-                        nestedListItem1 = nestedList.children()[0],
-                        nestedListItem2 = nestedList.children()[1];
-
-                    c.list.extractItems({element1: nestedListItem1, element2: nestedListItem2});
-
-                    var section = c.doc(),
-                        list = section.children()[0],
-                        item1 = list.children()[0],
-                        item2 = list.children()[1],
-                        item3 = list.children()[2],
-                        item4 = list.children()[3];
-
-                    expect(list.children().length).to.equal(4, 'top list has four items');
-                    expect(item1.children()[0].getText()).to.equal('0', 'first item ok');
-                    expect(item2.children()[0].getText()).to.equal('1.1', 'second item ok');
-                    expect(item3.children()[0].getText()).to.equal('1.2', 'third item ok');
-                    expect(item4.children()[0].getText()).to.equal('2', 'fourth item ok');
-                });
-
-                it('extracts items out of outer most list when merge flag is set to false', function() {
-                    var c = canvas.fromXML('\
-                        <section>\
-                            <div class="list.items">\
-                                <div class="item">0</div>\
-                                <div class="item">\
-                                    <div class="list.items">\
-                                        <div class="item">1.1</div>\
-                                        <div class="item">1.2</div>\
-                                    </div>\
-                                </div>\
-                                <div class="item">2</div>\
-                            </div>\
-                        </section>'),
-                        section = c.doc(),
-                        list = section.children()[0],
-                        nestedList = list.children()[1].children()[0],
-                        nestedListItem = nestedList.children()[0];
-
-                    var test = c.list.extractItems({element1: nestedListItem, element2: nestedListItem, merge: false});
-
-                    expect(test).to.equal(true, 'extraction status ok');
-
-                    var sectionChildren = section.children(),
-                        extractedItem = sectionChildren[1];
-
-                    expect(sectionChildren.length).to.equal(3, 'section has three children');
-                    expect(sectionChildren[0].is('list')).to.equal(true, 'first child is a list');
-
-                    expect(extractedItem.getWlxmlTag()).to.equal('div', 'extracted item is a wlxml div');
-                    expect(extractedItem.getWlxmlClass()).to.equal(undefined, 'extracted item has no wlxml class');
-                    expect(extractedItem.children()[0].getText()).to.equal('1.1', 'extracted item ok');
-                    expect(sectionChildren[2].is('list')).to.equal(true, 'second child is a list');
-                });
-            });
-        });
-
-    });
-
-    describe('Cursor', function() {
-
-        var getSelection;
-
-        var findTextNode = function(inside, text) {
-            var nodes = inside.find(':not(iframe)').addBack().contents().filter(function() {
-                return this.nodeType === Node.TEXT_NODE && this.data === text;
-            });
-            if(nodes.length)
-                return nodes[0];
-            return null;
-        }
-
-        beforeEach(function() {
-            getSelection = sinon.stub(window, 'getSelection');
-        });
-
-        afterEach(function() {
-            getSelection.restore();
-        });
-
-        it('returns position when browser selection collapsed', function() {
-            var c = canvas.fromXML('<section>Alice has a cat</section>'),
-                dom = c.doc().dom(),
-                text = findTextNode(dom, 'Alice has a cat');
-
-            expect(text.nodeType).to.equal(Node.TEXT_NODE, 'correct node selected');
-            expect($(text).text()).to.equal('Alice has a cat');
-
-            getSelection.returns({
-                anchorNode: text,
-                focusNode: text,
-                anchorOffset: 5,
-                focusOffset: 5,
-                isCollapsed: true
-            });
-            var cursor = c.getCursor(),
-                position = cursor.getPosition();
-
-            expect(cursor.isSelecting()).to.equal(false, 'cursor is not selecting anything');
-            expect(position.element.getText()).to.equal('Alice has a cat');
-            expect(position.offset).to.equal(5);
-            expect(position.offsetAtEnd).to.equal(false, 'offset is not at end');
-
-            getSelection.returns({
-                anchorNode: text,
-                focusNode: text,
-                anchorOffset: 15,
-                focusOffset: 15,
-                isCollapsed: true
-            });
-
-            expect(cursor.getPosition().offsetAtEnd).to.equal(true, 'offset at end');
-        });
-
-        it('returns boundries of selection when browser selection not collapsed', function() {
-            var c = canvas.fromXML('<section>Alice <span>has</span> a <span>big</span> cat</section>'),
-                dom = c.doc().dom(),
-                text = {
-                    alice: findTextNode(dom, 'Alice '),
-                    has: findTextNode(dom, 'has'),
-                    cat: findTextNode(dom, ' cat')
-                },
-                cursor = c.getCursor(),
-                aliceElement = c.getDocumentElement(text.alice),
-                catElement = c.getDocumentElement(text.cat);
-
-
-                [
-                    {focus: text.alice, focusOffset: 1, anchor: text.cat,   anchorOffset: 2, selectionAnchor: catElement},
-                    {focus: text.cat,   focusOffset: 2, anchor: text.alice, anchorOffset: 1, selectionAnchor: aliceElement}
-                ].forEach(function(s, idx) {
-                    getSelection.returns({isColapsed: false, anchorNode: s.anchor, anchorOffset: s.anchorOffset, focusNode: s.focus, focusOffset: s.focusOffset});
-
-                    var selectionStart = cursor.getSelectionStart(),
-                        selectionEnd = cursor.getSelectionEnd(),
-                        selectionAnchor = cursor.getSelectionAnchor();
-
-                    expect(cursor.isSelecting()).to.equal(true, 'cursor is selecting');
-                    expect(selectionStart.element.sameNode(aliceElement)).to.equal(true, '"Alice" is the start of the selection ' + idx);
-                    expect(selectionStart.offset).to.equal(1, '"Alice" offset ok' + idx);
-                    expect(selectionEnd.element.sameNode(catElement)).to.equal(true, '"Cat" is the start of the selection ' + idx);
-                    expect(selectionEnd.offset).to.equal(2, '"Cat" offset ok' + idx);
-                    expect(selectionAnchor.element.sameNode(s.selectionAnchor)).to.equal(true, 'anchor ok');
-                    expect(selectionAnchor.offset).to.equal(s.anchorOffset, 'anchor offset ok');
-                });
-        });
-
-        it('recognizes when browser selection boundries lies in sibling DocumentTextElements', function() {
-            var c = canvas.fromXML('<section>Alice <span>has</span> a <span>big</span> cat</section>'),
-                dom = c.doc().dom(),
-                text = {
-                    alice: findTextNode(dom, 'Alice '),
-                    has: findTextNode(dom, 'has'),
-                    a: findTextNode(dom, ' a '),
-                    big: findTextNode(dom, 'big'),
-                    cat: findTextNode(dom, ' cat'),
-                },
-                cursor = c.getCursor();
-
-            expect($(text.alice).text()).to.equal('Alice ');
-            expect($(text.has).text()).to.equal('has');
-            expect($(text.a).text()).to.equal(' a ');
-            expect($(text.big).text()).to.equal('big');
-            expect($(text.cat).text()).to.equal(' cat');
-
-            getSelection.returns({anchorNode: text.alice, focusNode: text.a});
-            expect(cursor.isSelectingSiblings()).to.equal(true, '"Alice" and "a" are children');
-
-            getSelection.returns({anchorNode: text.alice, focusNode: text.cat});
-            expect(cursor.isSelectingSiblings()).to.equal(true, '"Alice" and "cat" are children');
-
-            getSelection.returns({anchorNode: text.alice, focusNode: text.has});
-            expect(cursor.isSelectingSiblings()).to.equal(false, '"Alice" and "has" are not children');
-
-            getSelection.returns({anchorNode: text.has, focusNode: text.big});
-            expect(cursor.isSelectingSiblings()).to.equal(false, '"has" and "big" are not children');
-            
-        })
-    });
-
-    describe('Serializing document to WLXML', function() {
-        it('keeps document intact when no changes have been made', function() {
-            var xmlIn = '<section>Alice<div>has</div>a <span class="uri" meta-uri="http://cat.com">cat</span>!</section>',
-                c = canvas.fromXML(xmlIn),
-                xmlOut = c.toXML();
-
-            var parser = new DOMParser(),
-                input = parser.parseFromString(xmlIn, "application/xml").childNodes[0],
-                output = parser.parseFromString(xmlOut, "application/xml").childNodes[0];
-            
-            expect(input.isEqualNode(output)).to.be.true;
-        });
-
-        it('keeps arbitrary node attributes intact', function() {
-            var xmlIn = '<section a="1" xmlns:dcterms="http://purl.org/dc/terms/"></section>',
-                $xmlOut = $(canvas.fromXML(xmlIn).toXML());
-
-            expect($xmlOut.attr('a')).to.equal('1');
-            expect($xmlOut.attr('xmlns:dcterms')).to.equal('http://purl.org/dc/terms/');
-        });
-
-        it('doesn\' serialize meta attribute if its empty', function() {
-            var c;
-
-            c = canvas.fromXML('<section class="uri" meta-uri="some.uri"></section>');
-            c.doc().setWlxmlMetaAttr('uri', '');
-            expect($(c.toXML()).attr('meta-uri')).to.equal(undefined, 'overriding attribute with zero length string');
-
-            c = canvas.fromXML('<section class="uri"></section>');
-            c.doc().setWlxmlMetaAttr('uri', '');
-            expect($(c.toXML()).attr('meta-uri')).to.equal(undefined, 'setting attribute to zero length string');
-        });
-
-        describe('output xml', function() {
-            it('keeps entities intact', function() {
-                var xmlIn = '<section>&lt; &gt;</section>',
-                    c = canvas.fromXML(xmlIn),
-                    xmlOut = c.toXML();
-                expect(xmlOut).to.equal(xmlIn);
-            });
-            it('keeps entities intact when they form html/xml', function() {
-                var xmlIn = '<section>&lt;abc&gt;</section>',
-                    c = canvas.fromXML(xmlIn),
-                    xmlOut = c.toXML();
-                expect(xmlOut).to.equal(xmlIn);
-            });
-        });
-
-        describe('formatting output xml', function() {
-            /*it('keeps white spaces at the edges of input xml', function() {
-                var xmlIn = '  <section></section>  ',
-                c = canvas.fromXML(xmlIn),
-                xmlOut = c.toXML();
-
-                expect(xmlOut.substr(4)).to.equal('   <', 'start');
-                expect(xmlOut.substr(-2)).to.equal('>  ', 'end');
-            });*/
-            it('keeps white space between XML nodes', function() {
-                var xmlIn = '<section>\n\n\n<div></div>\n\n\n<div></div>\n\n\n</section>',
-                c = canvas.fromXML(xmlIn),
-                xmlOut = c.toXML();
-
-                var partsIn = xmlIn.split('\n\n\n'),
-                    partsOut = xmlOut.split('\n\n\n');
-                
-                expect(partsIn).to.deep.equal(partsOut);
-            });
-
-            it('keeps white space between XML nodes - inline case', function() {
-                var xmlIn = '<section>\n\n\n<span></span>\n\n\n<span></span>\n\n\n</section>',
-                c = canvas.fromXML(xmlIn);
-                
-                var xmlOut = c.toXML();
-
-                var partsIn = xmlIn.split('\n\n\n'),
-                    partsOut = xmlOut.split('\n\n\n');
-                
-                expect(partsIn).to.deep.equal(partsOut);
-            });
-
-            it('keeps white space at the beginning of text', function() {
-                var xmlIn = '<section>    abc<div>some div</div>    abc</section>',
-                    c = canvas.fromXML(xmlIn),
-                    xmlOut = c.toXML();
-
-                expect(xmlOut).to.equal(xmlIn);
-            });
-
-            it('nests new children block elements', function() {
-                var c = canvas.fromXML('<section></section>');
-    
-                c.doc().append({tag: 'header'});
-
-                var xmlOut = c.toXML();
-                expect(xmlOut.split('\n  ')[0]).to.equal('<section>', 'nesting start ok');
-                expect(xmlOut.split('\n').slice(-1)[0]).to.equal('</section>', 'nesting end ok');
-
-            });
-
-            it('doesn\'t nest new children inline elements', function() {
-                var c = canvas.fromXML('<section></section>');
-    
-                c.doc().append({tag: 'span'});
-
-                var xmlOut = c.toXML();
-                expect(xmlOut).to.equal('<section><span></span></section>');
-            });
-
-            it('keeps original white space at the end of text', function() {
-                
-                var xmlIn = '<header>    Some text ended with white space \
-                \
-                <span class="uri">Some text</span> some text\
-            \
-            </header>',
-                    c = canvas.fromXML(xmlIn);
-
-            var xmlOut = c.toXML();
-            console.log(xmlOut);
-            expect(xmlOut).to.equal(xmlIn);
-            });
-
-            it('keeps white space around text node', function() {
-                var xmlIn = '<section>\
-                <header>header1</header>\
-                Some text surrounded by white space\
-                <header>header2</header>\
-            </section>',
-                    c = canvas.fromXML(xmlIn);
-
-                var xmlOut = c.toXML();
-                expect(xmlOut).to.equal(xmlIn);
-            });
-
-            it('keeps white space around text node - last node case', function() {
-                var xmlIn = '<section>\
-                <header>header</header>\
-                    \
-                Some text surrounded by white space\
-                    \
-            </section>',
-                    c = canvas.fromXML(xmlIn);
-
-                var xmlOut = c.toXML();
-                expect(xmlOut).to.equal(xmlIn);
-            });
-
-            it('keeps white space after detaching text element', function() {
-                var xmlIn = '<section><header>header</header>\n\
-                    \n\
-                text1\n\
-                    \n\
-            </section>',
-                    expectedXmlOut = '<section><header>header</header>\n\
-                    \n\
-                \n\
-                    \n\
-            </section>',
-                    c = canvas.fromXML(xmlIn),
-                    children = c.doc().children(),
-                    text = children[children.length-1];
-                
-                expect(text.getText()).to.equal('text1');
-
-                text.detach();
-
-                var xmlOut = c.toXML();
-                expect(xmlOut).to.equal(expectedXmlOut);
-            });
-
-        })
-    })
-});
-
-
-});
\ No newline at end of file
diff --git a/modules/documentCanvas/canvas/documentElement.js b/modules/documentCanvas/canvas/documentElement.js
deleted file mode 100644 (file)
index 0627bc1..0000000
+++ /dev/null
@@ -1,669 +0,0 @@
-define([
-'libs/jquery',
-'libs/underscore',
-'modules/documentCanvas/classAttributes',
-'modules/documentCanvas/canvas/utils',
-'modules/documentCanvas/canvas/widgets',
-'modules/documentCanvas/canvas/wlxmlManagers'
-], function($, _, classAttributes, utils, widgets, wlxmlManagers) {
-    
-'use strict';
-
-
-// DocumentElement represents a text or an element node from WLXML document rendered inside Canvas
-var DocumentElement = function(htmlElement, canvas) {
-    if(arguments.length === 0)
-        return;
-    this.canvas = canvas;
-    this._setupDOMHandler(htmlElement);
-}
-
-var elementTypeFromParams = function(params) {
-    return params.text !== undefined ? DocumentTextElement : DocumentNodeElement;
-
-};
-
-$.extend(DocumentElement, {
-    create: function(params, canvas) {
-        return elementTypeFromParams(params).create(params);
-    },
-
-    createDOM: function(params) {
-        return elementTypeFromParams(params).createDOM(params);
-    },
-
-    fromHTMLElement: function(htmlElement, canvas) {
-        var $element = $(htmlElement);
-        if(htmlElement.nodeType === Node.ELEMENT_NODE && $element.attr('document-node-element') !== undefined)
-            return DocumentNodeElement.fromHTMLElement(htmlElement, canvas);
-        if($element.attr('document-text-element') !== undefined || (htmlElement.nodeType === Node.TEXT_NODE && $element.parent().attr('document-text-element') !== undefined))
-            return DocumentTextElement.fromHTMLElement(htmlElement, canvas);
-        return undefined;
-    }
-});
-
-$.extend(DocumentElement.prototype, {
-    _setupDOMHandler: function(htmlElement) {
-        this.$element = $(htmlElement);
-    },
-    bound: function() {
-        return $.contains(document.documentElement, this.dom()[0]);
-    },
-    dom: function() {
-        return this.$element;
-    },
-    parent: function() {
-        var parents = this.$element.parents('[document-node-element]');
-        if(parents.length)
-            return DocumentElement.fromHTMLElement(parents[0], this.canvas);
-        return null;
-    },
-
-    parents: function() {
-        var parents = [],
-            parent = this.parent();
-        while(parent) {
-            parents.push(parent);
-            parent = parent.parent();
-        }
-        return parents;
-    },
-
-    sameNode: function(other) {
-        return other && (typeof other === typeof this) && other.dom()[0] === this.dom()[0];
-    },
-
-    wrapWithNodeElement: function(wlxmlNode) {
-        var wrapper = DocumentNodeElement.create({tag: wlxmlNode.tag, klass: wlxmlNode.klass}, this);
-        this.dom().replaceWith(wrapper.dom());
-        wrapper.append(this);
-        return wrapper;
-    },
-
-    markAsCurrent: function() {
-        this.canvas.markAsCurrent(this);
-    },
-
-    getVerticallyFirstTextElement: function() {
-        var toret;
-        this.children().some(function(child) {
-            if(!child.isVisible())
-                return false; // continue
-            if(child instanceof DocumentTextElement) {
-                toret = child;
-                return true; // break
-            } else {
-                toret = child.getVerticallyFirstTextElement();
-                if(toret)
-                    return true; // break
-            }
-        });
-        return toret;
-    },
-
-    getPreviousTextElement: function(includeInvisible) {
-        return this.getNearestTextElement('above', includeInvisible);
-    },
-
-    getNextTextElement: function(includeInvisible) {
-        return this.getNearestTextElement('below', includeInvisible);
-    },
-
-    getNearestTextElement: function(direction, includeInvisible) {
-        includeInvisible = includeInvisible !== undefined ? includeInvisible : false;
-        var selector = '[document-text-element]' + (includeInvisible ? '' : ':visible');
-        return this.canvas.getDocumentElement(utils.nearestInDocumentOrder(selector, direction, this.dom()[0]));
-    },
-
-    isVisible: function() {
-        return this instanceof DocumentTextElement || this.getWlxmlTag() !== 'metadata';
-    },
-
-    isInsideList: function() {
-        return this.parents().some(function(parent) {
-            return parent.is('list');
-        });
-    },
-
-    exec: function(method) {
-        var manager = this.data('_wlxmlManager');
-        if(manager[method])
-            return manager[method].apply(manager, Array.prototype.slice.call(arguments, 1));
-    }
-});
-
-
-// DocumentNodeElement represents an element node from WLXML document rendered inside Canvas
-var DocumentNodeElement = function(htmlElement, canvas) {
-    DocumentElement.call(this, htmlElement, canvas);
-};
-
-$.extend(DocumentNodeElement, {
-    createDOM: function(params, canvas) {
-        var dom = $('<div>')
-                .attr('document-node-element', ''),
-            widgetsContainer = $('<div>')
-                .addClass('canvas-widgets')
-                .attr('contenteditable', false),
-            container = $('<div>')
-                .attr('document-element-content', '');
-        
-        dom.append(widgetsContainer, container);
-        // Make sure widgets aren't navigable with arrow keys
-        widgetsContainer.find('*').add(widgetsContainer).attr('tabindex', -1);
-
-        var element = this.fromHTMLElement(dom[0], canvas);
-
-        element.setWlxml({tag: params.tag, klass: params.klass});
-        if(params.meta) {
-            _.keys(params.meta).forEach(function(key) {
-                element.setWlxmlMetaAttr(key, params.meta[key]);
-            });
-        }
-        element.data('other-attrs', params.others);
-
-        if(params.rawChildren && params.rawChildren.length) {
-            container.append(params.rawChildren);
-        } else if(params.prepopulateOnEmpty) {
-            element.append(DocumentTextElement.create({text: ''}));
-        }
-        return dom;
-    },
-
-    create: function(params, canvas) {
-        return this.fromHTMLElement(this.createDOM(params, canvas)[0], canvas);
-    },
-
-    fromHTMLElement: function(htmlElement, canvas) {
-        return new this(htmlElement, canvas);
-    }
-});
-
-var manipulate = function(e, params, action) {
-    var element;
-    if(params instanceof DocumentElement) {
-        element = params;
-    } else {
-        element = DocumentElement.create(params);
-    }
-    var target = (action === 'append' || action === 'prepend') ? e._container() : e.dom();
-    target[action](element.dom());
-    return element;
-};
-
-DocumentNodeElement.prototype = new DocumentElement();
-
-
-$.extend(DocumentNodeElement.prototype, {
-    _container: function() {
-        return this.dom().children('[document-element-content]');
-    },
-    detach: function() {
-        var parent = this.parent();
-        if(!parent)
-            return;
-
-        var parentChildren = parent.children(),
-            myIdx = parent.childIndex(this);
-
-        if(myIdx > 0 && myIdx < parentChildren.length) {
-            if((parentChildren[myIdx-1] instanceof DocumentTextElement) && (parentChildren[myIdx+1] instanceof DocumentTextElement)) {
-                parentChildren[myIdx-1].appendText(parentChildren[myIdx+1].getText());
-                parentChildren[myIdx+1].detach();
-            }
-        }
-        this.dom().detach();
-        this.canvas = null;
-        return this;
-    },
-    unwrapContents: function() {
-        var parent = this.parent();
-        if(!parent)
-            return;
-
-        var parentChildren = parent.children(),
-            myChildren = this.children(),
-            myIdx = parent.childIndex(this);
-
-        if(myChildren.length === 0)
-            return this.detach();
-
-        var moveLeftRange, moveRightRange, leftMerged;
-
-        if(myIdx > 0 && (parentChildren[myIdx-1] instanceof DocumentTextElement) && (myChildren[0] instanceof DocumentTextElement)) {
-            parentChildren[myIdx-1].appendText(myChildren[0].getText());
-            myChildren[0].detach();
-            moveLeftRange = true;
-            leftMerged = true;
-        } else {
-            leftMerged = false;
-        }
-
-        if(!(leftMerged && myChildren.length === 1)) {
-            if(myIdx < parentChildren.length - 1 && (parentChildren[myIdx+1] instanceof DocumentTextElement) && (myChildren[myChildren.length-1] instanceof DocumentTextElement)) {
-                parentChildren[myIdx+1].prependText(myChildren[myChildren.length-1].getText());
-                myChildren[myChildren.length-1].detach();
-                moveRightRange = true;
-            }
-        }
-
-        var childrenLength = this.children().length;
-        this.children().forEach(function(child) {
-            this.before(child);
-        }.bind(this));
-
-        this.detach();
-
-        return {
-            element1: parent.children()[myIdx + (moveLeftRange ? -1 : 0)],
-            element2: parent.children()[myIdx + childrenLength-1 + (moveRightRange ? 1 : 0)]
-        };
-    },
-    data: function() {
-        var dom = this.dom(),
-            args = Array.prototype.slice.call(arguments, 0);
-        if(args.length === 2 && args[1] === undefined)
-            return dom.removeData(args[0]);
-        return dom.data.apply(dom, arguments);
-    },
-    toXML: function(level) {
-        var node = $('<' + this.getWlxmlTag() + '>');
-
-        if(this.getWlxmlClass())
-            node.attr('class', this.getWlxmlClass());
-        var meta = this.getWlxmlMetaAttrs();
-        meta.forEach(function(attr) {
-            if(attr.value)
-                node.attr('meta-' + attr.name, attr.value);
-        });
-        _.keys(this.data('other-attrs') || {}).forEach(function(key) {
-            node.attr(key, this.data('other-attrs')[key]);
-        }, this);
-
-        var addFormatting = function() {
-            var toret = $('<div>');
-            var formattings = {};
-
-            if(this.data('orig-before') !== undefined) {
-                if(this.data('orig-before')) {
-                    toret.prepend(document.createTextNode(this.data('orig-before')));
-                }
-            } else if(level && this.getWlxmlTag() !== 'span') {
-                toret.append('\n' + (new Array(level * 2 + 1)).join(' '));
-            }
-
-            toret.append(node);
-
-            if(this.data('orig-after')) {
-                toret.append(document.createTextNode(this.data('orig-after')));
-            }
-
-            /* Inside node */
-            if(this.data('orig-begin')) {
-                node.prepend(this.data('orig-begin'));
-                formattings.begin = true;
-            }
-
-            if(this.data('orig-end') !== undefined) {
-                if(this.data('orig-end')) {
-                    node.append(this.data('orig-end'));
-                }
-            } else if(this.getWlxmlTag() !== 'span' && children.length){
-                node.append('\n' + (new Array(level * 2 + 1)).join(' '));
-            }
-           
-            return {parts: toret.contents(), formattings: formattings};
-        }.bind(this);
-
-        
-        
-        var children = this.children(),
-            childParts;
-
-        var formatting = addFormatting(node);
-
-        for(var i = children.length - 1; i >= 0; i--) {
-            childParts = children[i].toXML(level + 1);
-            if(typeof childParts === 'string')
-                childParts = [document.createTextNode(childParts)];
-
-            if(formatting.formattings.begin) {
-                $(node.contents()[0]).after(childParts);
-            } else
-                node.prepend(childParts);
-        }
-        return formatting.parts;
-    },
-    append: function(params) {
-        if(params.tag !== 'span')
-            this.data('orig-end', undefined);
-        return manipulate(this, params, 'append');
-    },
-    prepend: function(params) {
-        return manipulate(this, params, 'prepend');
-    },
-    before: function(params) {
-        return manipulate(this, params, 'before');
-
-    },
-    after: function(params) {
-        return manipulate(this, params, 'after');
-    },
-    children: function() {
-        var toret = [];
-        if(this instanceof DocumentTextElement)
-            return toret;
-
-
-        var elementContent = this._container().contents();
-        var element = this;
-        elementContent.each(function(idx) {
-            var childElement = DocumentElement.fromHTMLElement(this, element.canvas);
-            if(childElement === undefined)
-                return true;
-            if(idx === 0 && elementContent.length > 1 && elementContent[1].nodeType === Node.ELEMENT_NODE && (childElement instanceof DocumentTextElement) && $.trim($(this).text()) === '')
-                return true;
-            if(idx > 0 && childElement instanceof DocumentTextElement) {
-                if(toret[toret.length-1] instanceof DocumentNodeElement && $.trim($(this).text()) === '')
-                    return true;
-            }
-            toret.push(childElement);
-        });
-        return toret;
-    },
-    childIndex: function(child) {
-        var children = this.children(),
-            toret = null;
-        children.forEach(function(c, idx) {
-            if(c.sameNode(child)) {
-                toret = idx;
-                return false;
-            }
-        });
-        return toret;
-    },
-    getWlxmlTag: function() {
-        return this._container().attr('wlxml-tag');
-    },
-    setWlxmlTag: function(tag) {
-        if(tag === this.getWlxmlTag())
-            return;
-
-        this._container().attr('wlxml-tag', tag);
-        if(!this.__updatingWlxml)
-            this._updateWlxmlManager();
-    },
-    getWlxmlClass: function() {
-        var klass = this._container().attr('wlxml-class');
-        if(klass)
-            return klass.replace(/-/g, '.');
-        return undefined;
-    },
-    setWlxmlClass: function(klass) {
-        if(klass === this.getWlxmlClass())
-            return;
-
-        this.getWlxmlMetaAttrs().forEach(function(attr) {
-            if(!classAttributes.hasMetaAttr(klass, attr.name))
-                this.dom().removeAttr('wlxml-meta-' + attr.name);
-        }, this);
-
-        if(klass)
-            this._container().attr('wlxml-class', klass.replace(/\./g, '-'));
-        else
-            this._container().removeAttr('wlxml-class');
-        if(!this.__updatingWlxml)
-            this._updateWlxmlManager();
-    },
-    setWlxml: function(params) {
-        this.__updatingWlxml = true;
-        if(params.tag !== undefined)
-            this.setWlxmlTag(params.tag);
-        if(params.klass !== undefined)
-            this.setWlxmlClass(params.klass);
-        this._updateWlxmlManager();
-        this.__updatingWlxml = false;
-    },
-    _updateWlxmlManager: function() {
-        var manager = wlxmlManagers.getFor(this);
-        this.data('_wlxmlManager', manager);
-        manager.setup();
-    },
-    is: function(what) {
-        if(what === 'list' && _.contains(['list.items', 'list.items.enum'], this.getWlxmlClass()))
-            return true;
-        return false;
-    },
-
-    getWlxmlMetaAttr: function(attr) {
-        return this.dom().attr('wlxml-meta-'+attr);
-    },
-
-    getWlxmlMetaAttrs: function() {
-        var toret = [];
-        var attrList = classAttributes.getMetaAttrsList(this.getWlxmlClass());
-        attrList.all.forEach(function(attr) {
-            toret.push({name: attr.name, value: this.getWlxmlMetaAttr(attr.name) || ''});
-        }, this);
-        return toret;
-    },
-
-    setWlxmlMetaAttr: function(attr, value) {
-        this.dom().attr('wlxml-meta-'+attr, value);
-    },
-    
-    toggleLabel: function(toggle) {
-        var displayCss = toggle ? 'inline-block' : 'none';
-        var label = this.dom().children('.canvas-widgets').find('.canvas-widget-label');
-        label.css('display', displayCss);
-        this.toggleHighlight(toggle);
-    },
-
-    toggleHighlight: function(toggle) {
-        this._container().toggleClass('highlighted-element', toggle);
-    },
-
-    toggle: function(toggle) {
-        var mng = this.data('_wlxmlManager');
-        if(mng) {
-            mng.toggle(toggle);
-        }
-    }
-});
-
-
-// DocumentNodeElement represents a text node from WLXML document rendered inside Canvas
-var DocumentTextElement = function(htmlElement, canvas) {
-    DocumentElement.call(this, htmlElement, canvas);
-};
-
-$.extend(DocumentTextElement, {
-    createDOM: function(params) {
-        return $('<div>')
-            .attr('document-text-element', '')
-            .text(params.text || utils.unicode.ZWS);
-    },
-
-    create: function(params, canvas) {
-        return this.fromHTMLElement(this.createDOM(params)[0]);
-    },
-
-    fromHTMLElement: function(htmlElement, canvas) {
-        return new this(htmlElement, canvas);
-    },
-    isContentContainer: function(htmlElement) {
-        return htmlElement.nodeType === Node.TEXT_NODE && $(htmlElement).parent().is('[document-text-element]');
-    }
-});
-
-DocumentTextElement.prototype = new DocumentElement();
-
-$.extend(DocumentTextElement.prototype, {
-    toXML: function(parent) {
-        return this.getText();
-    },
-    _setupDOMHandler: function(htmlElement) {
-        var $element = $(htmlElement);
-        if(htmlElement.nodeType === Node.TEXT_NODE)
-            this.$element = $element.parent();
-        else
-            this.$element = $element;
-    },
-    detach: function() {
-        this.dom().detach();
-        this.canvas = null;
-        return this;
-    },
-    setText: function(text) {
-        this.dom().contents()[0].data = text;
-    },
-    appendText: function(text) {
-        this.dom().contents()[0].data += text;
-    },
-    prependText: function(text) {
-        this.dom().contents()[0].data = text + this.dom().contents()[0].data;
-    },
-    getText: function() {
-        return this.dom().text().replace(utils.unicode.ZWS, '');
-    },
-    isEmpty: function() {
-        // Having at least Zero Width Space is guaranteed be Content Observer
-        return this.dom().contents()[0].data === utils.unicode.ZWS;
-    },
-    after: function(params) {
-        if(params instanceof DocumentTextElement || params.text)
-            return false;
-        var element;
-        if(params instanceof DocumentNodeElement) {
-            element = params;
-        } else {
-            element = DocumentNodeElement.create(params, this.canvas);
-        }
-        this.dom().wrap('<div>');
-        this.dom().parent().after(element.dom());
-        this.dom().unwrap();
-        return element;
-    },
-    before: function(params) {
-        if(params instanceof DocumentTextElement || params.text)
-            return false;
-        var element;
-        if(params instanceof DocumentNodeElement) {
-            element = params;
-        } else {
-            element = DocumentNodeElement.create(params, this.canvas);
-        }
-        this.dom().wrap('<div>');
-        this.dom().parent().before(element.dom());
-        this.dom().unwrap();
-        return element;
-    },
-    wrapWithNodeElement: function(wlxmlNode) {
-        if(typeof wlxmlNode.start === 'number' && typeof wlxmlNode.end === 'number') {
-            return this.canvas.wrapText({
-                inside: this.parent(),
-                textNodeIdx: this.parent().childIndex(this),
-                offsetStart: Math.min(wlxmlNode.start, wlxmlNode.end),
-                offsetEnd: Math.max(wlxmlNode.start, wlxmlNode.end),
-                _with: {tag: wlxmlNode.tag, klass: wlxmlNode.klass}
-            });
-        } else {
-            return DocumentElement.prototype.wrapWithNodeElement.call(this, wlxmlNode);
-        }
-    },
-    unwrap: function() {
-        var parent = this.parent(),
-            toret;
-        if(parent.children().length === 1) {
-            toret = parent.parent();
-            var grandParent = parent.parent();
-            if(grandParent) {
-                var grandParentChildren = grandParent.children(),
-                    idx = grandParent.childIndex(parent),
-                    prev = idx - 1 > -1 ? grandParentChildren[idx-1] : null,
-                    next = idx + 1 < grandParentChildren.length ? grandParentChildren[idx+1] : null;
-                
-                prev = (prev instanceof DocumentTextElement) ? prev : null;
-                next = (next instanceof DocumentTextElement) ? next : null;
-
-                if(prev && next) {
-                    prev.setText(prev.getText() + this.getText() + next.getText());
-                    next.detach();
-                } else if (prev || next) {
-                    var target = prev ? prev : next,
-                        newText = prev ? target.getText() + this.getText() : this.getText() + target.getText();
-                    target.setText(newText);
-                } else {
-                    parent.after(this);
-                }
-            } else {
-                parent.after(this);
-            }
-            parent.detach();
-            return toret;
-        }
-    },
-    split: function(params) {
-        var parentElement = this.parent(),
-            myIdx = parentElement.childIndex(this),
-            myCanvas = this.canvas,
-            passed = false,
-            succeedingChildren = [],
-            thisElement = this,
-            prefix = this.getText().substr(0, params.offset),
-            suffix = this.getText().substr(params.offset);
-
-        parentElement.children().forEach(function(child) {
-            if(passed)
-                succeedingChildren.push(child);
-            if(child.sameNode(thisElement))
-                passed = true;
-        });
-
-        if(prefix.length > 0)
-            this.setText(prefix);
-        else
-            this.detach();
-        
-        var newElement = DocumentNodeElement.create({tag: parentElement.getWlxmlTag(), klass: parentElement.getWlxmlClass()}, myCanvas);
-        parentElement.after(newElement);
-
-        if(suffix.length > 0)
-            newElement.append({text: suffix});
-        succeedingChildren.forEach(function(child) {
-            newElement.append(child);
-        });
-
-        return {first: parentElement, second: newElement};
-    },
-    divide: function(params) {
-        var myText = this.getText();
-
-        if(params.offset === myText.length)
-            return this.after(params);
-        if(params.offset === 0)
-            return this.before(params);
-
-        var lhsText = myText.substr(0, params.offset),
-            rhsText = myText.substr(params.offset),
-            newElement = DocumentNodeElement.create({tag: params.tag, klass: params.klass}, this.canvas),
-            rhsTextElement = DocumentTextElement.create({text: rhsText});
-
-        this.setText(lhsText);
-        this.after(newElement);
-        newElement.after(rhsTextElement);
-        return newElement;
-    },
-
-    toggleHighlight: function() {
-        // do nothing for now
-    }
-});
-
-return {
-    DocumentElement: DocumentElement,
-    DocumentNodeElement: DocumentNodeElement,
-    DocumentTextElement: DocumentTextElement
-};
-
-});
\ No newline at end of file
diff --git a/modules/documentCanvas/canvas/keyboard.js b/modules/documentCanvas/canvas/keyboard.js
deleted file mode 100644 (file)
index 1830017..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-define([
-'modules/documentCanvas/canvas/documentElement',
-'modules/documentCanvas/canvas/utils'
-], function(documentElement, utils) {
-    
-'use strict';
-
-
-var KEYS = {
-    ENTER: 13,
-    ARROW_LEFT: 37,
-    ARROW_UP: 38,
-    ARROW_RIGHT: 39,
-    ARROW_DOWN: 40,
-    BACKSPACE: 8,
-    DELETE: 46,
-    X: 88
-};
-
-var handleKey = function(event, canvas) {
-    handlers.some(function(handler) {
-        if(handles(handler, event) && handler[event.type]) {
-            handler[event.type](event, canvas);
-            return true;
-        }
-    });
-};
-
-var handles = function(handler, event) {
-    if(handler.key === event.which)
-        return true;
-    if(handler.keys && handler.keys.indexOf(event.which) !== -1)
-        return true;
-    return false;
-};
-
-var handlers = [];
-
-
-handlers.push({key: KEYS.ENTER,
-    keydown: function(event, canvas) {
-        event.preventDefault();
-        var cursor = canvas.getCursor(),
-            position = cursor.getPosition(),
-            element = position.element;
-
-        if(!cursor.isSelecting()) {
-            if(event.ctrlKey) {
-                var added = element.after({tag: 'block'});
-                added.append({text:''});
-                canvas.setCurrentElement(added, {caretTo: 'start'});
-
-            } else {
-
-                if(!(element.parent().parent())) {
-                    return false; // top level element is unsplittable
-                }
-
-                var elements = position.element.split({offset: position.offset}),
-                    newEmpty,
-                    goto,
-                    gotoOptions;
-
-                if(position.offsetAtBeginning)
-                    newEmpty = elements.first;
-                else if(position.offsetAtEnd)
-                    newEmpty = elements.second;
-                
-                if(newEmpty) {
-                    goto = newEmpty.append(documentElement.DocumentTextElement.create({text: ''}, this));
-                    gotoOptions = {};
-                } else {
-                    goto = elements.second;
-                    gotoOptions = {caretTo: 'start'};
-                }
-
-                canvas.setCurrentElement(goto, gotoOptions);
-            }
-        }
-    }
-});
-
-handlers.push({keys: [KEYS.ARROW_UP, KEYS.ARROW_DOWN, KEYS.ARROW_LEFT, KEYS.ARROW_RIGHT],
-    keydown: function(event, canvas) {
-        var position = canvas.getCursor().getPosition(),
-            element = position.element;
-        if(element && (element instanceof documentElement.DocumentTextElement) && element.isEmpty()) {
-            var direction, caretTo;
-            if(event.which === KEYS.ARROW_LEFT  || event.which === KEYS.ARROW_UP) {
-                direction = 'above';
-                caretTo = 'end';
-            } else {
-                direction = 'below';
-                caretTo = 'start';
-            }
-            var el = canvas.getDocumentElement(utils.nearestInDocumentOrder('[document-text-element]', direction, element.dom()[0]));
-            canvas.setCurrentElement(el, {caretTo: caretTo});
-        }
-    },
-    keyup: function(event, canvas) {
-        var element = canvas.getCursor().getPosition().element,
-            caretTo = false;
-        if(!element) {
-            // Chrome hack
-            var direction;
-            if(event.which === KEYS.ARROW_LEFT  || event.which === KEYS.ARROW_UP) {
-                direction = 'above';
-                caretTo = 'end';
-            } else {
-                direction = 'below';
-                caretTo = 'start';
-            }
-            element = canvas.getDocumentElement(utils.nearestInDocumentOrder('[document-text-element]:visible', direction, window.getSelection().focusNode));
-        }
-        canvas.setCurrentElement(element, {caretTo: caretTo});
-    }
-});
-
-
-var selectsWholeTextElement = function(cursor) {
-    if(cursor.isSelecting() && cursor.getSelectionStart().offsetAtBeginning && cursor.getSelectionEnd().offsetAtEnd)
-        return true;
-    return false;
-}
-
-handlers.push({key: KEYS.X,
-    keydown: function(event, canvas) {
-        if(event.ctrlKey && selectsWholeTextElement(canvas.getCursor()))
-            event.preventDefault();
-    }
-});
-
-handlers.push({keys: [KEYS.BACKSPACE, KEYS.DELETE],
-    keydown: function(event, canvas) {
-        var cursor = canvas.getCursor(),
-            position = canvas.getCursor().getPosition(),
-            element = position.element;
-
-        if(cursor.isSelecting() && !cursor.isSelectingWithinElement()) {
-            event.preventDefault();
-            return;
-        }
-            
-        var cursorAtOperationEdge = position.offsetAtBeginning;
-        if(event.which === KEYS.DELETE) {
-            cursorAtOperationEdge = position.offsetAtEnd;
-        }
-
-        var willDeleteWholeText = function() {
-            return element.getText().length === 1 || selectsWholeTextElement(cursor);
-        }
-
-        if(willDeleteWholeText()) {
-            event.preventDefault();
-            element.setText('');
-        }
-        else if(element.isEmpty()) {
-
-            var direction = 'above',
-                caretTo = 'end';
-                
-            if(event.which === KEYS.DELETE) {
-                direction = 'below';
-                caretTo = 'start';
-            }
-
-            event.preventDefault();
-
-            var parent = element.parent(),
-                grandParent = parent ? parent.parent() : null,
-                goto;
-            if(parent.children().length === 1 && parent.children()[0].sameNode(element)) {
-                if(grandParent && grandParent.children().length === 1) {
-                    goto = grandParent.append({text: ''});
-                } else {
-                    goto = element.getNearestTextElement(direction);
-                }
-                parent.detach();
-            } else {
-                goto = element.getNearestTextElement(direction);
-                element.detach();
-            }
-            canvas.setCurrentElement(goto, {caretTo: caretTo});
-            canvas.publisher('contentChanged');
-        }
-        else if(cursorAtOperationEdge) {
-            // todo
-            event.preventDefault();
-        }
-    }
-});
-
-return {
-    handleKey: handleKey
-};
-
-});
\ No newline at end of file
diff --git a/modules/documentCanvas/canvas/utils.js b/modules/documentCanvas/canvas/utils.js
deleted file mode 100644 (file)
index 0eb19d0..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-define([
-'libs/jquery',
-], function($) {
-    
-'use strict';
-
-
-var nearestInDocumentOrder = function(selector, direction, element) {
-    var parents = $(element).parents(),
-        parent = parents.length ? $(parents[parents.length-1]) : element;
-
-    var adj = parent.find(selector).filter(function() {
-        return this.compareDocumentPosition(element) & (direction === 'above' ? Node.DOCUMENT_POSITION_FOLLOWING : Node.DOCUMENT_POSITION_PRECEDING);
-    });
-
-    if(adj.length) {
-        return adj[direction === 'above' ? adj.length-1 : 0];
-    }
-    return null;
-}
-
-return {
-    nearestInDocumentOrder: nearestInDocumentOrder,
-    unicode: {
-        ZWS: '\u200B'
-    }
-};
-
-});
diff --git a/modules/documentCanvas/canvas/utils.test.js b/modules/documentCanvas/canvas/utils.test.js
deleted file mode 100644 (file)
index c93e74b..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-define([
-'libs/chai',
-'modules/documentCanvas/canvas/utils'
-
-], function(chai, utils) {
-
-'use strict';
-
-var expect = chai.expect;
-
-
-describe('utils.nearestInDocumentOrder', function() {
-
-
-    var tests = [
-        ['return null if no match found',
-            '<span>\
-                <span></span>\
-                <div id="b">\
-                <span></span>\
-            </span>'
-        ],
-        ['returns nearest sibling if applicable',
-            '<div n1>\
-                <div n2></div>\
-                <div n3>\
-                    <div id="a"></div>\
-                    <div id="b"></div>\
-                    <div id="c"></div>\
-                </div>\
-                <div n4></div>\
-            </div>'
-        ],
-        ['looks inside siblings children',
-            '<div>\
-                <div></div>\
-                <div>\
-                    <div></div>\
-                    <span>\
-                        <div id="a"></div>\
-                    </span>\
-                    <div id="b"></div>\
-                    <span>\
-                        <div id="c"></div>\
-                    </span>\
-                    <div></div>\
-                </div>\
-                <div></div>\
-            </div>'
-        ]
-
-
-    ];
-
-    tests.forEach(function(test) {
-        var description = test[0],
-            html = test[1];
-        it(description, function() {
-            var dom = $(html),
-                a = dom.find('#a').length ? dom.find('#a')[0] : null,
-                b = dom.find('#b')[0],
-                c = dom.find('#c').length ? dom.find('#c')[0] : null;
-            expect(utils.nearestInDocumentOrder('div', 'above', b)).to.equal(a, 'above');
-            expect(utils.nearestInDocumentOrder('div', 'below', b)).to.equal(c, 'below');
-        });
-    });
-
-});
-
-});
diff --git a/modules/documentCanvas/canvas/widgets.js b/modules/documentCanvas/canvas/widgets.js
deleted file mode 100644 (file)
index 408d2c0..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-define([
-'libs/jquery',
-'utils/wlxml'
-], function($, wlxmlUtils) {
-    
-'use strict';
-
-return {
-    labelWidget: function(tag, klass) {
-        return $('<span>')
-            .addClass('canvas-widget canvas-widget-label')
-            .text(wlxmlUtils.wlxmlTagNames[tag] + (klass ? ' / ' + wlxmlUtils.wlxmlClassNames[klass] : ''));
-    },
-
-    footnoteHandler: function(clickHandler) {
-        var mydom = $('<span>')
-            .addClass('canvas-widget canvas-widget-footnote-handle')
-            .css('display', 'inline')
-            .show();
-
-        mydom.click(function(e) {
-            e.stopPropagation();
-            clickHandler();
-        });
-
-        return mydom;
-    },
-
-    hideButton: function(clickHandler) {
-        var mydom = $('<span>x</span>')
-            .addClass('canvas-widget canvas-widget-hide-button');
-        mydom.click(function(e) {
-            e.stopPropagation();
-            clickHandler();
-        });
-        return mydom;
-    }
-
-};
-
-});
\ No newline at end of file
diff --git a/modules/documentCanvas/canvas/widgets.less b/modules/documentCanvas/canvas/widgets.less
deleted file mode 100644 (file)
index 0f43c89..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-[document-node-element] {
-    .canvas-widgets {
-        display: inline;
-        text-indent: 0;
-    }
-
-    .canvas-widget {
-        display: none;
-    }
-
-    .canvas-widget-label {
-        position: absolute;
-        display: none;
-        top: -20px;
-        left:0;
-        background-color: red;
-        color: white;
-        font-size:12px;
-        font-weight: bold;
-        padding: 1px 3px;
-        //width:300px;
-        opacity: 0.65;
-        font-family: monospace;
-        z-index:9999;
-        white-space: nowrap;
-    }
-
-
-
-    .canvas-widget-footnote-handle {
-        display: inline;
-        outline: 0px solid transparent;
-        cursor: pointer;
-        counter-increment: footnote;
-        vertical-align: super;
-        color: blue;
-        font-size: .8em;
-
-        &::before, {
-            content: "[" counter(footnote) "]";
-        }
-    }
-
-    .canvas-widget-hide-button {
-        @line_height: 12px;
-        @padding: 2px;
-        @temporary_footnote_hack: 10px;
-
-        position: absolute;
-        top: -(@line_height + 2 * @padding) + @temporary_footnote_hack;
-        right: 0;
-        line-height: @line_height;
-        padding: @padding;
-        font-weight: bold;
-        color: white;
-        background-image: linear-gradient(to bottom,#ee5f5b,#bd362f);
-        border-radius: 1px;
-        cursor: pointer;
-    }
-}
\ No newline at end of file
diff --git a/modules/documentCanvas/canvas/wlxmlManagers.js b/modules/documentCanvas/canvas/wlxmlManagers.js
deleted file mode 100644 (file)
index d566686..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-define([
-'libs/jquery',
-'modules/documentCanvas/canvas/widgets'
-], function($, widgets) {
-    
-'use strict';
-
-
-var DocumentElementWrapper = function(documentElement) {
-    
-    this.documentElement = documentElement;
-
-    this.addWidget = function(widget) {
-        documentElement.dom().children('.canvas-widgets').append(widget);
-    };
-
-    this.clearWidgets = function() {
-        documentElement.dom().children('.canvas-widgets').empty();
-    }
-
-    this.setDisplayStyle = function(displayStyle) {
-        documentElement.dom().css('display', displayStyle || '');
-        documentElement._container().css('display', displayStyle || '');
-    };
-
-    this.tag = function() {
-        return documentElement.getWlxmlTag();
-    };
-
-    this.klass = function() {
-        return documentElement.getWlxmlClass();
-    };
-
-    this.toggle = function(toggle) {
-        documentElement._container().toggle(toggle);
-    }
-
-    var eventBus = documentElement.canvas ? documentElement.canvas.eventBus :
-        {trigger: function() {}};
-    this.trigger = function() {
-        eventBus.trigger.apply(eventBus, arguments);
-    }
-
-}
-
-var getDisplayStyle = function(tag, klass) {
-    if(tag === 'metadata')
-        return 'none';
-    if(tag === 'span')
-        return 'inline';
-    if(klass === 'item')
-        return null;
-    return 'block';
-}
-
-var GenericManager = function(wlxmlElement) {
-    this.el = wlxmlElement;
-};
-
-$.extend(GenericManager.prototype, {
-    setup: function() {
-        this.el.setDisplayStyle(getDisplayStyle(this.el.tag(), this.el.klass()));
-
-        this.el.clearWidgets();
-        this.el.addWidget(widgets.labelWidget(this.el.tag(), this.el.klass()));
-
-    },
-    toggle: function(toggle) {
-        this.el.toggle(toggle);
-    }
-
-})
-
-var managers = {
-    _m: {},
-    set: function(tag, klass, manager) {
-        if(!this._m[tag])
-            this._m[tag] = {};
-        this._m[tag][klass] = manager;
-    },
-    get: function(tag,klass) {
-        if(this._m[tag] && this._m[tag][klass])
-            return this._m[tag][klass];
-        return GenericManager;
-    }
-}
-
-var FootnoteManager = function(wlxmlElement) {
-    this.el = wlxmlElement;
-};
-$.extend(FootnoteManager.prototype, {
-    setup: function() {
-        this.el.clearWidgets();
-
-        var clickHandler = function() {
-            this.toggle(true);
-        }.bind(this);
-        this.footnoteHandler = widgets.footnoteHandler(clickHandler);
-        this.el.addWidget(this.footnoteHandler);
-
-        var closeHandler = function() {
-            this.toggle(false);
-
-        }.bind(this);
-        this.hideButton = widgets.hideButton(closeHandler);
-        this.el.addWidget(this.hideButton);
-
-        this.toggle(false, {silent: true});
-    },
-    toggle: function(toggle, options) {
-        options = options || {};
-        this.hideButton.toggle(toggle);
-        this.footnoteHandler.toggle(!toggle);
-        
-        this.el.setDisplayStyle(toggle ? 'block' : 'inline');
-        this.el.toggle(toggle);
-        if(!options.silent)
-            this.el.trigger('elementToggled', toggle, this.el.documentElement);
-    }
-})
-managers.set('aside', 'footnote', FootnoteManager);
-
-
-var ListItemManager = function(wlxmlElement) {
-    this.el = wlxmlElement;
-};
-$.extend(ListItemManager.prototype, {
-    setup: function() {
-        this.el.clearWidgets();
-        this.el.addWidget(widgets.labelWidget(this.el.tag(), this.el.klass()));
-        this.el.documentElement._container().css({display: 'list-item'});
-    },
-    toggleBullet: function(toggle) {
-        this.el.documentElement._container().css({display : toggle ? 'list-item' : 'block'});
-    }
-});
-managers.set('div', 'item', ListItemManager);
-
-return {
-    getFor: function(documentElement) {
-        var wlxmlElement = new DocumentElementWrapper(documentElement);
-        return new (managers.get(wlxmlElement.tag(), wlxmlElement.klass()))(wlxmlElement);
-
-    }
-};
-
-});
\ No newline at end of file
diff --git a/modules/documentCanvas/classAttributes.js b/modules/documentCanvas/classAttributes.js
deleted file mode 100644 (file)
index 06c9485..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-define([], function() {
-    
-'use strict';
-
-var wlxmlDict = {
-    'uri': {
-        'uri': 'string'
-    }
-};
-
-var hasMetaAttr = function(klass, attrName, dict) {
-    dict = dict || wlxmlDict;
-    if(!klass)
-        return false;
-
-    var parts = klass.split('.');
-    var partialClass = '';
-    for(var i = 0; i < parts.length; i++) {
-        partialClass += (partialClass === '' ? '' : '.') + parts[i];
-        if(dict[partialClass] && dict[partialClass][attrName])
-            return true;
-    }
-    return false;
-};
-
-var getMetaAttrsList = function(klass, dict) {
-    dict = dict || wlxmlDict;
-    klass = klass || '';
-
-    var toret = {own: [], inheritedFrom: {}, all: []};
-    var parts = klass.split('.');
-    var partialClass = '';
-    
-    var generate = function(klass) {
-        var toret = [],
-            desc = dict[klass];
-
-        if(!desc)
-            return toret;
-        
-        _.keys(desc).forEach(function(key) {
-            toret.push({name: key, type: desc[key]});
-        });
-        return toret;
-    };
-
-    toret.own = generate(klass);
-    for(var i = 0; i < parts.length; i++) {
-        partialClass += (partialClass === '' ? '' : '.') + parts[i];
-        var list = generate(partialClass);
-        if(list.length > 0) {
-            toret.inheritedFrom[partialClass] = generate(partialClass);
-            toret.all = toret.all.concat(toret.inheritedFrom[partialClass]);
-        }
-    }
-    return toret;
-};
-
-return {
-    hasMetaAttr: hasMetaAttr,
-    getMetaAttrsList: getMetaAttrsList
-};
-
-});
\ No newline at end of file
diff --git a/modules/documentCanvas/commands.js b/modules/documentCanvas/commands.js
deleted file mode 100644 (file)
index 043d6b7..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-define([
-'modules/documentCanvas/canvas/documentElement'
-], function(documentElement) {
-    
-'use strict';
-
-
-var gridToggled = false;
-
-var commands = {
-    _cmds: {},
-    register: function(name, command) {
-        this._cmds[name] = command;
-    },
-
-    run: function(name, params, canvas) {
-        return this._cmds[name](canvas, params);
-    }
-};
-
-commands.register('unwrap-node', function(canvas) {
-    var cursor = canvas.getCursor(),
-        selectionStart = cursor.getSelectionStart(),
-        selectionEnd = cursor.getSelectionEnd(),
-        parent1 = selectionStart.element.parent() || undefined,
-        parent2 = selectionEnd.element.parent() || undefined;
-
-    if(canvas.list.areItemsOfTheSameList({element1: parent1, element2: parent2})) {
-        var selectionAnchor = cursor.getSelectionAnchor();
-        canvas.list.extractItems({element1: parent1, element2: parent2});
-        canvas.setCurrentElement(selectionAnchor.element, {caretTo: selectionAnchor.offset});
-    } else if(!cursor.isSelecting()) {
-        var toUnwrap = cursor.getPosition().element,
-            parent = toUnwrap.unwrap();
-        canvas.setCurrentElement(parent);
-    }
-});
-
-commands.register('wrap-node', function(canvas) {
-    var cursor = canvas.getCursor(),
-        selectionStart = cursor.getSelectionStart(),
-        selectionEnd = cursor.getSelectionEnd(),
-        parent1 = selectionStart.element.parent() || undefined,
-        parent2 = selectionEnd.element.parent() || undefined;
-
-    if(canvas.list.areItemsOfTheSameList({element1: parent1, element2: parent2})) {
-        canvas.list.create({element1: parent1, element2: parent2});
-    }
-});
-
-commands.register('list', function(canvas, params) {
-    var cursor = canvas.getCursor(),
-        selectionStart = cursor.getSelectionStart(),
-        selectionEnd = cursor.getSelectionEnd(),
-        parent1 = selectionStart.element.parent() || undefined,
-        parent2 = selectionEnd.element.parent() || undefined;
-
-    var selectionFocus = cursor.getSelectionFocus();
-
-    if(selectionStart.element.isInsideList() || selectionEnd.element.isInsideList()) {
-        return;
-    }
-
-    canvas.list.create({element1: parent1, element2: parent2});
-
-    canvas.setCurrentElement(selectionFocus.element, {caretTo: selectionFocus.offset});
-});
-
-commands.register('toggle-grid', function(canvas, params) {
-    canvas.doc().dom().find('[wlxml-tag]').toggleClass('rng-common-hoveredNode', params.toggle);
-    gridToggled = params.toggle;
-});
-
-commands.register('newNodeRequested', function(canvas, params) {
-    var cursor = canvas.getCursor(),
-        selectionStart = cursor.getSelectionStart(),
-        selectionEnd = cursor.getSelectionEnd();
-
-    if(cursor.isSelecting()) {
-        if(cursor.isSelectingSiblings()) {
-            if(cursor.isSelectingWithinElement()) {
-                var newElement = selectionStart.element.wrapWithNodeElement({tag: params.wlxmlTag, klass: params.wlxmlClass, start: selectionStart.offset, end: selectionEnd.offset}),
-                    caretTo = selectionStart.offset < selectionEnd.offset ? 'start' : 'end';
-                canvas.setCurrentElement(newElement.children()[0], {caretTo: caretTo});
-            }
-            else {
-                var parent = selectionStart.element.parent(),
-                    caretTo = selectionStart.element.sameNode(cursor.getSelectionAnchor().element) ? 'end' : 'start';
-
-                var wrapper = canvas.wrapText({
-                    inside: parent,
-                    _with: {tag: params.wlxmlTag, klass: params.wlxmlClass},
-                    offsetStart: selectionStart.offset,
-                    offsetEnd: selectionEnd.offset,
-                    textNodeIdx: [parent.childIndex(selectionStart.element), parent.childIndex(selectionEnd.element)]
-                });
-
-                canvas.setCurrentElement(wrapper.children()[caretTo === 0 ? 0 : wrapper.children().length - 1], {caretTo: caretTo});
-            }
-        } else {
-            var siblingParents = canvas.getSiblingParents({element1: selectionStart.element, element2: selectionEnd.element})
-            if(siblingParents) {
-                canvas.wrapElements({
-                    element1: siblingParents.element1,
-                    element2: siblingParents.element2,
-                    _with: {tag: params.wlxmlTag, klass: params.wlxmlClass}
-                });
-            }
-        }
-    } else if(canvas.getCurrentNodeElement()) {
-        var el = canvas.getCurrentNodeElement().wrapWithNodeElement({tag: params.wlxmlTag, klass: params.wlxmlClass});
-        canvas.setCurrentElement(el);
-    }
-
-
-});
-
-commands.register('footnote', function(canvas, params) {
-    var cursor = canvas.getCursor(),
-        position = cursor.getPosition(),
-        asideElement;
-        
-
-    if(cursor.isSelectingWithinElement()) {
-        asideElement = position.element.wrapWithNodeElement({tag: 'aside', klass: 'footnote', start: cursor.getSelectionStart().offset, end: cursor.getSelectionEnd().offset});
-    } else {
-        asideElement = position.element.divide({tag: 'aside', klass: 'footnote', offset: position.offset});
-        asideElement.append({text: ''});
-    }
-
-    asideElement.toggle(true);
-    canvas.setCurrentElement(asideElement);
-});
-
-commands.register('take-away-node', function(canvas) {
-    var position = canvas.getCursor().getPosition(),
-        element = position.element,
-        nodeElement = element ? element.parent() : canvas.getCurrentNodeElement();
-
-    if(!nodeElement || !(nodeElement.parent()))
-        return;
-
-
-    var range = nodeElement.unwrapContents();
-
-    if(element) {
-        var elementIsFirstChild = nodeElement.childIndex(element);
-        if(element.bound()) {
-            canvas.setCurrentElement(element, {caretTo: position.offset});
-        } else {
-            if(elementIsFirstChild) {
-                canvas.setCurrentElement(range.element1, {caretTo: 'end'});
-            } else {
-                canvas.setCurrentElement(range.element2, {caretTo: 'end'});
-            }
-        }
-    } else {
-        canvas.setCurrentElement(range.element1, {caretTo: 'start'});
-    }
-
-});
-
-
-return {
-    run: function(name, params, canvas) {
-        return commands.run(name, params, canvas);
-    }
-};
-
-});
\ No newline at end of file
diff --git a/modules/documentCanvas/documentCanvas.js b/modules/documentCanvas/documentCanvas.js
deleted file mode 100644 (file)
index 3ebab2e..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-// Module that implements main WYSIWIG edit area
-
-define([
-'libs/underscore',
-'./canvas/canvas',
-'./commands',
-'libs/text!./template.html'], function(_, canvas3, commands, template) {
-
-'use strict';
-
-return function(sandbox) {
-
-    var canvas = canvas3.fromXML('', sandbox.publish);
-    var canvasWrapper = $(template);
-    var shownAlready = false;
-    var scrollbarPosition = 0,
-        cursorPosition;
-    
-    canvasWrapper.onShow = function() {
-        if(!shownAlready) {
-            shownAlready = true;
-            canvas.setCurrentElement(canvas.doc().getVerticallyFirstTextElement());
-        } else {
-            canvas.setCursorPosition(cursorPosition);
-            this.find('#rng-module-documentCanvas-contentWrapper').scrollTop(scrollbarPosition);
-        }
-    };
-    
-    canvasWrapper.onHide = function() {
-       scrollbarPosition = this.find('#rng-module-documentCanvas-contentWrapper').scrollTop();
-       cursorPosition = canvas.getCursor().getPosition();
-    };
-
-    /* public api */
-    return {
-        start: function() { sandbox.publish('ready'); },
-        getView: function() { 
-            return canvasWrapper;
-        },
-        setDocument: function(xml) {
-            canvas.loadWlxml(xml);
-            canvasWrapper.find('#rng-module-documentCanvas-content').empty().append(canvas.view());
-            sandbox.publish('documentSet');
-        },
-        getDocument: function() {
-            return canvas.toXML();
-        },
-        modifyCurrentNodeElement: function(attr, value) {
-            var currentNodeElement = canvas.getCurrentNodeElement();
-            if(attr === 'class' || attr === 'tag') {
-                currentNodeElement['setWlxml'+(attr[0].toUpperCase() + attr.substring(1))](value);
-            } else {
-                currentNodeElement.setWlxmlMetaAttr(attr, value);
-            }
-            sandbox.publish('currentNodeElementChanged', currentNodeElement);
-        },
-        highlightElement: function(element) {
-            element.toggleHighlight(true);
-        },
-        dimElement: function(element) {
-            element.toggleHighlight(false);
-        },
-        jumpToElement: function(element) {
-            canvas.setCurrentElement(element);
-        },
-        command: function(command, params) {
-            commands.run(command, params, canvas);
-            sandbox.publish('contentChanged');
-        }
-    };
-    
-};
-
-});
\ No newline at end of file
diff --git a/modules/documentCanvas/documentCanvas.less b/modules/documentCanvas/documentCanvas.less
deleted file mode 100644 (file)
index e17dce4..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-@import 'nodes.less';
-@import 'canvas/widgets.less';
-
-#rng-module-documentCanvas {
-   height: 100%;
-}
-
-#rng-module-documentCanvas-mainArea {
-   height: 100%;
-   margin-bottom: 20px;
-}
-
-#rng-module-documentCanvas-contentWrapper {
-    border-color: #ddd;
-    border-style: solid;
-    border-width: 1px;
-    float:left;
-    width: 100%;
-    height: 100%;
-    overflow-y: scroll;
-    padding: 0 10px;
-    
-    &::-webkit-scrollbar {
-        .rng-mixin-scrollbar;
-    }
-    &::-webkit-scrollbar-track {
-        .rng-mixin-scrollbar-track;
-    }
-    &::-webkit-scrollbar-thumb {
-        .rng-mixin-scrollbar-thumb;
-    }
-    &::-webkit-scrollbar-thumb:window-inactive {
-        .rng-mixin-scrollbar-thumb-window-inactive;
-    }
-    
-    .canvas-wrapper {
-        outline: 0px solid transparent;
-    }
-
-    .current-text-element {
-        outline: 1px dashed black;
-    }
-
-    .current-node-element {
-        border-color: lighten(#000, 15%);
-        border-style: solid;
-        border-width: 1px;
-    }
-
-    .highlighted-element {
-        border: 1px solid red;
-    }
-
-    counter-reset: footnote;
-}
-
-.rng-module-documentCanvas-currentNode {
-    background: #fffacd;
-    border-color: grey;
-    border-style:dashed;
-    border-width:1px;
-}
-
-.rng-module-documentCanvas-hoveredNodeTag {
-    position:absolute;
-    height:20px;
-    top:-20px;
-    left:0;
-    background: #bd362f;
-    color: white;
-    font-size:9px;
-    font-weight: normal;
-    font-style: normal;
-    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
-    padding: 0 5px;
-    text-indent: 0;
-}
-
-[document-node-element] {
-    position:relative;
-    border: 1px solid white;
-}
\ No newline at end of file
diff --git a/modules/documentCanvas/nodes.less b/modules/documentCanvas/nodes.less
deleted file mode 100644 (file)
index d02e872..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-[document-text-element] {
-    font-size: 14px;
-    display: inline;
-    margin: 0 1px;
-    border: 1px solid white;
-    white-space: pre-wrap;
-}
-
-[wlxml-tag] {
-    float: none !important; /* temporaty workaround for Bootstrap's influence via [class*="span"] { float: left; } */
-    border-color: white;
-    border-style:solid;
-    border-width:1px;
-    min-height:20px;
-    position:relative;
-    text-indent: 0;
-    padding: 1px 1px;
-}
-
-[wlxml-tag=span] {
-    min-width: 10px;
-}
-
-[wlxml-tag=header] [document-text-element] {
-    font-size: 13px;
-    font-weight: bold;
-    margin: 10px 0;
-}
-
-[wlxml-tag=section] {
-    margin-top: 10px;
-    margin-bottom: 10px;
-}
-
-[wlxml-tag=section] [wlxml-tag=section] {
-    margin-left:10px;
-}
-
-[wlxml-class|="cite"] {
-    font-style: italic;
-}
-
-[wlxml-class|="cite-code"] {
-    font-family: monospace;
-}
-
-[wlxml-class|="cite-code-xml"] {
-    color: blue;
-}
-
-[wlxml-tag=header] [wlxml-class=author] [document-text-element] {
-    font-size: 14px;
-}
-
-[wlxml-tag=header] [wlxml-class=title] [document-text-element] {
-    font-size:18px;
-}
-
-[wlxml-class|="uri"] {
-    color: blue;
-    text-decoration: underline;
-}
-
-[wlxml-class|="p"] {
-    text-indent: 1.5em;
-}
-
-[wlxml-class|="emph-tech"] {
-    font-style: italic;
-}
-
-[wlxml-tag=metadata] {
-    display:none;
-}
-
-[wlxml-class="list-items"] {
-
-     [wlxml-class="item"] {
-        display: list-item;
-        margin-left: 10px;
-        padding-left: 5px;
-    }
-}
-
-[wlxml-class="item"] {
-    [wlxml-class="list-items"] {
-        display: block;
-    }
-}
-
-
-[wlxml-class="list-items-enum"] {
-
-    counter-reset: myitem;
-
-    > [wlxml-class="item"] {
-        counter-increment: myitem;
-        margin-left: 10px;
-        padding-left: 5px;
-        
-        &:before {
-            content: counter(myitem) '. ';
-            margin-right:10px;
-            padding-right:10px;
-        }
-    }
-}
-
-.canvas-silent-item {
-    display: block !important;
-    counter-increment: none !important;
-    &:before {
-        content: normal !important;
-    }
-}
-
-[wlxml-class="table"] {
-
-    display: table;
-    border: 1px solid black;
-
-    [wlxml-class="row"] {
-        display: table-row;
-        border: 1px solid black;
-    }
-    [wlxml-class="cell"] {
-        display: table-cell;
-        border: 1px solid black;
-        padding: 5px;
-    }
-
-}
-
-[wlxml-tag="aside"] {
-    margin-top: 10px;
-    margin-bottom: 10px;
-}
\ No newline at end of file
diff --git a/modules/documentCanvas/template.html b/modules/documentCanvas/template.html
deleted file mode 100644 (file)
index 63dc331..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<div id="rng-module-documentCanvas">
-    <div id="rng-module-documentCanvas-mainArea">
-        <div id="rng-module-documentCanvas-contentWrapper">
-            <div id="rng-module-documentCanvas-content"></div>
-        </div>
-    </div>
-</div>
\ No newline at end of file
diff --git a/modules/documentCanvas/tests/classAttributes.test.js b/modules/documentCanvas/tests/classAttributes.test.js
deleted file mode 100644 (file)
index 49e0be0..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-define([
-'libs/chai',
-'modules/documentCanvas/classAttributes'
-], function(chai, classAttributes) {
-   
-var stubDict = {
-    'klass': {
-        'prop': 'string'
-    },
-    'klass.sub1': {
-        'prop1': 'string'
-    },
-    'klass.sub1.sub2': {
-        'prop2': 'string'
-    }
-};
-
-var assert = chai.assert;
-
-describe('Class attributes', function() {
-    it('class has own attribute', function() {
-        assert.ok(classAttributes.hasMetaAttr('klass.sub1.sub2', 'prop2', stubDict));
-    });
-
-    it('class has attributes from parent classes', function() {
-        assert.ok(classAttributes.hasMetaAttr('klass.sub1.sub2', 'prop', stubDict));
-        assert.ok(classAttributes.hasMetaAttr('klass.sub1.sub2', 'prop1', stubDict));
-    });
-
-    it('list of class meta attributes', function() {
-        var attrList = classAttributes.getMetaAttrsList('klass.sub1.sub2', stubDict);
-
-        assert.deepEqual(attrList.own, [{name: 'prop2', type: 'string'}]);
-        assert.deepEqual(attrList.inheritedFrom['klass.sub1'], [{name: 'prop1', type: 'string'}]);
-        assert.deepEqual(attrList.inheritedFrom.klass, [{name: 'prop', type: 'string'}]);
-        assert.deepEqual(attrList.all.sort(), [
-            {name: 'prop', type: 'string'},
-            {name: 'prop1', type: 'string'},
-            {name: 'prop2', type: 'string'}
-            ].sort(), 'all values');
-    });
-
-    it('class without meta attrs', function() {
-        var attrList = classAttributes.getMetaAttrsList('some.class', {});
-        assert.deepEqual(attrList.own, [], 'empty own list');
-        assert.deepEqual(attrList.inheritedFrom, {}, 'empty inherited dict');
-        assert.deepEqual(attrList.all, [], 'empty all list');
-    });
-
-    it('empty class', function() {
-        var attrList = classAttributes.getMetaAttrsList('', {});
-        assert.deepEqual(attrList.own, [], 'empty own list');
-        assert.deepEqual(attrList.inheritedFrom, {}, 'empty inherited dict');
-        assert.deepEqual(attrList.all, [], 'empty all list');
-    });
-});
-
-});
\ No newline at end of file
diff --git a/modules/documentHistory/documentHistory.js b/modules/documentHistory/documentHistory.js
deleted file mode 100644 (file)
index f595594..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-define([
-'libs/jquery',
-'libs/underscore',
-'./restoreDialog',
-'libs/text!./templates/main.html',
-'libs/text!./templates/item.html'
-], function($, _, restoreDialog, mainTemplateSrc, itemTemplateSrc) {
-
-'use strict';
-    
-return function(sandbox) {
-    
-    var dom = $(_.template(mainTemplateSrc)());
-    var domNodes = {
-        itemList: dom.find('.rng-module-documentHistory-itemsList'),
-    };
-    var itemViews = [];
-    
-    
-    dom.find('.btn.compare').click(function(e) {
-        var selected = historyItems.getSelected();
-        sandbox.publish('compare', selected[0], selected[1]);
-    });
-    
-    dom.find('.btn.restore').click(function(e) {
-        var dialog = restoreDialog.create();
-        dialog.on('restore', function(event) {
-            sandbox.publish('restoreVersion', {version: historyItems.getSelected()[0], description: event.data.description});
-            event.success();
-        });
-        dialog.show();
-    });
-    
-    dom.find('.btn.display').click(function(e) {
-        sandbox.publish('displayVersion', {version: historyItems.getSelected()[0]});
-    });
-        
-    var addHistoryItem = function(item, options) {
-        historyItems.add(item);
-        var view = new ItemView(item);
-        itemViews.push(view);
-        domNodes.itemList.prepend(view.dom);
-        if(options.animate) {
-            view.dom.hide().slideDown();
-        }
-    };
-    
-    var toggleItemViews = function(toggle) {
-        itemViews.forEach(function(view) {
-            if(!historyItems.isSelected(view.item))
-                view.toggle(toggle);
-        });
-    };
-    
-    var toggleButton = function(btn, toggle) {
-        dom.find('button.'+btn).toggleClass('disabled', !toggle);
-    };
-    
-    var historyItems = {
-        _itemsById: {},
-        _selected: [],
-        select: function(item) {
-            if(this._selected.length < 2) {
-                this._selected.push(item.version);
-                this._updateUI();
-                return true;
-            }
-            return false;
-        },
-        unselect: function(item) {
-            this._selected = _.without(this._selected, item.version);
-            this._updateUI();
-        },
-        add: function(item) {
-            this._itemsById[item.version] = item;
-        },
-        isSelected: function(item) {
-            return _.contains(this._selected, item.version);
-        },
-        getSelected: function() {
-            return this._selected;
-        },
-        _updateUI: function() {
-            var len = this._selected.length;
-            if(len === 0) {
-                toggleButton('compare', false);
-                toggleButton('display', false);
-                toggleButton('restore', false);
-            }
-            if(len === 1) {
-                toggleButton('compare', false);
-                toggleButton('display', true);
-                toggleButton('restore', true);
-            }
-            if(len === 2) {
-                toggleItemViews(false);
-                toggleButton('compare', true);
-                toggleButton('display', false);
-                toggleButton('restore', false);
-            } else {
-                toggleItemViews(true);
-            }
-        }
-    };
-    historyItems._updateUI();
-    
-    var ItemView = function(item) {
-        this.item = item;
-        this.dom = $(this.template(item));
-        this.dom.on('click', _.bind(this.onItemClicked, this));
-    };
-    ItemView.prototype.template = _.template(itemTemplateSrc);
-    ItemView.prototype.onItemClicked = function() {
-        if(historyItems.isSelected(this.item)) {
-            historyItems.unselect(this.item);
-            this.dimItem();
-        } else if(historyItems.select(this.item)) {
-            this.highlightItem();
-        }            
-    };
-    ItemView.prototype.highlightItem = function() {
-        this.dom.addClass('highlighted');
-    };
-    ItemView.prototype.dimItem = function() {
-        this.dom.removeClass('highlighted');
-    };
-    ItemView.prototype.toggle = function(toggle) {
-        this.dom.toggleClass('disabled', !toggle);
-    };
-
-    
-    
-    return {
-        start: function() { sandbox.publish('ready'); },
-        addHistory: function(history, options) {
-            history.forEach(function(historyItem) {
-                addHistoryItem(historyItem, options || {});
-            });
-        },
-        getView: function() {
-            return dom;
-        }
-    };
-};
-
-});
\ No newline at end of file
diff --git a/modules/documentHistory/documentHistory.less b/modules/documentHistory/documentHistory.less
deleted file mode 100644 (file)
index 608c9df..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-.rng-module-documentHistory {
-    
-    .item {
-        padding: 5px 5px;
-        margin: 0 0 15px 0;
-        cursor: pointer;
-        
-        .version {
-            float: left;
-            width: 30px;
-            font-weight: bold;
-        }
-        
-        .date, .author, .description {
-            margin: 5px 10px 0 40px;
-        }
-        
-        .description {
-            font-size: .9em;
-        }
-    }
-    
-    .item.highlighted {
-        background: #ffec63;
-    }
-    
-    .item.disabled {
-        cursor: default;
-    }
-    
-    .toolbar {
-        margin: 0 0 15px 0;
-        white-space:nowrap; 
-        word-spacing:0;
-        min-height: 22px;
-        button {
-            margin-right: 10px;
-        }
-    }
-    
-    .rng-module-documentHistory-itemsList {
-        overflow-y: scroll;
-        position: absolute;
-        top: 30px;
-        bottom: 0;
-        left: 0;
-        right: 0;
-        &::-webkit-scrollbar {
-            .rng-mixin-scrollbar;
-        }
-        &::-webkit-scrollbar-track {
-            .rng-mixin-scrollbar-track;
-        }
-        &::-webkit-scrollbar-thumb {
-            .rng-mixin-scrollbar-thumb;
-        }
-        &::-webkit-scrollbar-thumb:window-inactive {
-            .rng-mixin-scrollbar-thumb-window-inactive;
-        }
-    }
-}
-
diff --git a/modules/documentHistory/restoreDialog.js b/modules/documentHistory/restoreDialog.js
deleted file mode 100644 (file)
index 2bf16f6..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-define([
-'libs/text!./templates/restoreDialog.html',
-'libs/underscore',
-'libs/backbone',
-'libs/jquery'
-], function(restoreDialogTemplate, _, Backbone, $) {
-
-    var DialogView = Backbone.View.extend({
-        template: _.template(restoreDialogTemplate),
-        events: {
-            'click .restore-btn': 'onSave',
-            'click .cancel-btn': 'close',
-            'click .close': 'close'
-        },
-        initialize: function() {
-            _.bindAll(this);
-            this.actionsDisabled = false;
-        },
-        show: function() {           
-            this.setElement(this.template());
-            this.$el.modal({backdrop: 'static'});
-            this.$el.modal('show');
-            this.$('textarea').focus();
-        },
-        onSave: function(e) {
-            e.preventDefault();
-            var view = this;
-            this.trigger('restore', {
-                data: {description: view.$el.find('textarea').val()},
-                success: function() { view.actionsDisabled = false; view.close(); },
-                error: function() { view.actionsDisabled = false; view.close(); },
-            });
-        },
-        close: function(e) {
-            if(e)
-                e.preventDefault();
-            if(!this.actionsDisabled) {
-                this.$el.modal('hide');
-                this.$el.remove();
-            }
-        },
-        toggleButtons: function(toggle) {
-            this.$('.btn, button').toggleClass('disabled', !toggle);
-            this.$('textarea').attr('disabled', !toggle);
-            this.actionsDisabled = !toggle;
-        }
-    });
-
-    return {
-        create: function() {
-            return new DialogView();
-        }
-    };
-
-});
\ No newline at end of file
diff --git a/modules/documentHistory/templates/item.html b/modules/documentHistory/templates/item.html
deleted file mode 100644 (file)
index 1629226..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-<div class="item">
-    <div class="version"><%= version %></div>
-    <div class="date"><%= date %></div>
-    <div class="author"><%= author %></div>
-    <div class="description"><%= description %></div>
-</div>
\ No newline at end of file
diff --git a/modules/documentHistory/templates/main.html b/modules/documentHistory/templates/main.html
deleted file mode 100644 (file)
index 83cd627..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<div class="rng-module-documentHistory">
-    <div class="toolbar">
-        <div class="group">
-            <button class="btn btn-mini compare">Porównaj</button>
-            <button class="btn btn-mini restore">Przywróć</button>
-            <button class="btn btn-mini display">Zobacz</button>
-        </div>
-    </div>
-    <div style="clear:both;"></div>
-    <div class="rng-module-documentHistory-itemsList"></div>
-<div>
\ No newline at end of file
diff --git a/modules/documentHistory/templates/restoreDialog.html b/modules/documentHistory/templates/restoreDialog.html
deleted file mode 100644 (file)
index ded0a11..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<div class="rng-module-data-saveDialog modal hide static">
-    <div class="modal-header">
-        <button type="button" class="close">&times;</button>
-        <h1>Przywróć wersję</h1>
-    </div>
-    <div class="modal-body">
-        <label>Opisz powód przywrócenia</label>
-        <textarea rows="5"></textarea>
-    </div>
-    <div class="modal-footer">
-        <a href="#" class="btn btn-info btn-mini restore-btn">Przywróć</a>
-        <a href="#" class="btn btn-danger btn-mini cancel-btn">Anuluj</a>
-    </div>
-</div>
\ No newline at end of file
diff --git a/modules/documentToolbar/documentToolbar.js b/modules/documentToolbar/documentToolbar.js
deleted file mode 100644 (file)
index 7fbf6c1..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-define(['libs/jquery', 'libs/underscore', 'utils/wlxml', 'libs/text!./template.html'], function($, _, wlxmlUtils, template) {
-
-'use strict';
-
-return function(sandbox) {
-    
-    var view = {
-        node: $(_.template(template)({tagNames: wlxmlUtils.wlxmlTagNames, classNames: wlxmlUtils.wlxmlClassNames})),
-        setup: function() {
-            var view = this;
-            
-            this.node.find('button').click(function(e) {
-                e.stopPropagation();
-                var btn = $(e.currentTarget),
-                    btnName = btn.attr('data-name'),
-                    meta = btn.attr('data-meta'),
-                    params = {},
-                    command = btnName;
-
-                if(btn.attr('data-btn-type') === 'toggle') {
-                    command = 'toggle-' + command;
-                    btn.toggleClass('active');
-                    params.toggle = btn.hasClass('active');
-                }
-
-                if(btnName === 'new-node') {
-                    command = 'newNodeRequested';
-                    params.wlxmlTag = view.getOption('newTag-tag');
-                    params.wlxmlClass = view.getOption('newTag-class');
-                    if(meta) {
-                        var split = meta.split('/');
-                        params.wlxmlTag = split[0];
-                        params.wlxmlClass = split[1];
-                    }
-                } else {
-                    params.meta = meta;
-                }
-
-                sandbox.publish('command', command, params);
-            });
-        },
-        getOption: function(option) {
-            return this.node.find('.rng-module-documentToolbar-toolbarOption[data-option=' + option +']').val();
-        }
-    };
-    
-    view.setup();
-
-    return {
-        start: function() { sandbox.publish('ready'); },
-        getView: function() { return view.node; },
-        getOption: function(option) { return view.getOption(option); }
-    };
-};
-
-});
\ No newline at end of file
diff --git a/modules/documentToolbar/documentToolbar.less b/modules/documentToolbar/documentToolbar.less
deleted file mode 100644 (file)
index 559afde..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-.rng-module-documentToolbar {
-    margin: 0;
-    white-space:nowrap; 
-    word-spacing:0;
-    
-    select {
-        line-height: 14px;
-        font-size:9px;
-        height: auto;
-        width: 50px;
-        padding: 1px;
-    -webkit-appearance: button;
-    -moz-appearance: button;
-    appearance: button;
-        margin-bottom: 0;
-    }
-
-    .rng-module-documentToolbar-toolbarGroup {
-        border-width: 0 1px 0 0;
-        border-style: solid;
-        border-color: #ddd;
-        padding: 0 8px 0 0;
-        margin: 0 8px 0 0;
-        float:left;
-    }
-    
-}
-
diff --git a/modules/documentToolbar/template.html b/modules/documentToolbar/template.html
deleted file mode 100644 (file)
index 7ba2bc5..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-<div class="rng-module-documentToolbar">
-    <div class="rng-module-documentToolbar-toolbarGroup">
-        <button data-name="new-node" data-meta="header/" data-btn-type="cmd" class="btn btn-mini">nagłówek</button>
-        <button data-name="new-node" data-meta="div/p" data-btn-type="cmd" class="btn btn-mini">paragraf</button>
-        <button data-name="new-node" data-meta="span/cite" data-btn-type="cmd" class="btn btn-mini">cytat</i></button>
-        <button data-name="new-node" data-meta="span/uri" data-btn-type="cmd" class="btn btn-mini">uri</i></button>
-        <select class="rng-module-documentToolbar-toolbarOption" data-option="newTag-tag">
-            <% var options = ['', 'section', 'header', 'div', 'span', 'aside']; %>
-            <% options.forEach(function(option) { %>
-                <option value="<%= option %>" <% if(option==='span') { %>selected<% } %>><%= tagNames[option] %></option>
-            <% }); %>
-        </select>
-        <select class="rng-module-documentToolbar-toolbarOption" data-option="newTag-class">
-            <% var options = ['', 'author', 'title', 'cite', 'cite.code', 'cite.code.xml', 'list.items', 'item', 'uri', 'p', 'footnote', 'todo', 'emp'] %>
-            <% options.forEach(function(option) { %>
-                <option value="<%= option.replace(/\./g, '-') %>" <% if(option==='cite') { %>selected<% } %>><%= classNames[option] %></option>
-            <% }); %>
-        </select>
-        <button data-name="new-node" data-btn-type="cmd" class="btn btn-mini"><i class="icon-plus"></i></button>
-    </div>
-    <div class="rng-module-documentToolbar-toolbarGroup">
-        <button data-name="take-away-node" data-btn-type="cmd" class="btn btn-mini"><i class="icon-remove-circle"></i></button>
-    </div>    
-    <div class="rng-module-documentToolbar-toolbarGroup">
-        <button data-name="list" data-btn-type="cmd" data-meta="bullets" class="btn btn-mini"><i class="icon-list"></i></button>
-        <button data-name="unwrap-node" data-btn-type="cmd" class="btn btn-mini"><i class="icon-arrow-up"></i></button>
-        <button data-name="wrap-node" data-btn-type="cmd" class="btn btn-mini"><i class="icon-arrow-down"></i></button>
-    </div>
-    <div class="rng-module-documentToolbar-toolbarGroup">
-        <button data-name="footnote" data-btn-type="cmd" class="btn btn-mini"><i class="icon-comment"></i></button>
-    </div>
-    <div class="rng-module-documentToolbar-toolbarGroup">
-        <button data-name="grid" data-btn-type="toggle" class="btn btn-mini"><i class="icon-th-large"></i></button>
-    </div>
-
-    <div style="clear: both;"></div>
-</div>
\ No newline at end of file
diff --git a/modules/indicator/indicator.js b/modules/indicator/indicator.js
deleted file mode 100644 (file)
index 827a4b7..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-define([
-'libs/jquery',
-'libs/underscore',
-'libs/text!./template.html'
-], function($, _, template) {
-
-'use strict';
-
-return function(sandbox) {
-
-    var view = {
-        dom: $(_.template(template)()),
-        setup: function() {
-        
-        }
-    };
-
-    return {
-        start: function() { sandbox.publish('ready'); },
-        getView: function() { return view.dom; },
-        showMessage: function(msg) {
-            view.dom.html('<span>' + msg + '</span>').show();
-        },
-        clearMessage: function(report) {
-            view.dom.empty();
-            if(report && report.message) {
-                view.dom.html('<span class="success">' + report.message + '</span>').show().fadeOut(4000);
-            }
-        }
-        
-    };
-
-};
-
-});
diff --git a/modules/indicator/indicator.less b/modules/indicator/indicator.less
deleted file mode 100644 (file)
index 48f6e3b..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-.rng-module-indicator {
-     span {
-        font-weight: bold;
-        background: #f9edbe;
-        padding: 2px 5px;
-        border: solid 1px #f6e39c;
-        font-size:12px;
-    }
-    
-    span.success {
-        background: #cef9be;
-        border-color: darken(#cef9be, 10%);
-    }
-}
\ No newline at end of file
diff --git a/modules/indicator/template.html b/modules/indicator/template.html
deleted file mode 100644 (file)
index e4e449b..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-<div class="rng-module-indicator">
-
-</div>
\ No newline at end of file
diff --git a/modules/mainBar/mainBar.js b/modules/mainBar/mainBar.js
deleted file mode 100644 (file)
index ecf6812..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-define([
-'libs/jquery',
-'libs/underscore',
-'libs/text!./template.html'], function($, _, template) {
-
-'use strict';
-
-return function(sandbox) {
-
-    var view = $(_.template(template)());
-    view.find('button').click(function(e) {
-        e.preventDefault();
-        sandbox.publish('cmd.' + $(e.target).attr('data-cmd'));
-    });
-
-    return {
-        start: function() { sandbox.publish('ready'); },
-        getView: function() {return view;},
-        setCommandEnabled: function(cmd, enabled) {
-            view.find('[data-cmd='+cmd+']').toggleClass('disabled', !enabled);
-        },
-        setVersion: function(version) {
-            view.find('.version').text(version);
-        }
-    };
-
-};
-
-});
\ No newline at end of file
diff --git a/modules/mainBar/mainBar.less b/modules/mainBar/mainBar.less
deleted file mode 100644 (file)
index 0e2a899..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*#rng-skelton-menu {
-    float: right;
-}*/
-
-.rng-module-mainBar {
-
-    font-size: 10px;
-
-    li {
-        display: inline;
-        border-width: 0 1px 0 0;
-        border-color: #ddd;
-        border-style: solid;
-        margin: 0 5px 0 0;
-        padding: 0 5px 0 0;
-    }
-
-    ul {
-        list-style-type: none;
-    }
-}
\ No newline at end of file
diff --git a/modules/mainBar/template.html b/modules/mainBar/template.html
deleted file mode 100644 (file)
index 3c43ee6..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<div class="rng-module-mainBar">
-    <ul>
-        <li>wersja: <span class="version"></span></li>
-        <li><button class="btn btn-mini btn-info" data-cmd="save">Zapisz</button></li>
-        <li><a class="btn btn-mini btn-danger" href="/">Wyjdź</a></li>
-    </ul>
-</div>
\ No newline at end of file
diff --git a/modules/metadataEditor/metadataEditor.js b/modules/metadataEditor/metadataEditor.js
deleted file mode 100644 (file)
index 94ad85b..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-define([
-'libs/jquery',
-'libs/underscore',
-'./transformations',
-'libs/text!./templates/main.html',
-'libs/text!./templates/item.html'
-], function($, _, transformations, mainTemplate, itemTemplate) {
-
-'use strict';
-
-return function(sandbox) {
-
-    
-    var view = {
-        node: $(_.template(mainTemplate)()),
-        setup: function() {
-            var view = this;
-            var metaTable = this.metaTable = this.node.find('table');
-            
-            this.node.find('.rng-module-metadataEditor-addBtn').click(function() {
-                var newRow = view._addMetaRow('', '');
-                $(newRow.find('td div')[0]).focus();
-                sandbox.publish('metadataChanged', view.getMetadata());
-            });
-            
-            this.metaTable.on('click', '.rng-visualEditor-metaRemoveBtn', function(e) {
-                $(e.target).closest('tr').remove();
-                sandbox.publish('metadataChanged', view.getMetadata());
-            });
-            
-            this.metaTable.on('keydown', '[contenteditable]', function(e) {
-                console.log(e.which);
-                if(e.which === 13) { 
-                    if($(document.activeElement).hasClass('rng-module-metadataEditor-metaItemKey')) {
-                        metaTable.find('.rng-module-metadataEditor-metaItemValue').focus();
-                    } else {
-                        var input = $('<input>');
-                        input.appendTo('body').focus();
-                        view.node.find('.rng-module-metadataEditor-addBtn').focus();
-                        input.remove();
-                    }
-                    e.preventDefault();
-                }
-            });
-            
-            
-            var onKeyUp = function(e) {
-                if(e.which !== 13)
-                    sandbox.publish('metadataChanged', view.getMetadata());
-            };
-            this.metaTable.on('keyup', '[contenteditable]', _.throttle(onKeyUp, 500));
-        },
-        getMetadata: function() {
-            var toret = {};
-            this.node.find('tr').each(function() {
-                var tr = $(this);
-                var inputs = $(this).find('td [contenteditable]');
-                var key = $(inputs[0]).text();
-                var value = $(inputs[1]).text();
-                toret[key] = value;
-            });
-            return toret;
-        },
-        setMetadata: function(metadata) {
-            var view = this;
-            this.metaTable.find('tr').remove();
-            _.each(_.keys(metadata), function(key) {    
-                view._addMetaRow(key, metadata[key]);
-            });
-        },
-        _addMetaRow: function(key, value) {
-            var newRow = $(_.template(itemTemplate)({key: key || '', value: value || ''}));
-            newRow.appendTo(this.metaTable);
-            return newRow;
-        }
-    };
-    
-    view.setup();
-    
-    return {
-        start: function() {
-            sandbox.publish('ready');
-        },
-        setDocument: function(xml) {
-            view.setMetadata(transformations.getMetadata(xml));
-            sandbox.publish('metadataSet');
-        },
-        getMetadata: function() {
-            return transformations.getXML(view.getMetadata());
-        },
-        getView: function() {
-            return view.node;
-        },
-        attachMetadata: function(document) {
-            var toret = $('<div>');
-            toret.append($(document));
-            var meta = $('<metadata></metadata>\n').append(transformations.getXML(view.getMetadata()));
-            
-            var metadata = toret.find('metadata');
-            if(metadata.length === 0) {
-                var section = toret.find('section');
-                section = section.length ? $(section[0]) : null;
-                if(section) {
-                    section.prepend(meta);
-                }
-            } else {
-                metadata.replaceWith(meta);
-            }
-            return toret.html();
-        }
-        
-    };
-};
-
-});
\ No newline at end of file
diff --git a/modules/metadataEditor/metadataEditor.less b/modules/metadataEditor/metadataEditor.less
deleted file mode 100644 (file)
index 42bf5c0..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-.rng-module-metadataEditor {
-
-    table {
-        margin-bottom:10px;
-    
-        [contenteditable] {
-            cursor: pointer;
-        }
-
-        li:last-child {
-            border-bottom: none !important;
-        }
-
-        tr td:nth-child(1){
-            width: 20%;
-        }
-
-        tr td:nth-child(2)  {
-            width:80%;
-        }
-    }
-
-    .rng-module-metadataEditor-addBtn {
-        float:right;
-        margin-right:6px;
-    }
-
-    .btn {
-        padding:3px;
-        line-height:10px;
-    }
-}
\ No newline at end of file
diff --git a/modules/metadataEditor/templates/item.html b/modules/metadataEditor/templates/item.html
deleted file mode 100644 (file)
index 3920f68..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<tr>
-    <td><div contenteditable="true" class="rng-module-metadataEditor-metaItemKey"><%= key %></div></td>
-    <td><div contenteditable="true" class="rng-module-metadataEditor-metaItemValue"><%= value %></div></td>
-    <td><button class="rng-visualEditor-metaRemoveBtn btn btn-mini btn-danger">x</button></td>
-</tr>
\ No newline at end of file
diff --git a/modules/metadataEditor/templates/main.html b/modules/metadataEditor/templates/main.html
deleted file mode 100644 (file)
index 5826f82..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<div class="rng-module-metadataEditor">
-    <fieldset>
-    <legend>Meta dane</legend>
-    <table class="table table-condensed table-bordered">
-    </table>
-    <button class="rng-module-metadataEditor-addBtn btn btn-mini btn-info">+</button></td>
-    </fieldset>
-</div>
\ No newline at end of file
diff --git a/modules/metadataEditor/transformations.js b/modules/metadataEditor/transformations.js
deleted file mode 100644 (file)
index a09ee38..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-define(['libs/jquery'], function($) {
-
-    'use strict';
-
-    return {
-        getMetadata: function(xml) {
-            var toret = {};
-            $(xml).find('metadata').children().each(function() {
-                var node = $(this);
-                toret[this.nodeName.split(':')[1].toLowerCase()] = node.text();
-            });
-            return toret;
-        },
-        getXML: function(metadata) {
-            var meta = $('<metadata></metadata>\n');
-            _.each(_.keys(metadata), function(key) {
-                meta.append('\n\t<dc:'+key+'>' + metadata[key] + '</dc:'+key+'>');
-            });
-            meta.append('\n');
-            return vkbeautify.xml(meta.html());
-        }
-    };
-
-});
\ No newline at end of file
diff --git a/modules/nodeBreadCrumbs/nodeBreadCrumbs.js b/modules/nodeBreadCrumbs/nodeBreadCrumbs.js
deleted file mode 100644 (file)
index 320a1e8..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-define([
-'libs/jquery',
-'libs/underscore',
-'utils/wlxml',
-'libs/text!./template.html'], function($, _, wlxmlUtils, templateSrc) {
-
-'use strict';
-
-return function(sandbox) {
-    
-    var template = _.template(templateSrc);
-    
-    var view = {
-        dom: $('<div>' + template({node:null, parents: null}) + '</div>'),
-        setup: function() {
-            var view = this;
-            this.dom.on('mouseenter', 'a', function(e) {
-                var target = $(e.target);
-                sandbox.publish('elementEntered', target.data('element'));
-            });
-            this.dom.on('mouseleave', 'a', function(e) {
-                var target = $(e.target);
-                sandbox.publish('elementLeft', target.data('element'));
-            });
-            this.dom.on('click', 'a', function(e) {
-                e.preventDefault();
-                var target = $(e.target);
-                sandbox.publish('elementClicked', target.data('element'));
-            });
-        },
-        
-        setNodeElement: function(nodeElement) {
-            this.dom.empty();
-            this.currentNodeElement = nodeElement;
-            var parents = nodeElement.parents();
-            this.dom.html(template({node: nodeElement, parents: parents, tagNames: wlxmlUtils.wlxmlTagNames, classNames: wlxmlUtils.wlxmlClassNames}));
-
-            this.dom.find('li > a[href="#"]').each(function(idx, a) {
-                $(a).data('element', parents[parents.length - 1 - idx]);
-            });
-            this.dom.find('a.active').data('element', nodeElement);
-        },
-        
-        highlightNode: function(node) {
-            this.dom.find('a[data-id="'+node.id+'"]').addClass('rng-common-hoveredNode');
-        },
-        dimNode: function(node) {
-            this.dom.find('a[data-id="'+node.id+'"]').removeClass('rng-common-hoveredNode');
-        }
-    };
-    
-    view.setup();
-    
-    return {
-        start: function() { sandbox.publish('ready'); },
-        getView: function() { return view.dom; },
-        setNodeElement: function(nodeElement) {
-            view.setNodeElement(nodeElement);
-        },
-        highlightNode: function(id) { view.highlightNode(id); },
-        dimNode: function(id) { view.dimNode(id); }
-    };
-};
-
-});
\ No newline at end of file
diff --git a/modules/nodeBreadCrumbs/template.html b/modules/nodeBreadCrumbs/template.html
deleted file mode 100644 (file)
index afe44de..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<div class="rng-module-nodeBreadCrumbs">
-    <ul class="breadcrumb">
-    <% if(node && parents) { %>
-        <% for(var i = parents.length - 1; i >= 0; i--) { %>
-            <li><a href="#"> <%= tagNames[parents[i].getWlxmlTag()] %><% if(parents[i].getWlxmlClass()) { %>.<%= classNames[parents[i].getWlxmlClass()] %> <% } %></a><span class="divider">/</span></li>
-        <% } %>
-            <li class="active"><%= tagNames[node.getWlxmlTag()] %><% if(node.getWlxmlClass()) { %>.<%= classNames[node.getWlxmlClass()] %> <% } %></span></li>
-    <% } %>
-    </ul>
-</div>
\ No newline at end of file
diff --git a/modules/nodeFamilyTree/nodeFamilyTree.js b/modules/nodeFamilyTree/nodeFamilyTree.js
deleted file mode 100644 (file)
index 45f47c0..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-define([
-'libs/jquery',
-'libs/underscore',
-'utils/wlxml',
-'libs/text!./template.html'
-], function($, _, wlxmlUtils, templateSrc) {
-
-'use strict';
-
-return function(sandbox) {
-    
-    var template = _.template(templateSrc);
-    
-    var view = {
-        dom: $('<div>' + template({children: null, parent: null}) + '</div>'),
-        setup: function() {
-            var view = this;
-            this.dom.on('click', 'a', function(e) {
-                var target = $(e.target);
-                sandbox.publish('elementClicked', target.data('element'));
-            });
-            
-            this.dom.on('mouseenter', 'a', function(e) {
-                var target = $(e.target);
-                sandbox.publish('elementEntered', target.data('element'));
-            });
-            this.dom.on('mouseleave', 'a', function(e) {
-                var target = $(e.target);
-                sandbox.publish('elementLeft', target.data('element'));
-            });
-        },
-        setElement: function(element) {
-            console.log('familyTree sets node');
-            var textElement = element.getText ? element : null,
-                nodeElement = element.getText ? element.parent() : element, // TODO: better type detection
-                nodeElementParent = nodeElement.parent(),
-                parent;
-            
-            this.currentNodeElement = nodeElement;
-
-            if(nodeElementParent) {
-                parent = {
-                    repr: wlxmlUtils.wlxmlTagNames[nodeElementParent.getWlxmlTag()] + (nodeElementParent.getWlxmlClass() ? ' / ' + wlxmlUtils.wlxmlClassNames[nodeElementParent.getWlxmlClass()] : '')
-                };
-            }
-        
-            var nodeChildren = nodeElement.children(),
-                children = [];
-            nodeChildren.forEach(function(child) {
-                if(child.getText) {
-                    var text = child.getText();
-                    if(!text)
-                        text = '<pusty tekst>';
-                    else {
-                        if(text.length > 13) {
-                            text = text.substr(0,13) + '...';
-                        }
-                        text = '"' + text + '"';
-                    }
-                    children.push({repr: _.escape(text), bold: child.sameNode(textElement)});
-                } else {
-                    children.push({repr: wlxmlUtils.wlxmlTagNames[child.getWlxmlTag()] + (child.getWlxmlClass() ? ' / ' + wlxmlUtils.wlxmlClassNames[child.getWlxmlClass()] : '')});
-                }
-            });
-            this.dom.empty();
-            this.dom.append($(template({parent: parent, children: children})));
-
-            if(parent) {
-                this.dom.find('.rng-module-nodeFamilyTree-parent').data('element', nodeElementParent)
-            }
-            this.dom.find('li a').each(function(idx, a) {
-                $(a).data('element', nodeChildren[idx]);
-            });
-        },
-        highlightNode: function(canvasNode) {
-            this.dom.find('a[data-id="'+canvasNode.getId()+'"]').addClass('rng-common-hoveredNode');
-        },
-        dimNode: function(canvasNode) {
-            this.dom.find('a[data-id="'+canvasNode.getId()+'"]').removeClass('rng-common-hoveredNode');
-        }
-    };
-    
-    view.setup();
-    
-    return {
-        start: function() {
-            sandbox.publish('ready');
-        },
-        setElement: function(element) {
-            if(!(element.sameNode(view.currentNodeElement)))
-                view.setElement(element);
-        },
-        getView: function() {
-            return view.dom;
-        },
-        highlightNode: function(canvasNode) {
-            view.highlightNode(canvasNode);
-        },
-        dimNode: function(canvasNode) {
-            view.dimNode(canvasNode);
-        }
-    };
-};
-
-});
\ No newline at end of file
diff --git a/modules/nodeFamilyTree/nodeFamilyTree.less b/modules/nodeFamilyTree/nodeFamilyTree.less
deleted file mode 100644 (file)
index 6b4dc77..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-.rng-module-nodeFamilyTree {
-    overflow-y: scroll;
-    max-height: 150px;
-    width:100%;
-    margin-top:10px;
-
-    table {
-        width: 90%;
-        margin: 0;
-    
-        tr {
-            td:nth-child(1) {
-                width: 30%;
-            }
-            td:nth-child(2) {
-                width: 70%;
-            }
-            td ul {
-                list-style-type: none;
-                margin: 0;
-            }
-            
-        }
-    }
-    
-    &::-webkit-scrollbar {
-        .rng-mixin-scrollbar;
-    }
-    &::-webkit-scrollbar-track {
-        .rng-mixin-scrollbar-track;
-    }
-    &::-webkit-scrollbar-thumb {
-        .rng-mixin-scrollbar-thumb;
-    }
-    &::-webkit-scrollbar-thumb:window-inactive {
-        .rng-mixin-scrollbar-thumb-window-inactive;
-    }
-}
\ No newline at end of file
diff --git a/modules/nodeFamilyTree/template.html b/modules/nodeFamilyTree/template.html
deleted file mode 100644 (file)
index 9bccfc3..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-<div class="rng-module-nodeFamilyTree">
-    <table class="table table-bordered">
-        <tr>
-            <td>powyżej</td>
-            <td><% if(parent) { %><a href="#" class="rng-module-nodeFamilyTree-parent"><%= parent.repr %></a><% } else { %>-<% } %></td>
-        </tr>
-        <tr>
-            <td rowspan="0">poniżej</td>
-            <td>
-                <ul>
-                <% if(!children || children.length === 0) { %>-<% } else { %>
-                    <% children.forEach(function(child) { %>
-                        <li><% if(child.bold) { %><strong><% } %><a href="#"><%= child.repr %></a><% if(child.bold) { %></strong><% } %></li>
-                    <% }); %>
-                <% } %>
-                </ul>
-            </td>
-        </tr>
-</div>
\ No newline at end of file
diff --git a/modules/nodePane/metaWidget/metaWidget.js b/modules/nodePane/metaWidget/metaWidget.js
deleted file mode 100644 (file)
index 14ba7b6..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-define([
-'libs/jquery',
-'libs/underscore',
-'libs/backbone',
-'libs/text!./stringField.html'
-], function($, _, Backbone, stringFieldTpl) {
-
-'use strict';
-
-var templates = {
-    string: _.template(stringFieldTpl)
-};
-
-var getAttrElement = function(attr) {
-    var toret = $('<div>');   
-    toret.append(templates.string({name: attr.name, value: attr.value}));
-    return toret;
-};
-
-var MetaWidget = Backbone.View.extend({
-    events: {
-        'change [metaField-name]': 'onMetaFieldChange' 
-    },
-    initialize: function() {
-        var view = this;
-        this.options.attrs.forEach(function(attr) {
-            view.$el.append(getAttrElement(attr));
-        });
-    },
-    onMetaFieldChange: function(e) {
-        var target = $(e.target);
-        this.trigger('valueChanged', target.attr('metaField-name'), target.val());
-    }
-});
-
-
-return {
-    create: function(options) {
-        return new MetaWidget(options);
-    }
-};
-
-});
\ No newline at end of file
diff --git a/modules/nodePane/metaWidget/metaWidget.test.js b/modules/nodePane/metaWidget/metaWidget.test.js
deleted file mode 100644 (file)
index 05b75dc..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-define([
-'libs/chai',
-'libs/sinon',
-'modules/nodePane/metaWidget/metaWidget'
-], function(chai, sinon, metaWidget) {
-
-'use strict';
-
-var assert = chai.assert;
-
-describe('metaWidget', function() {
-    it('calls calls registered callback on value change', function() {
-        var dom = $('<div>');
-        var widget = metaWidget.create({
-                el: dom,
-                attrs: [{name: 'uri', type: 'string', value: 'test string'}],
-            });
-
-        var spy = sinon.spy();
-        widget.on('valueChanged', spy);
-        var input = dom.find('input');
-
-        input.change();
-        assert.ok(spy.calledOnce, 'called once');
-        assert.ok(spy.calledWith('uri', 'test string'), 'called with');
-
-        spy.reset();
-        input.val('new val').change();
-        assert.ok(spy.calledOnce, 'called once');
-        assert.ok(spy.calledWith('uri', 'new val'), 'called with new val');
-    });
-});
-
-
-});
\ No newline at end of file
diff --git a/modules/nodePane/metaWidget/stringField.html b/modules/nodePane/metaWidget/stringField.html
deleted file mode 100644 (file)
index a5156a2..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-<label><%= name %></label>
-<input type="text" metaField-name="<%= name %>" value="<%= value %>">
\ No newline at end of file
diff --git a/modules/nodePane/nodePane.js b/modules/nodePane/nodePane.js
deleted file mode 100644 (file)
index f3fb68a..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-define([
-'libs/text!./template.html',
-'libs/jquery',
-'libs/underscore',
-'modules/nodePane/metaWidget/metaWidget',
-'utils/wlxml'
-], function(templateSrc, $, _, metaWidget, wlxmlUtils) {
-
-'use strict';
-
-return function(sandbox) {
-    
-    var view = $(_.template(templateSrc)({tagNames: wlxmlUtils.wlxmlTagNames, classNames: wlxmlUtils.wlxmlClassNames}));
-    
-    view.on('change', 'select', function(e) {
-        var target = $(e.target);
-        var attr = target.attr('class').split('-')[3] === 'tagSelect' ? 'tag' : 'class';
-        sandbox.publish('nodeElementChange', attr, target.val().replace(/-/g, '.'));
-    });
-    
-    return {
-        start: function() {
-            sandbox.publish('ready');
-        },
-        getView: function() {
-            return view;
-        },
-        setNodeElement: function(nodeElement) {
-            view.find('.rng-module-nodePane-tagSelect').val(nodeElement.getWlxmlTag());
-
-            var escapedClassName = (nodeElement.getWlxmlClass() || '').replace(/\./g, '-')
-            view.find('.rng-module-nodePane-classSelect').val(escapedClassName);
-
-            var widget = metaWidget.create({attrs:nodeElement.getWlxmlMetaAttrs()});
-            widget.on('valueChanged', function(key, value) {
-                sandbox.publish('nodeElementChange', key, value);
-            });
-            view.find('.metaFields').empty().append(widget.el);
-        }
-    };
-    
-};
-
-});
\ No newline at end of file
diff --git a/modules/nodePane/nodePane.less b/modules/nodePane/nodePane.less
deleted file mode 100644 (file)
index b7e4012..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-.rng-module-nodePane {
-    label {
-        width: 50px;
-        display: inline-block;
-    }
-    select {
-        width: 100px;
-    }
-
-    input {
-        width: 80px;
-        padding: 0 10px;
-    }
-}
\ No newline at end of file
diff --git a/modules/nodePane/template.html b/modules/nodePane/template.html
deleted file mode 100644 (file)
index b9a1254..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-<div class="rng-module-nodePane">
-    <fieldset>
-        <legend><%= gettext('Current node') %></legend>
-        <div>
-            <label>Tag</label>
-            <select class="rng-module-nodePane-tagSelect">
-                <% var options = ['', 'section', 'header', 'div', 'span', 'aside']; %>
-                <% options.forEach(function(option) { %>
-                    <option value="<%= option %>" <% if(option === '') { %>selected<% } %>><%= tagNames[option] %></option>
-                <% }); %>
-            </select>
-        </div>
-        <div>
-            <label>Klasa</label>
-            <select  class="rng-module-nodePane-classSelect">
-                <% var options = ['', 'author', 'title', 'cite', 'cite.code', 'cite.code.xml', 'list.items', 'list.items.enum', 'item', 'uri', 'p', 'footnote', 'todo', 'emp'] %>
-                <% options.forEach(function(option) { %>
-                    <option value="<%= option.replace(/\./g, '-') %>" <% if(option === '') { %>selected<% } %>><%= classNames[option] %></option>
-                <% }); %>
-            </select>
-        </div>
-        <div class="metaFields">
-        </div>
-    </fieldset>
-</div>
\ No newline at end of file
diff --git a/modules/rng/diffLayout.html b/modules/rng/diffLayout.html
deleted file mode 100644 (file)
index 8b03929..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<div class="rng-module-rng-diffLayout">
-    <div fnpjs-place="left"></div>
-    <div fnpjs-place="right"></div>
-</div>
\ No newline at end of file
diff --git a/modules/rng/diffLayout.less b/modules/rng/diffLayout.less
deleted file mode 100644 (file)
index 0f51948..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-.rng-module-rng-diffLayout {
-    [fnpjs-place=left] {
-        width: 300px;
-        margin: 0 20px 0 0px;
-        left: 0;
-    }
-    [fnpjs-place=right] {
-        left: 320px;
-        right: 0;
-    }
-    
-    [fnpjs-place=left], [fnpjs-place=right] {
-        position: absolute;
-        top: 0;
-        bottom: 0;
-        
-
-    }
-
-}
\ No newline at end of file
diff --git a/modules/rng/editingLayout.html b/modules/rng/editingLayout.html
deleted file mode 100644 (file)
index 54a29aa..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<div class="fnp-module-rng-editingLayout">
-    <div fnpjs-place="toolbar"></div>
-    <div class="rng-module-rng2-left" fnpjs-place="leftColumn"></div>
-    <div class="rng-module-rng2-right" fnpjs-place="rightColumn"></div>
-    <div style="clear:both;"></div>
-    <div class="rng-module-rng2-statusBar" fnpjs-place="statusBar"></div>
-</div>
\ No newline at end of file
diff --git a/modules/rng/editingLayout.less b/modules/rng/editingLayout.less
deleted file mode 100644 (file)
index 19d165f..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-.rng-module-rng2-left {
-    /*float: left;
-    width: 600px;*/
-}
-
-.rng-module-rng2-right {
-    /*float: right;
-    position: relative;
-    width: 258px;
-    margin-left: 50px;*/
-
-    border-width: 1px 1px 1px 1px;
-    border-style: solid;
-    border-color: #ddd;
-    padding: 5px 15px;
-    
-    p, td, label, input, select {
-        font-size: 11px;
-        line-height:13px;
-    }
-    
-    select {
-        -webkit-appearance: button;
-        -moz-appearance: button;
-        appearance: button;
-        height: auto;
-        line-height: 14px;
-    }
-    
-    legend {
-        font-size:11px;
-        height:30px;
-    }
-    
-    
-    .rng-view-tabs-tabBar {
-        position:absolute;
-        top:-1px;
-        right:-50px;
-        border-width: 1px 1px 1px 0px;
-        border-style: solid;
-        border-color: #ddd;
-        padding: 5px;
-        background: #ededed;
-    }
-    
-    label + select {
-        position:relative;
-        top: 5px;
-    }
-
-}
-
-.rng-module-rng2-statusBar {
-    margin: 10px 5px;
-    font-size:0.9em;
-}
-
-.fnp-module-rng-editingLayout {
-
-    [fnpjs-place="statusBar"] {
-        position: absolute;
-        bottom: 0;
-        left: 0;
-        right: 0;
-        height: 30px;
-        
-    }
-    
-    [fnpjs-place="leftColumn"], [fnpjs-place="rightColumn"] {
-        position: absolute;
-        top: 30px; //
-        bottom: 50px; //
-    }
-    
-    [fnpjs-place="leftColumn"] {
-        left:0;
-        right: 320px;
-    }
-    
-    [fnpjs-place="rightColumn"] {
-        right: 0px;
-        width: 250px;
-        
-    }
-
-
-}
\ No newline at end of file
diff --git a/modules/rng/mainLayout.html b/modules/rng/mainLayout.html
deleted file mode 100644 (file)
index 0cccb9c..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<div id="rng-module-rng-mainLayout">
-    <div fnpjs-place="messages"></div>
-    <div fnpjs-place="topPanel">
-    </div>
-    <div style="clear:both"></div>
-    <div fnpjs-place="mainView">
-    </div>
-</div>
\ No newline at end of file
diff --git a/modules/rng/mainLayout.less b/modules/rng/mainLayout.less
deleted file mode 100644 (file)
index ac28f02..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#rng-module-rng-mainLayout {
-    position: fixed;
-    top: 5px;
-    bottom: 5px;
-    left: 80px;
-    right: 80px;
-    
-    [fnpjs-place="messages"] {
-        position: absolute;
-        top: 5px;
-        width:100%;
-        text-align:center;
-    }
-    
-    [fnpjs-place="topPanel"] {
-        float: right;
-        position: relative;
-        z-index: 2;
-    }
-    
-    [fnpjs-place="mainView"] {
-        position: absolute;
-        top: 0;
-        left:0;
-        right:0;
-        bottom:0;
-        z-index: 1;
-        
-        > .rng-view-tabs {
-            position: relative;
-            height: 100%;
-
-            > .rng-view-tabs-content {
-                position: absolute;
-                top: 45px;
-                left: 0;
-                right: 0;
-                bottom: 0;
-            }    
-        }
-        
-    }
-}
\ No newline at end of file
diff --git a/modules/rng/rng.js b/modules/rng/rng.js
deleted file mode 100644 (file)
index 59652a7..0000000
+++ /dev/null
@@ -1,326 +0,0 @@
-define([
-'fnpjs/layout',
-'fnpjs/vbox',
-'views/tabs/tabs',
-'libs/text!./mainLayout.html',
-'libs/text!./editingLayout.html',
-'libs/text!./diffLayout.html',
-], function(layout, vbox, tabs, mainLayoutTemplate, visualEditingLayoutTemplate, diffLayoutTemplate) {
-
-'use strict';
-
-return function(sandbox) {
-    
-    function addMainTab(title, slug, view) {
-        views.mainTabs.addTab(title, slug, view);
-    }
-    
-    var dirty = {
-        sourceEditor: false,
-        documentCanvas: false,
-        metadataEditor: false,
-    };
-    
-    var synchronizeTab = function(slug) {
-        function tabIsDirty(slug) {
-            if(slug === 'editor' && (dirty.documentCanvas || dirty.metadataEditor))
-                return true;
-            if(slug === 'sourceEditor' && dirty.sourceEditor)
-                return true;
-            return false;
-        }
-    
-        if(tabIsDirty(slug)) {
-            var reason, doc;
-            if(slug === 'sourceEditor') {
-                doc = sandbox.getModule('sourceEditor').getDocument();
-                reason = 'source_edit';
-                dirty.sourceEditor = false;
-            }
-            if(slug === 'editor') {
-                doc = dirty.documentCanvas ? sandbox.getModule('documentCanvas').getDocument() : sandbox.getModule('data').getDocument();
-                if(dirty.metadataEditor) {
-                    doc = sandbox.getModule('metadataEditor').attachMetadata(doc);
-                }
-                reason = 'edit';
-                dirty.documentCanvas = dirty.metadataEditor = false;
-            }
-            sandbox.getModule('data').commitDocument(doc, reason);
-        }
-    };
-    
-    var commands = {
-        highlightDocumentElement: function(element, origin) {
-            ///'nodeBreadCrumbs', 'nodeFamilyTree'
-            ['documentCanvas', ].forEach(function(moduleName) {
-                if(!origin || moduleName != origin)
-                    sandbox.getModule(moduleName).highlightElement(element);
-            });
-        },
-        dimDocumentElement: function(element, origin) {
-            //'nodeBreadCrumbs', 'nodeFamilyTree'
-            ['documentCanvas'].forEach(function(moduleName) {
-                if(!origin || moduleName != origin)
-                    sandbox.getModule(moduleName).dimElement(element);
-            });
-        },
-        jumpToDocumentElement: function(element) {
-            sandbox.getModule('documentCanvas').jumpToElement(element);
-        },
-        updateCurrentNodeElement: function(nodeElement) {
-            sandbox.getModule('nodePane').setNodeElement(nodeElement);
-            sandbox.getModule('nodeFamilyTree').setElement(nodeElement);
-            sandbox.getModule('nodeBreadCrumbs').setNodeElement(nodeElement);
-        },
-        updateCurrentTextElement: function(textElement) {
-            sandbox.getModule('nodeFamilyTree').setElement(textElement);
-        },
-        resetDocument: function(document, reason) {
-            var modules = [];
-            if(reason === 'source_edit')
-                modules = ['documentCanvas', 'metadataEditor'];
-            else if (reason === 'edit')
-                modules = ['sourceEditor'];
-            else if (reason === 'revert')
-                modules = ['documentCanvas', 'metadataEditor', 'sourceEditor'];
-                
-            modules.forEach(function(moduleName) {
-                sandbox.getModule(moduleName).setDocument(document);
-            });
-        }
-    };
-    
-
-    var views = {
-        mainLayout: new layout.Layout(mainLayoutTemplate),
-        mainTabs: (new tabs.View()).render(),
-        visualEditing: new layout.Layout(visualEditingLayoutTemplate),
-        visualEditingSidebar: (new tabs.View({stacked: true})).render(),
-        currentNodePaneLayout: new vbox.VBox(),
-        diffLayout: new layout.Layout(diffLayoutTemplate)
-    };
-    
-    views.visualEditing.setView('rightColumn', views.visualEditingSidebar.getAsView());
-    addMainTab('Edytor', 'editor', views.visualEditing.getAsView());
-    addMainTab(gettext('Source'), 'sourceEditor',  '');
-    addMainTab('Historia', 'history', views.diffLayout.getAsView());
-    
-    sandbox.getDOM().append(views.mainLayout.getAsView());
-    
-    views.visualEditingSidebar.addTab({icon: 'pencil'}, 'edit', views.currentNodePaneLayout.getAsView());
-
-    views.mainTabs.on('tabSelected', function(event) {
-        if(event.prevSlug) {
-            synchronizeTab(event.prevSlug);
-        }
-    });
-    
-    /* Events handling */
-    
-    var eventHandlers = {};
-     
-    eventHandlers.sourceEditor = {
-        ready: function() {
-            addMainTab(gettext('Source'), 'sourceEditor',  sandbox.getModule('sourceEditor').getView());
-            sandbox.getModule('sourceEditor').setDocument(sandbox.getModule('data').getDocument());
-        },
-        xmlChanged: function() {
-            dirty.sourceEditor = true;
-        },
-        documentSet: function() {
-            dirty.sourceEditor = false;
-        }
-    };
-    
-    eventHandlers.data = {
-        ready: function() {
-            views.mainLayout.setView('mainView', views.mainTabs.getAsView());
-            
-            _.each(['sourceEditor', 'documentCanvas', 'documentToolbar', 'nodePane', 'metadataEditor', 'nodeFamilyTree', 'nodeBreadCrumbs', 'mainBar', 'indicator', 'documentHistory', 'diffViewer'], function(moduleName) {
-                sandbox.getModule(moduleName).start();
-            });
-        },
-        documentChanged: function(document, reason) {
-            commands.resetDocument(document, reason);
-        },
-        savingStarted: function() {
-            sandbox.getModule('mainBar').setCommandEnabled('save', false);
-            sandbox.getModule('indicator').showMessage(gettext('Saving...'));
-        },
-        savingEnded: function(status) {
-            sandbox.getModule('mainBar').setCommandEnabled('save', true);
-            sandbox.getModule('indicator').clearMessage({message:'Dokument zapisany'});
-        },
-        restoringStarted: function(event) {
-            sandbox.getModule('mainBar').setCommandEnabled('save', false);
-            sandbox.getModule('indicator').showMessage(gettext('Restoring version ') + event.version + '...');
-        },
-        historyItemAdded: function(item) {
-            sandbox.getModule('documentHistory').addHistory([item], {animate: true});
-        },
-        diffFetched: function(diff) {
-            sandbox.getModule('diffViewer').setDiff(diff);
-        },
-        documentReverted: function(event) {
-            commands.resetDocument(event.document, 'revert');
-            sandbox.getModule('mainBar').setCommandEnabled('save', true);
-            sandbox.getModule('indicator').clearMessage({message:'Wersja ' + event.reverted_version + ' przywrócona'});
-            sandbox.getModule('mainBar').setVersion(event.current_version);
-        }
-    };
-    
-    eventHandlers.mainBar = {
-        ready: function() {
-            sandbox.getModule('mainBar').setVersion(sandbox.getModule('data').getDocumentVersion());
-            views.mainLayout.setView('topPanel', sandbox.getModule('mainBar').getView());
-        },
-        'cmd.save': function() {
-            synchronizeTab(views.mainTabs.getCurrentSlug());
-            sandbox.getModule('data').saveDocument();
-        }
-    };
-    
-    eventHandlers.indicator = {
-        ready: function() {
-            views.mainLayout.setView('messages', sandbox.getModule('indicator').getView());
-        }
-    };
-    
-
-    
-    eventHandlers.documentCanvas = {
-        ready: function() {
-            sandbox.getModule('documentCanvas').setDocument(sandbox.getModule('data').getDocument());
-            views.visualEditing.setView('leftColumn', sandbox.getModule('documentCanvas').getView());
-        },
-        documentSet: function() {
-            dirty.documentCanvas = false;
-        },
-        
-        currentTextElementSet: function(textElement) {
-            commands.updateCurrentTextElement(textElement);
-        },
-
-        currentNodeElementSet: function(nodeElement) {
-            commands.updateCurrentNodeElement(nodeElement);
-        },
-        
-        currentNodeElementChanged: function(nodeElement) {
-            commands.updateCurrentNodeElement(nodeElement);
-            dirty.documentCanvas = true;
-        },
-
-        contentChanged: function() {
-            dirty.documentCanvas = true;
-        },
-
-        nodeHovered: function(canvasNode) {
-            commands.highlightDocumentNode(canvasNode);
-        },
-        
-        nodeBlured: function(canvasNode) {
-            commands.dimDocumentNode(canvasNode);
-        }
-    };
-
-    eventHandlers.nodePane = {
-        ready: function() {
-            views.currentNodePaneLayout.appendView(sandbox.getModule('nodePane').getView());
-        },
-        
-        nodeElementChange: function(attr, value) {
-            sandbox.getModule('documentCanvas').modifyCurrentNodeElement(attr, value);
-        }
-    };
-    
-    eventHandlers.metadataEditor = {
-        ready: function() {
-            sandbox.getModule('metadataEditor').setDocument(sandbox.getModule('data').getDocument());
-            views.visualEditingSidebar.addTab({icon: 'info-sign'}, 'metadataEditor', sandbox.getModule('metadataEditor').getView());
-        },
-        metadataChanged: function(metadata) {
-            dirty.metadataEditor = true;
-        },
-        metadataSet: function() {
-            dirty.metadataEditor = false;
-        },
-    };
-    
-    eventHandlers.nodeFamilyTree = {
-        ready: function() {
-            views.currentNodePaneLayout.appendView(sandbox.getModule('nodeFamilyTree').getView());
-        },
-        elementEntered: function(element) {
-            commands.highlightDocumentElement(element, 'nodeFamilyTree');
-        },
-        elementLeft: function(element) {
-            commands.dimDocumentElement(element, 'nodeFamilyTree');
-        },
-        elementClicked: function(element) {
-            commands.jumpToDocumentElement(element);
-        }
-    };
-    
-    eventHandlers.documentToolbar = {
-        ready: function() {
-            views.visualEditing.setView('toolbar', sandbox.getModule('documentToolbar').getView());
-        },
-        command: function(cmd, params) {
-            sandbox.getModule('documentCanvas').command(cmd, params);
-        }
-    };
-    
-    eventHandlers.nodeBreadCrumbs = {
-        ready: function() {
-            views.visualEditing.setView('statusBar', sandbox.getModule('nodeBreadCrumbs').getView());
-        },
-        elementEntered: function(element) {
-            commands.highlightDocumentElement(element, 'nodeBreadCrumbs');
-        },
-        elementLeft: function(element) {
-            commands.dimDocumentElement(element, 'nodeBreadCrumbs');
-        },
-        elementClicked: function(element) {
-            commands.jumpToDocumentElement(element);
-        }        
-    };
-    
-    eventHandlers.documentHistory = {
-        ready: function() {
-            sandbox.getModule('documentHistory').addHistory(sandbox.getModule('data').getHistory());
-            views.diffLayout.setView('left', sandbox.getModule('documentHistory').getView());
-        },
-        compare: function(ver1, ver2) {
-            sandbox.getModule('data').fetchDiff(ver1, ver2);
-        },
-        restoreVersion: function(event) {
-            sandbox.getModule('data').restoreVersion(event);
-        },
-        displayVersion: function(event) {
-            window.open('/' + gettext('editor') + '/' + sandbox.getModule('data').getDocumentId() + '?version=' + event.version, _.uniqueId());
-        }
-    };
-    
-    eventHandlers.diffViewer = {
-        ready: function() {
-            views.diffLayout.setView('right', sandbox.getModule('diffViewer').getView());
-        }
-    };
-    
-    /* api */
-    
-    return {
-        start: function() {
-            sandbox.getModule('data').start();
-        },
-        handleEvent: function(moduleName, eventName, args) {
-            if('')
-                wysiwigHandler.handleEvent(moduleName, eventName, args);
-            else if(eventHandlers[moduleName] && eventHandlers[moduleName][eventName]) {
-                eventHandlers[moduleName][eventName].apply(eventHandlers, args);
-            }
-        }
-    };
-};
-
-});
\ No newline at end of file
diff --git a/modules/rng/rng.less b/modules/rng/rng.less
deleted file mode 100644 (file)
index 196be1f..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-@import 'mainLayout.less';
-@import 'editingLayout.less';
-@import 'diffLayout.less';
\ No newline at end of file
diff --git a/modules/sourceEditor/sourceEditor.js b/modules/sourceEditor/sourceEditor.js
deleted file mode 100644 (file)
index e88e5e1..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-define(function() {
-
-'use strict';
-
-return function(sandbox) {
-
-    var view = $(sandbox.getTemplate('main')());
-    
-    var editor = ace.edit(view.find('#rng-sourceEditor-editor')[0]),
-        session = editor.getSession();
-    editor.setTheme("ace/theme/chrome");
-    session.setMode("ace/mode/xml")
-    session.setUseWrapMode(true);
-    
-    $('textarea', view).on('keyup', function() {
-        sandbox.publish('xmlChanged');
-    });
-    
-    editor.getSession().on('change', function() {
-        sandbox.publish('xmlChanged');
-    });
-    return {
-        start: function() {
-            sandbox.publish('ready');
-        },
-        getView: function() {
-            return view;
-        },
-        setDocument: function(document) {
-            editor.setValue(document);
-            editor.gotoLine(0);
-            sandbox.publish('documentSet');
-        },
-        getDocument: function() {
-            return editor.getValue();
-        }
-    };
-};
-
-});
\ No newline at end of file
diff --git a/modules/sourceEditor/sourceEditor.less b/modules/sourceEditor/sourceEditor.less
deleted file mode 100644 (file)
index 003ee6e..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#rng-sourceEditor-editor {
-    position: absolute;
-    top: 0;
-    bottom: 0;
-    left: 0;
-    right: 0;
-}
\ No newline at end of file
diff --git a/rng.js b/rng.js
deleted file mode 100644 (file)
index 04a8d4f..0000000
--- a/rng.js
+++ /dev/null
@@ -1,14 +0,0 @@
-define(function() {
-    
-'use strict';
-    
-return {
-    modules: {}, 
-    initModules: ['rng'],
-    permissions: {
-        'skelton': ['getDOM'],
-        'rng': ['getModule', 'handleEvents', 'getDOM']
-    }
-}
-
-});
\ No newline at end of file
diff --git a/src/editor/entrypoint.js b/src/editor/entrypoint.js
new file mode 100644 (file)
index 0000000..5e546ef
--- /dev/null
@@ -0,0 +1,56 @@
+(function() {
+    'use strict';
+    
+    requirejs.config({
+        baseUrl: '/static/editor/src/editor',
+        
+        paths: {
+            'fnpjs': '../fnpjs',
+            'libs': '../../libs'
+        },
+
+        map: {
+            '*':
+                {
+                    'libs/jquery': '../../libs/jquery-1.9.1.min',
+                    'libs/underscore': '../../libs/underscore-min',
+                    'libs/bootstrap': '../../libs/bootstrap/js/bootstrap.min',
+                    'libs/backbone': '../../libs/backbone-min',
+
+                }
+        },
+
+        shim: {
+            '../../libs/jquery-1.9.1.min': {
+                exports: '$',
+            },
+            '../../libs/underscore-min': {
+                exports: '_'
+            },
+            '../../libs/bootstrap/js/bootstrap.min': {
+                deps: ['libs/jquery']
+            },
+            '../../libs/backbone-min': {
+                exports: 'Backbone',
+                deps: ['libs/jquery', 'libs/underscore']
+            }
+        }
+
+    });
+    
+    requirejs([
+        'libs/jquery',
+        '../fnpjs/runner',
+        'rng',
+        './modules',
+        'libs/bootstrap'
+    ], function($, runner, rng, modules) {
+        $(function() {
+            var app = new runner.Runner(rng, modules);
+            app.setBootstrappedData('data', RNG_BOOTSTRAP_DATA);
+            app.start({rootSelector:'#editor_root'});
+        });
+    });
+
+
+})();
\ No newline at end of file
diff --git a/src/editor/modules.js b/src/editor/modules.js
new file mode 100644 (file)
index 0000000..bb4d439
--- /dev/null
@@ -0,0 +1,28 @@
+define(function(require) {
+    /*
+       Each module must be required explicitly by apropriate 'require' function call
+       in order for requirejs optimizer to work.
+    */
+    
+    'use strict';
+    
+    return {
+        data: require('modules/data/data'),
+        rng: require('modules/rng/rng'),
+        mainBar: require('modules/mainBar/mainBar'),
+        indicator: require('modules/indicator/indicator'),
+        
+        sourceEditor: require('modules/sourceEditor/sourceEditor'),
+        
+        documentCanvas: require('modules/documentCanvas/documentCanvas'),
+        documentToolbar: require('modules/documentToolbar/documentToolbar'),
+        nodePane: require('modules/nodePane/nodePane'),
+        metadataEditor: require('modules/metadataEditor/metadataEditor'),
+        nodeFamilyTree: require('modules/nodeFamilyTree/nodeFamilyTree'),
+        nodeBreadCrumbs: require('modules/nodeBreadCrumbs/nodeBreadCrumbs'),
+        
+        documentHistory: require('modules/documentHistory/documentHistory'),
+        diffViewer: require('modules/diffViewer/diffViewer')
+        
+    }
+});
\ No newline at end of file
diff --git a/src/editor/modules/data/data.js b/src/editor/modules/data/data.js
new file mode 100644 (file)
index 0000000..9b2f163
--- /dev/null
@@ -0,0 +1,133 @@
+define(['./saveDialog'], function(saveDialog) {
+
+'use strict';
+
+return function(sandbox) {
+
+    var doc = sandbox.getBootstrappedData().document;
+    var document_id = sandbox.getBootstrappedData().document_id;
+    var document_version = sandbox.getBootstrappedData().version;
+    var history = sandbox.getBootstrappedData().history;
+
+    
+    if(doc === '') {
+        doc = '<section\n\
+        xmlns="http://nowoczesnapolska.org.pl/sst#"\n\
+        xmlns:xlink="http://www.w3.org/1999/xlink"\n\
+        xmlns:dc="http://purl.org/dc/elements/1.1/"\n\
+        xmlns:dcterms="http://purl.org/dc/terms/"\n\
+    >\n\
+        <metadata>\n\
+        </metadata>\n\
+        <div class="p"></div>\n\
+    </section>';
+    }
+    
+    
+    function readCookie(name) {
+        var nameEQ = escape(name) + "=";
+        var ca = document.cookie.split(';');
+        for (var i = 0; i < ca.length; i++) {
+            var c = ca[i];
+            while (c.charAt(0) == ' ') c = c.substring(1, c.length);
+            if (c.indexOf(nameEQ) === 0) return unescape(c.substring(nameEQ.length, c.length));
+        }
+        return null;
+    }
+    
+    $.ajaxSetup({
+        crossDomain: false,
+        beforeSend: function(xhr, settings) {
+            if (!(/^(GET|HEAD|OPTIONS|TRACE)$/.test(settings.type))) {
+                xhr.setRequestHeader("X-CSRFToken", readCookie('csrftoken'));
+            }
+        }
+    });
+    
+    var reloadHistory = function() {
+        $.ajax({
+            method: 'get',
+            url: '/' + gettext('editor') + '/' + document_id + '/history',
+            success: function(data) {
+                history = data; 
+                sandbox.publish('historyItemAdded', data.slice(-1)[0]);
+            },
+        });
+    };
+    
+    return {
+        start: function() {
+            sandbox.publish('ready');
+        },
+        getDocument: function() {
+            return doc;
+        },
+        commitDocument: function(newDocument, reason) {
+            doc = newDocument;
+            sandbox.publish('documentChanged', doc, reason);
+        },
+        saveDocument: function() {
+
+            var dialog = saveDialog.create();
+            dialog.on('save', function(event) {
+                sandbox.publish('savingStarted');
+                dialog.toggleButtons(false);
+                $.ajax({
+                    method: 'post',
+                    url: '/' + gettext('editor') + '/' + document_id,
+                    data: JSON.stringify({document:doc, description: event.data.description}),
+                    success: function() {
+                        event.success();
+                        sandbox.publish('savingEnded', 'success');
+                        reloadHistory();
+                    },
+                    error: function() {event.error(); sandbox.publish('savingEnded', 'error');}
+                });
+                console.log('save');
+            });
+            dialog.on('cancel', function() {
+            });
+            dialog.show();
+            
+
+        },
+        getHistory: function() {
+            return history;
+        },
+        fetchDiff: function(ver1, ver2) {
+            $.ajax({
+                method: 'get',
+                url: '/' + gettext('editor') + '/' + document_id + '/diff',
+                data: {from: ver1, to: ver2},
+                success: function(data) {
+                    sandbox.publish('diffFetched', {table: data, ver1: ver1, ver2: ver2});
+                },
+            });
+        },
+        restoreVersion: function(options) {
+            if(options.version && options.description) {
+                sandbox.publish('restoringStarted', {version: options.version});
+                $.ajax({
+                    method: 'post',
+                    dataType: 'json',
+                    url: '/' + gettext('editor') + '/' + document_id + '/revert',
+                    data: JSON.stringify(options),
+                    success: function(data) {
+                        doc = data.document;
+                        document_version = data.version;
+                        reloadHistory();
+                        sandbox.publish('documentReverted', data);
+                    },
+                }); 
+            }
+        },
+        getDocumentId: function() {
+            return document_id;
+        },
+        getDocumentVersion: function() {
+            return document_version;
+        }
+    };
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/data/data.less b/src/editor/modules/data/data.less
new file mode 100644 (file)
index 0000000..1beb090
--- /dev/null
@@ -0,0 +1 @@
+@import 'saveDialog.less';
\ No newline at end of file
diff --git a/src/editor/modules/data/saveDialog.html b/src/editor/modules/data/saveDialog.html
new file mode 100644 (file)
index 0000000..0846910
--- /dev/null
@@ -0,0 +1,14 @@
+<div class="rng-module-data-saveDialog modal hide static">
+    <div class="modal-header">
+        <button type="button" class="close">&times;</button>
+        <h1>Zapisz dokument</h1>
+    </div>
+    <div class="modal-body">
+        <label>Opisz swoje zmiany</label>
+        <textarea rows="5"></textarea>
+    </div>
+    <div class="modal-footer">
+        <a href="#" class="btn btn-info btn-mini save-btn">Zapisz</a>
+        <a href="#" class="btn btn-danger btn-mini cancel-btn">Anuluj</a>
+    </div>
+</div>
\ No newline at end of file
diff --git a/src/editor/modules/data/saveDialog.js b/src/editor/modules/data/saveDialog.js
new file mode 100644 (file)
index 0000000..90832e6
--- /dev/null
@@ -0,0 +1,56 @@
+define([
+'libs/text!./saveDialog.html',
+'libs/underscore',
+'libs/backbone',
+'libs/jquery'
+], function(saveDialogTemplate, _, Backbone, $) {
+
+    var DialogView = Backbone.View.extend({
+        template: _.template(saveDialogTemplate),
+        events: {
+            'click .save-btn': 'onSave',
+            'click .cancel-btn': 'close',
+            'click .close': 'close'
+        },
+        initialize: function() {
+            _.bindAll(this);
+            this.actionsDisabled = false;
+        },
+        show: function() {           
+            this.setElement(this.template());
+            this.$el.modal({backdrop: 'static'});
+            this.$el.modal('show');
+            this.$('textarea').focus();
+
+        },
+        onSave: function(e) {
+            e.preventDefault();
+            var view = this;
+            this.trigger('save', {
+                data: {description: view.$el.find('textarea').val()},
+                success: function() { view.actionsDisabled = false; view.close(); },
+                error: function() { view.actionsDisabled = false; view.close(); },
+            });
+        },
+        close: function(e) {
+            if(e)
+                e.preventDefault();
+            if(!this.actionsDisabled) {
+                this.$el.modal('hide');
+                this.$el.remove();
+            }
+        },
+        toggleButtons: function(toggle) {
+            this.$('.btn, button').toggleClass('disabled', !toggle);
+            this.$('textarea').attr('disabled', !toggle);
+            this.actionsDisabled = !toggle;
+        }
+    });
+
+    return {
+        create: function() {
+            return new DialogView();
+        }
+    };
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/data/saveDialog.less b/src/editor/modules/data/saveDialog.less
new file mode 100644 (file)
index 0000000..e863e93
--- /dev/null
@@ -0,0 +1,19 @@
+.rng-module-data-saveDialog {
+    textarea {
+        padding: 3px 3px;
+        margin: 5px auto;
+        width: 95%;
+        display: block;
+    }
+    
+    h1, label {
+        font-size: 12px;
+        line-height: 12px;
+
+    }
+    
+    h1 {
+        margin: 2px 5px;
+        font-weight: bold;
+    }
+}
\ No newline at end of file
diff --git a/src/editor/modules/diffViewer/diff.html b/src/editor/modules/diffViewer/diff.html
new file mode 100644 (file)
index 0000000..cbf9b54
--- /dev/null
@@ -0,0 +1 @@
+<div class="rng-module-diffViewer-diffView"></div>
\ No newline at end of file
diff --git a/src/editor/modules/diffViewer/diffViewer.js b/src/editor/modules/diffViewer/diffViewer.js
new file mode 100644 (file)
index 0000000..e792545
--- /dev/null
@@ -0,0 +1,38 @@
+define([
+'libs/jquery',
+'libs/underscore',
+'views/tabs/tabs',
+'libs/text!./diff.html'
+], function($, _, tabs, diffTemplateSrc) {
+
+'use strict';
+
+return function(sandbox) {
+    
+    var dom = $('<div>').addClass('rng-module-diffViewer');
+    var tabsView = (new tabs.View({position: 'right'})).render();
+    dom.append(tabsView.getAsView());
+    
+    var DiffView = function() {
+        this.dom = $(diffTemplateSrc);
+    };
+    
+    DiffView.prototype.setTable = function(table) {
+        this.dom.append(table);
+    };
+    
+
+    return {
+        start: function() {sandbox.publish('ready');},
+        getView: function() {return dom;},
+        setDiff: function(diff) {
+            var diffView = new DiffView();
+            diffView.setTable(diff.table);
+            var slug = diff.ver1 + '-' + diff.ver2;
+            tabsView.addTab(diff.ver1 + '->' + diff.ver2, slug, diffView.dom);
+            tabsView.selectTab(slug);
+        }
+    };
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/diffViewer/diffViewer.less b/src/editor/modules/diffViewer/diffViewer.less
new file mode 100644 (file)
index 0000000..34ab240
--- /dev/null
@@ -0,0 +1,77 @@
+.rng-module-diffViewer {
+    .nav-tabs > li > a {
+        min-width: 0;
+        font-size: 0.9em;
+        padding: 4px 6px;
+    }
+    
+    .tab-content {
+        position: absolute;
+        top:0;
+        bottom:0;
+        left:0;
+        right:60px;
+        overflow-y: scroll;
+        &::-webkit-scrollbar {
+            .rng-mixin-scrollbar;
+        }
+        &::-webkit-scrollbar-track {
+            .rng-mixin-scrollbar-track;
+        }
+        &::-webkit-scrollbar-thumb {
+            .rng-mixin-scrollbar-thumb;
+        }
+        &::-webkit-scrollbar-thumb:window-inactive {
+            .rng-mixin-scrollbar-thumb-window-inactive;
+        }
+    }
+    
+    .diff_table {
+        border-width: 1px 0 1px 1px;
+        border-style: solid;
+        border-color: #ddd;
+        empty-cells: show;
+        border-spacing: 0px;
+    }
+
+    .diff_table td {
+        border-width: 0px 1px 1px 0px;
+        border-style: dotted;
+        border-color: grey;
+        font-size: 10px;
+        line-height: 20px;
+        font-family: monospace;
+        padding: 0px;
+        white-space: pre-line;
+        /*word-wrap:break-word;
+         word-break:break-all; */
+    }
+
+    .diff_table th {
+        border-width: 0px 1px 1px 0px;
+        border-style: solid;
+        border-color: #ddd;
+        background: #e5ffe5;
+    }
+
+    .diff_table tr.change {
+        background-color: #dcdcdc;
+    }
+
+    .diff_mark {
+        display: inline-block;
+        padding: 2px;
+    }
+
+    .diff_mark_removed {
+        background-color: #ff9c94;
+    }
+
+    .diff_mark_added {
+        background-color: #90ee90;
+    }
+
+    .diff_mark_changed {
+        background-color: yellow;
+    }
+}
\ No newline at end of file
diff --git a/src/editor/modules/documentCanvas/canvas/canvas.js b/src/editor/modules/documentCanvas/canvas/canvas.js
new file mode 100644 (file)
index 0000000..d8db2bc
--- /dev/null
@@ -0,0 +1,657 @@
+define([
+'libs/jquery',
+'libs/underscore',
+'libs/backbone',
+'modules/documentCanvas/canvas/documentElement',
+'modules/documentCanvas/canvas/keyboard',
+'modules/documentCanvas/canvas/utils'
+], function($, _, Backbone, documentElement, keyboard, utils) {
+    
+'use strict';
+
+var Canvas = function(wlxml, publisher) {
+    this.eventBus = _.extend({}, Backbone.Events);
+    this.loadWlxml(wlxml);
+    this.publisher = publisher ? publisher : function() {};
+};
+
+$.extend(Canvas.prototype, {
+
+    loadWlxml: function(wlxml) {
+        var d = wlxml ? $($.trim(wlxml)) : null;
+        if(d) {
+            this.wrapper = $('<div>').addClass('canvas-wrapper').attr('contenteditable', true);
+            this.wrapper.append(d);
+            var canvas = this;
+
+            this.wrapper.find('*').replaceWith(function() {
+                var currentTag = $(this);
+                if(currentTag.attr('wlxml-tag'))
+                    return;
+
+                var meta = {}, others = {};
+                for(var i = 0; i < this.attributes.length; i++) {
+                    var attr = this.attributes[i];
+                    if(attr.name.substr(0, 5) === 'meta-')
+                        meta[attr.name.substr(5)] = attr.value;
+                    else if(attr.name !== 'class')
+                        others[attr.name] = attr.value;
+                }
+
+                var element = canvas.createNodeElement({
+                    tag: currentTag.prop('tagName').toLowerCase(),
+                    klass: currentTag.attr('class'),
+                    meta: meta,
+                    others: others,
+                    rawChildren: currentTag.contents(),
+                    prepopulateOnEmpty: true
+                });
+
+                ['orig-before', 'orig-after', 'orig-begin', 'orig-end'].forEach(function(attr) {
+                    element.data(attr, '');
+                });
+                return element.dom();
+            });
+
+            var FIRST_CONTENT_INDEX = 0;
+
+            // @@ TODO - refactor!
+            var getNode = function(element) {
+                return element.children('[document-element-content]');
+            }
+
+            this.wrapper.find(':not(iframe)').addBack().contents()
+                .filter(function() {return this.nodeType === Node.TEXT_NODE})
+                .each(function() {
+
+                    // TODO: use DocumentElement API
+
+                    var el = $(this),
+                        text = {original: el.text(), trimmed: $.trim(el.text())},
+                        elParent = el.parent(),
+                        hasSpanParent = elParent.attr('wlxml-tag') === 'span',
+                        hasSpanBefore = el.prev().length > 0  && getNode($(el.prev()[0])).attr('wlxml-tag') === 'span',
+                        hasSpanAfter = el.next().length > 0 && getNode($(el.next()[0])).attr('wlxml-tag') === 'span';
+
+                    if(el.parent().hasClass('canvas-widget') || elParent.attr('document-text-element') !== undefined)
+                        return true; // continue
+
+                    var addInfo = function(toAdd, where) {
+                        var parentContents = elParent.contents(),
+                            idx = parentContents.index(el[0]),
+                            prev = idx > FIRST_CONTENT_INDEX ? parentContents[idx-1] : null,
+                            next = idx < parentContents.length - 1 ? parentContents[idx+1] : null,
+                            target, key;
+
+                        if(where === 'above') {
+                            target = prev ? $(prev) : elParent.parent();
+                            key = prev ? 'orig-after' : 'orig-begin';
+                        } else if(where === 'below') {
+                            target = next ? $(next) : elParent.parent();
+                            key = next ? 'orig-before' : 'orig-end';
+                        } else { throw new Object;}
+
+                        target.data(key, toAdd);
+                    }
+
+                    text.transformed = text.trimmed;
+                    
+                    if(hasSpanParent || hasSpanBefore || hasSpanAfter) {
+                        var startSpace = /\s/g.test(text.original.substr(0,1)),
+                            endSpace = /\s/g.test(text.original.substr(-1)) && text.original.length > 1;
+                        text.transformed = (startSpace && (hasSpanParent || hasSpanBefore) ? ' ' : '')
+                                    + text.trimmed
+                                    + (endSpace && (hasSpanParent || hasSpanAfter) ? ' ' : '');
+                    } else {
+                        if(text.trimmed.length === 0 && text.original.length > 0 && elParent.contents().length === 1)
+                            text.transformed = ' ';
+                    }
+
+                    if(!text.transformed) {
+                        addInfo(text.original, 'below');
+                        el.remove();
+                        return true; // continue
+                    }
+
+                    if(text.transformed !== text.original) {
+                        if(!text.trimmed) {
+                            addInfo(text.original, 'below');
+                        } else {
+                            var startingMatch = text.original.match(/^\s+/g),
+                                endingMatch = text.original.match(/\s+$/g),
+                                startingWhiteSpace = startingMatch ? startingMatch[0] : null,
+                                endingWhiteSpace = endingMatch ? endingMatch[0] : null;
+
+                            if(endingWhiteSpace) {
+                                if(text.transformed[text.transformed.length - 1] === ' ' && endingWhiteSpace[0] === ' ')
+                                    endingWhiteSpace = endingWhiteSpace.substr(1);
+                                addInfo(endingWhiteSpace, 'below');
+                            }
+
+                            if(startingWhiteSpace) {
+                                if(text.transformed[0] === ' ' && startingWhiteSpace[startingWhiteSpace.length-1] === ' ')
+                                    startingWhiteSpace = startingWhiteSpace.substr(0, startingWhiteSpace.length -1);
+                                addInfo(startingWhiteSpace, 'above');
+                            }
+                        }
+                    }
+
+                    var element = documentElement.DocumentTextElement.create({text: text.transformed});
+                    el.replaceWith(element.dom());
+                });
+            
+            this.d = this.wrapper.children(0);
+
+            this.wrapper.on('keyup keydown keypress', function(e) {
+                keyboard.handleKey(e, this);
+            }.bind(this));
+
+            this.wrapper.on('click', '[document-node-element], [document-text-element]', function(e) {
+                e.stopPropagation();
+                canvas.setCurrentElement(canvas.getDocumentElement(e.currentTarget), {caretTo: false});
+            });
+
+            var observer = new MutationObserver(function(mutations) {
+                mutations.forEach(function(mutation) {
+                    if(documentElement.DocumentTextElement.isContentContainer(mutation.target)) {
+                        observer.disconnect();
+                        if(mutation.target.data === '')
+                            mutation.target.data = utils.unicode.ZWS;
+                        else if(mutation.oldValue === utils.unicode.ZWS) {
+                            mutation.target.data = mutation.target.data.replace(utils.unicode.ZWS, '');
+                            canvas._moveCaretToTextElement(canvas.getDocumentElement(mutation.target), 'end');
+                        }
+                        observer.observe(canvas.d[0], config);
+                        canvas.publisher('contentChanged');
+                    }
+                });
+            });
+            var config = { attributes: false, childList: false, characterData: true, subtree: true, characterDataOldValue: true};
+            observer.observe(this.d[0], config);
+
+
+            this.wrapper.on('mouseover', '[document-node-element], [document-text-element]', function(e) {
+                var el = canvas.getDocumentElement(e.currentTarget);
+                if(!el)
+                    return;
+                e.stopPropagation();
+                if(el instanceof documentElement.DocumentTextElement)
+                    el = el.parent();
+                el.toggleLabel(true);
+            });
+            this.wrapper.on('mouseout', '[document-node-element], [document-text-element]', function(e) {
+                var el = canvas.getDocumentElement(e.currentTarget);
+                if(!el)
+                    return;
+                e.stopPropagation();
+                if(el instanceof documentElement.DocumentTextElement)
+                    el = el.parent();
+                el.toggleLabel(false);
+            });
+
+            this.eventBus.on('elementToggled', function(toggle, element) {
+                if(!toggle) {
+                    canvas.setCurrentElement(element.getPreviousTextElement());
+                }
+            })
+
+        } else {
+            this.d = null;
+        }
+    },
+
+    view: function() {
+        return this.wrapper;
+    },
+
+    doc: function() {
+        if(this.d === null)
+            return null;
+        return documentElement.DocumentNodeElement.fromHTMLElement(this.d.get(0), this); //{wlxmlTag: this.d.prop('tagName')};
+    },
+
+    createNodeElement: function(params) {
+        return documentElement.DocumentNodeElement.create(params, this);
+    },
+
+    wrapText: function(params) {
+        params = _.extend({textNodeIdx: 0}, params);
+        if(typeof params.textNodeIdx === 'number')
+            params.textNodeIdx = [params.textNodeIdx];
+        
+        var childrenInside = params.inside.children(),
+            idx1 = Math.min.apply(Math, params.textNodeIdx),
+            idx2 = Math.max.apply(Math, params.textNodeIdx),
+            textNode1 = childrenInside[idx1],
+            textNode2 = childrenInside[idx2],
+            sameNode = textNode1.sameNode(textNode2),
+            prefixOutside = textNode1.getText().substr(0, params.offsetStart),
+            prefixInside = textNode1.getText().substr(params.offsetStart),
+            suffixInside = textNode2.getText().substr(0, params.offsetEnd),
+            suffixOutside = textNode2.getText().substr(params.offsetEnd)
+        ;
+        
+        var wrapperElement = this.createNodeElement({tag: params._with.tag, klass: params._with.klass});
+        textNode1.after(wrapperElement);
+        textNode1.detach();
+        
+        if(prefixOutside.length > 0)
+            wrapperElement.before({text:prefixOutside});
+        if(sameNode) {
+            var core = textNode1.getText().substr(params.offsetStart, params.offsetEnd - params.offsetStart);
+            wrapperElement.append({text: core});
+        } else {
+            textNode2.detach();
+            if(prefixInside.length > 0)
+                wrapperElement.append({text: prefixInside});
+            for(var i = idx1 + 1; i < idx2; i++) {
+                wrapperElement.append(childrenInside[i]);
+            }
+            if(suffixInside.length > 0)
+                wrapperElement.append({text: suffixInside});
+        }
+        if(suffixOutside.length > 0)
+            wrapperElement.after({text: suffixOutside});
+        return wrapperElement;
+    },
+
+    wrapElements: function(params) {
+        if(!(params.element1.parent().sameNode(params.element2.parent())))
+            return;
+
+        var parent = params.element1.parent(),
+            parentChildren = parent.children(),
+            wrapper = this.createNodeElement({
+                tag: params._with.tag,
+                klass: params._with.klass}),
+            idx1 = parent.childIndex(params.element1),
+            idx2 = parent.childIndex(params.element2);
+
+        if(idx1 > idx2) {
+            var tmp = idx1;
+            idx1 = idx2;
+            idx2 = tmp;
+        }
+
+        var insertingMethod, insertingTarget;
+        if(idx1 === 0) {
+            insertingMethod = 'prepend';
+            insertingTarget = parent;
+        } else {
+            insertingMethod = 'after';
+            insertingTarget = parentChildren[idx1-1];
+        }
+
+        for(var i = idx1; i <= idx2; i++) {
+            wrapper.append(parentChildren[i].detach());
+        }
+
+        insertingTarget[insertingMethod](wrapper);
+        return wrapper;
+    },
+
+    getSiblingParents: function(params) {
+        var parents1 = [params.element1].concat(params.element1.parents()).reverse(),
+            parents2 = [params.element2].concat(params.element2.parents()).reverse(),
+            noSiblingParents = null;
+
+        if(parents1.length === 0 || parents2.length === 0 || !(parents1[0].sameNode(parents2[0])))
+            return noSiblingParents;
+
+        var i;
+        for(i = 0; i < Math.min(parents1.length, parents2.length); i++) {
+            if(parents1[i].sameNode(parents2[i]))
+                continue;
+            break;
+        }
+        return {element1: parents1[i], element2: parents2[i]};
+    },
+
+    getDocumentElement: function(from) {
+        if(from instanceof HTMLElement || from instanceof Text) {
+           return documentElement.DocumentElement.fromHTMLElement(from, this);
+        }
+    },
+    getCursor: function() {
+        return new Cursor(this);
+    },
+
+    list: {},
+
+    
+    getCurrentNodeElement: function() {
+        return this.getDocumentElement(this.wrapper.find('.current-node-element').parent()[0]);
+    },
+
+    getCurrentTextElement: function() {
+        return this.getDocumentElement(this.wrapper.find('.current-text-element')[0]);
+    },
+
+
+
+    setCurrentElement: function(element, params) {
+        params = _.extend({caretTo: 'end'}, params);
+        var findFirstDirectTextChild = function(e, nodeToLand) {
+            var byBrowser = this.getCursor().getPosition().element;
+            if(byBrowser && byBrowser.parent().sameNode(nodeToLand))
+                return byBrowser;
+            var children = e.children();
+            for(var i = 0; i < children.length; i++) {
+                if(children[i] instanceof documentElement.DocumentTextElement)
+                    return children[i];
+            }
+            return null;
+        }.bind(this);
+        var _markAsCurrent = function(element) {
+            if(element instanceof documentElement.DocumentTextElement) {
+                this.wrapper.find('.current-text-element').removeClass('current-text-element');
+                element.dom().addClass('current-text-element');
+            } else {
+                this.wrapper.find('.current-node-element').removeClass('current-node-element')
+                element._container().addClass('current-node-element');
+                this.publisher('currentElementChanged', element);
+            }
+        }.bind(this);
+
+
+        var isTextElement = element instanceof documentElement.DocumentTextElement,
+            nodeElementToLand = isTextElement ? element.parent() : element,
+            textElementToLand = isTextElement ? element : findFirstDirectTextChild(element, nodeElementToLand),
+            currentTextElement = this.getCurrentTextElement(),
+            currentNodeElement = this.getCurrentNodeElement();
+
+        if(currentTextElement && !(currentTextElement.sameNode(textElementToLand)))
+            this.wrapper.find('.current-text-element').removeClass('current-text-element');
+
+        if(textElementToLand) {
+            _markAsCurrent(textElementToLand);
+            if(params.caretTo || !textElementToLand.sameNode(this.getCursor().getPosition().element))
+                this._moveCaretToTextElement(textElementToLand, params.caretTo); // as method on element?
+            if(!(textElementToLand.sameNode(currentTextElement)))
+                this.publisher('currentTextElementSet', textElementToLand);
+        } else {
+            document.getSelection().removeAllRanges();
+        }
+
+        if(!(currentNodeElement && currentNodeElement.sameNode(nodeElementToLand))) {
+            _markAsCurrent(nodeElementToLand);
+
+            this.publisher('currentNodeElementSet', nodeElementToLand);
+        }
+    },
+
+    _moveCaretToTextElement: function(element, where) {
+        var range = document.createRange(),
+            node = element.dom().contents()[0];
+
+        if(typeof where !== 'number') {
+            range.selectNodeContents(node);
+        } else {
+            range.setStart(node, where);
+        }
+        
+        var collapseArg = true;
+        if(where === 'end')
+            collapseArg = false;
+        range.collapse(collapseArg);
+        
+        var selection = document.getSelection();
+
+        selection.removeAllRanges();
+        selection.addRange(range);
+        this.wrapper.focus(); // FF requires this for caret to be put where range colllapses, Chrome doesn't.
+    },
+
+    setCursorPosition: function(position) {
+        if(position.element)
+            this._moveCaretToTextElement(position.element, position.offset);
+    },
+
+    toXML: function() {
+        var parent = $('<div>'),
+            parts = this.doc().toXML(0);
+        parent.append(parts);
+        return parent.html();
+    }
+});
+
+$.extend(Canvas.prototype.list, {
+    create: function(params) {
+        if(!(params.element1.parent().sameNode(params.element2.parent())))
+            return false;
+            
+        var parent = params.element1.parent(),
+            canvas = params.element1.canvas;
+        
+        if(parent.childIndex(params.element1) > parent.childIndex(params.element2)) {
+            var tmp = params.element1;
+            params.element1 = params.element2;
+            params.element2 = tmp;
+        }
+        
+        var elementsToWrap = [];
+        
+        var place = 'before';
+        parent.children().some(function(element) {
+            var _e = element;
+            if(element.sameNode(params.element1))
+                place = 'inside';
+            if(place === 'inside') {
+                if(element instanceof documentElement.DocumentTextElement) {
+                    element = element.wrapWithNodeElement({tag: 'div', klass: 'list.item'});
+                    if(element.children()[0].sameNode(params.element1))
+                        params.element1 = element;
+                }
+                element.setWlxmlClass('item');
+                elementsToWrap.push(element);
+            }
+            if(_e.sameNode(params.element2))
+                return true;
+        });
+        
+        var listElement = canvas.createNodeElement({tag: 'div', klass: 'list-items' + (params.type === 'enum' ? '-enum' : '')});
+        var toret;
+        if(parent.is('list')) {
+            var item = listElement.wrapWithNodeElement({tag: 'div', klass: 'item'});
+            item.exec('toggleBullet', false);
+            toret = listElement.parent();
+        } else {
+            toret = listElement;
+        }  
+        
+        params.element1.before(toret);
+        
+        elementsToWrap.forEach(function(element) {
+            element.detach();
+            listElement.append(element);
+        });
+    },
+    extractItems: function(params) {
+        params = _.extend({merge: true}, params);
+        var list = params.element1.parent();
+        if(!list.is('list') || !(list.sameNode(params.element2.parent())))
+            return false;
+
+        var idx1 = list.childIndex(params.element1),
+            idx2 = list.childIndex(params.element2),
+            precedingItems = [],
+            extractedItems = [],
+            succeedingItems = [],
+            items = list.children(),
+            listIsNested = list.parent().getWlxmlClass() === 'item',
+            canvas = params.element1.canvas,
+            i;
+
+        if(idx1 > idx2) {
+            var tmp = idx1; idx1 = idx2; idx2 = tmp;
+        }
+
+        items.forEach(function(item, idx) {
+            if(idx < idx1)
+                precedingItems.push(item);
+            else if(idx >= idx1 && idx <= idx2) {
+                extractedItems.push(item);
+            }
+            else {
+                succeedingItems.push(item);
+            }
+        });
+
+        var reference = listIsNested ? list.parent() : list;
+        if(succeedingItems.length === 0) {
+            var reference_orig = reference;
+            extractedItems.forEach(function(item) {
+                reference.after(item);
+                reference = item;
+                if(!listIsNested)
+                    item.setWlxmlClass(null);
+            });
+            if(precedingItems.length === 0)
+                reference_orig.detach();
+        } else if(precedingItems.length === 0) {
+            extractedItems.forEach(function(item) {
+                reference.before(item);
+                if(!listIsNested)
+                    item.setWlxmlClass(null);
+            });
+        } else {
+            extractedItems.forEach(function(item) {
+                reference.after(item);
+                if(!listIsNested)
+                    item.setWlxmlClass(null);
+                reference = item;
+            });
+            var secondList = canvas.createNodeElement({tag: 'div', klass:'list-items'}),
+                toAdd = secondList;
+            
+            if(listIsNested) {
+                toAdd = secondList.wrapWithNodeElement({tag: 'div', klass:'item'});
+            }
+            succeedingItems.forEach(function(item) {
+                secondList.append(item);
+            });
+
+            reference.after(toAdd);
+        }
+        if(!params.merge && listIsNested) {
+            return this.extractItems({element1: extractedItems[0], element2: extractedItems[extractedItems.length-1]});
+        }
+        return true;
+    },
+    areItemsOfTheSameList: function(params) {
+        var e1 = params.element1,
+            e2 = params.element2;
+        return e1.parent().sameNode(e2.parent())
+            && e1.parent().is('list');
+    }
+});
+
+
+var Cursor = function(canvas) {
+    this.canvas = canvas;
+};
+
+$.extend(Cursor.prototype, {
+    isSelecting: function() {
+        var selection = window.getSelection();
+        return !selection.isCollapsed;
+    },
+    isSelectingWithinElement: function() {
+        return this.isSelecting() && this.getSelectionStart().element.sameNode(this.getSelectionEnd().element);
+    },
+    isSelectingSiblings: function() {
+        return this.isSelecting() && this.getSelectionStart().element.parent().sameNode(this.getSelectionEnd().element.parent());
+    },
+    getPosition: function() {
+        return this.getSelectionAnchor();
+    },
+    getSelectionStart: function() {
+        return this.getSelectionBoundry('start');
+    },
+    getSelectionEnd: function() {
+        return this.getSelectionBoundry('end');
+    },
+    getSelectionAnchor: function() {
+        return this.getSelectionBoundry('anchor');
+    },
+    getSelectionFocus: function() {
+        return this.getSelectionBoundry('focus');
+    },
+    getSelectionBoundry: function(which) {
+        var selection = window.getSelection(),
+            anchorElement = this.canvas.getDocumentElement(selection.anchorNode),
+            focusElement = this.canvas.getDocumentElement(selection.focusNode);
+        
+        if((!anchorElement) || (anchorElement instanceof documentElement.DocumentNodeElement) || (!focusElement) || focusElement instanceof documentElement.DocumentNodeElement)
+            return {};
+
+        if(which === 'anchor') {
+            return {
+                element: anchorElement,
+                offset: selection.anchorOffset,
+                offsetAtBeginning: selection.anchorOffset === 0,
+                offsetAtEnd: selection.anchorNode.data.length === selection.anchorOffset
+            };
+        }
+        if(which === 'focus') {
+            return {
+                element: focusElement,
+                offset: selection.focusOffset,
+                offsetAtBeginning: selection.focusOffset === 0,
+                offsetAtEnd: selection.focusNode.data.length === selection.focusOffset
+            };
+        }
+        
+        var element,
+            offset;
+
+        if(anchorElement.parent().sameNode(focusElement.parent())) {
+            var parent = anchorElement.parent(),
+                anchorFirst = parent.childIndex(anchorElement) < parent.childIndex(focusElement);
+            if(anchorFirst) {
+                if(which === 'start') {
+                    element = anchorElement;
+                    offset = selection.anchorOffset
+                }
+                else if(which === 'end') {
+                    element = focusElement,
+                    offset = selection.focusOffset
+                }
+            } else {
+                if(which === 'start') {
+                    element = focusElement,
+                    offset = selection.focusOffset
+                }
+                else if(which === 'end') {
+                    element = anchorElement;
+                    offset = selection.anchorOffset
+                }
+            }
+        } else {
+            // TODO: Handle order via https://developer.mozilla.org/en-US/docs/Web/API/Node.compareDocumentPosition
+            if(which === 'start') {
+                element = anchorElement;
+                offset = selection.anchorOffset
+            } else {
+                element = focusElement;
+                offset = selection.focusOffset
+            }
+        }
+
+        var nodeLen = (element.sameNode(focusElement) ? selection.focusNode : selection.anchorNode).length;
+        return {
+            element: element,
+            offset: offset,
+            offsetAtBeginning: offset === 0,
+            offsetAtEnd: nodeLen === offset
+        }
+    }
+})
+
+return {
+    fromXML: function(xml, publisher) {
+        return new Canvas(xml, publisher);
+    }
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/documentCanvas/canvas/canvas.test.js b/src/editor/modules/documentCanvas/canvas/canvas.test.js
new file mode 100644 (file)
index 0000000..2dc24eb
--- /dev/null
@@ -0,0 +1,1553 @@
+define([
+'libs/chai',
+'libs/sinon',
+'modules/documentCanvas/canvas/canvas',
+'modules/documentCanvas/canvas/documentElement',
+'modules/documentCanvas/canvas/utils'
+], function(chai, sinon, canvas, documentElement, utils) {
+    
+'use strict';
+
+var expect = chai.expect;
+
+
+describe('Canvas', function() {
+
+
+
+    describe('ZWS', function() {
+        var view, section, textElement;
+        
+        beforeEach(function() {
+            var c = canvas.fromXML('<section></section>');
+
+            section = c.doc();
+            textElement = section.children()[0];
+            view = c.view()[0];
+            document.getElementsByTagName('body')[0].appendChild(view);
+        });
+
+        afterEach(function() {
+            view.parentNode.removeChild(view);
+        });
+
+        var getTextContainerNode = function(textElement) {
+            return textElement.dom().contents()[0];
+        }
+
+        it('is set automatically on all empty DocumentTextElements', function() {
+            expect(getTextContainerNode(textElement).data).to.equal(utils.unicode.ZWS);
+
+            var header = section.append({tag: 'header'}),
+                newText = header.append({text: ''}),
+                textNode = getTextContainerNode(textElement);
+            
+            expect(textNode.data).to.equal(utils.unicode.ZWS);
+        });
+
+        it('is added automatically when whole text gets deleted', function() {
+            getTextContainerNode(textElement).data = '';
+            
+            window.setTimeout(function() {
+                expect(getTextContainerNode(textElement).data).to.equal(utils.unicode.ZWS);
+            }, 0)
+            
+            var header = section.append({tag: 'header'}),
+                newText = header.append({text: 'Alice'}),
+                textNode = getTextContainerNode(newText);
+
+            expect(textNode.data).to.have.length('Alice'.length);
+            textNode.data = '';
+
+            window.setTimeout(function() {
+                expect(textNode.data).to.equal(utils.unicode.ZWS);
+            }, 0)
+        });
+    });
+
+    describe('Internal HTML representation of a DocumentNodeElement', function() {
+        it('is always a div tag', function() {
+            ['section', 'header', 'span', 'aside', 'figure'].forEach(function(tagName) {
+                var dom = canvas.fromXML('<' + tagName + '></' + tagName + '>').doc().dom();
+                expect(dom.prop('tagName')).to.equal('DIV', tagName + ' is represented as div');
+            });
+        });
+        it('has wlxml tag put into wlxml-tag attribute of its internal container', function() {
+            var dom = canvas.fromXML('<section></section>').doc().dom();
+            expect(dom.children('[document-element-content]').attr('wlxml-tag')).to.equal('section');
+        });
+        it('has wlxml class put into wlxml-class attribute of its internal containr, dots replaced with dashes', function() {
+            var dom = canvas.fromXML('<section class="some.class"></section>').doc().dom();
+            expect(dom.children('[document-element-content]').attr('wlxml-class')).to.equal('some-class');
+        });
+    });
+
+    describe('Internal HTML representation of a DocumentTextElement', function() {
+        it('is text node wrapped in a div with document-text-element attribute set', function() {
+            var dom = canvas.fromXML('<section>Alice</section>').doc().children()[0].dom();
+            expect(dom.prop('tagName')).to.equal('DIV');
+            expect(dom.attr('document-text-element')).to.equal('');
+            expect(dom.contents().length).to.equal(1);
+            expect(dom.contents()[0].nodeType).to.equal(Node.TEXT_NODE);
+            expect($(dom.contents()[0]).text()).to.equal('Alice');
+        });
+    });
+
+    describe('basic properties', function() {
+        it('renders empty document when canvas created from empty XML', function() {
+            var c = canvas.fromXML('');
+            expect(c.doc()).to.equal(null);
+        });
+
+        it('gives access to its document root node', function() {
+            var c = canvas.fromXML('<section></section>');
+            expect(c.doc().getWlxmlTag()).to.equal('section');
+        });
+
+        describe('root element', function() {
+            it('has no parent', function() {
+                var c = canvas.fromXML('<section></section>');
+                expect(c.doc().parent()).to.be.null;
+            });
+        });
+
+        describe('DocumentTextElement', function() {
+            it('can have its content set', function() {
+                var c = canvas.fromXML('<section>Alice</section>'),
+                    root = c.doc(),
+                    text = root.children()[0];
+                
+                text.setText('a cat');
+                expect(root.children()[0].getText()).to.equal('a cat');
+            });
+        });
+
+        describe('DocumentNodeElement', function() {
+            it('knows index of its child', function() {
+                var c = canvas.fromXML('<section><div></div><header></header><span></span></section>'),
+                    root = c.doc(),
+                    child = root.children()[1];
+                expect(root.childIndex(child)).to.equal(1);
+            });
+
+            it('knows WLXML tag it renders', function(){
+                var c = canvas.fromXML('<section></section>'),
+                    section = c.doc();
+                expect(section.getWlxmlTag()).to.equal('section', 'initial tag is section');
+                section.setWlxmlTag('header');
+                expect(section.getWlxmlTag()).to.equal('header', 'tag is changed to header');
+            });
+
+            it('knows WLXML class of a WLXML tag it renders', function(){
+                var c = canvas.fromXML('<section class="some.class.A"></section>'),
+                    section = c.doc();
+                expect(section.getWlxmlClass()).to.equal('some.class.A');
+                section.setWlxmlClass('some.class.B');
+                expect(section.getWlxmlClass()).to.equal('some.class.B');
+                section.setWlxmlClass(null);
+                expect(section.getWlxmlClass()).to.be.undefined;
+            });
+
+
+
+            describe('element has meta attributes', function() {
+                it('can change its meta attributes', function() {
+                    var c = canvas.fromXML('<section><span class="uri" meta-uri="someuri"></span></section>'),
+                    span = c.doc().children()[0];
+                    
+                    expect(span.getWlxmlMetaAttr('uri')).to.equal('someuri');
+                    span.setWlxmlMetaAttr('uri', 'otheruri');
+                    expect(span.getWlxmlMetaAttr('uri')).to.equal('otheruri');
+                });
+
+                it('changes its meta attributes with class change', function() {
+                    var c = canvas.fromXML('<section><span class="uri" meta-uri="someuri"></span></section>'),
+                    span = c.doc().children()[0];
+                    
+                    expect(span.getWlxmlMetaAttr('uri')).to.equal('someuri');
+                    span.setWlxmlClass('author');
+                    expect(span.getWlxmlMetaAttr('uri')).to.be.undefined;
+                });
+
+                it('keeps meta attribute value on class change if a new class has this attribute', function() {
+                    var c = canvas.fromXML('<section><span class="uri" meta-uri="someuri"></span></section>'),
+                    span = c.doc().children()[0];
+                    span.setWlxmlClass('uri.some.subclass');
+                    expect(span.getWlxmlMetaAttr('uri')).to.equal('someuri');
+                });
+            });
+        });
+
+        it('returns DocumentNodeElement instance from HTMLElement', function() {
+            var c = canvas.fromXML('<section></section>'),
+                htmlElement = c.doc().dom().get(0),
+                element = c.getDocumentElement(htmlElement);
+            expect(element).to.be.instanceOf(documentElement.DocumentNodeElement);
+            expect(element.sameNode(c.doc()));
+        });
+        
+        it('returns DocumentTextElement instance from Text Node', function() {
+            var c = canvas.fromXML('<section>Alice</section>'),
+                aliceElement = c.doc().children()[0],
+                textNode = aliceElement.dom().contents()[0],
+                element = c.getDocumentElement(textNode);
+
+            expect(textNode.nodeType).to.equal(Node.TEXT_NODE, 'text node selected');
+            expect($(textNode).text()).to.equal('Alice');
+
+            expect(element).to.be.instanceOf(documentElement.DocumentTextElement);
+            expect(element.sameNode(c.doc().children()[0]));
+        });
+    });
+
+
+
+    describe('document representation api', function() {
+        describe('document root element', function() {
+            var c = canvas.fromXML('<section></section>');
+            it('exists', function() {
+                expect(c.doc()).to.be.instanceOf(documentElement.DocumentElement);
+            });
+            it('is of type DocumentNodeElement', function() {
+                expect(c.doc()).to.be.instanceOf(documentElement.DocumentNodeElement);
+            });
+        });
+
+        describe('DocumentElements comparison', function() {
+            it('reports dwo DocumentElements to be the same when they represent the same wlxml document element', function() {
+                var c = canvas.fromXML('<section><div></div><div></div></section>'),
+                    first_div1 = c.doc().children()[0],
+                    first_div2 = c.doc().children()[0],
+                    second_div = c.doc().children()[1];
+                expect(first_div1.sameNode(first_div1)).to.be.true;
+                expect(first_div1.sameNode(first_div2)).to.be.true;
+                expect(first_div1.sameNode(second_div)).to.be.false;
+            });
+        });
+
+        describe('traversing', function() {
+            it('reports element nodes', function() {
+                var c = canvas.fromXML('<section><div></div></section>'),
+                    children = c.doc().children();
+                expect(children.length).to.equal(1);
+                expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
+
+                c = canvas.fromXML('<section><div></div><div></div></section>'),
+                    children = c.doc().children();
+                expect(children.length).to.equal(2);
+                expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
+                expect(children[1]).to.be.instanceOf(documentElement.DocumentNodeElement);
+            });
+            
+            it('reports text nodes', function() {
+                var c = canvas.fromXML('<section>Alice</section>'),
+                    children = c.doc().children();
+                expect(children.length).to.equal(1);
+                expect(children[0]).to.be.instanceOf(documentElement.DocumentTextElement);
+            });
+
+            describe('accessing parents', function() {
+                it('returns DocumentNodeElement representing parent in wlxml document as DocumentNodeElement parent', function() {
+                    var c = canvas.fromXML('<section><div></div></section>'),
+                        div = c.doc().children()[0];
+                    expect(div.parent().sameNode(c.doc())).to.be.true;
+                });
+                it('returns DocumentNodeElement representing parent in wlxml document as DocumentTextElement parent', function() {
+                    var c = canvas.fromXML('<section>Alice</section>'),
+                        text = c.doc().children()[0];
+                    expect(text.parent().sameNode(c.doc())).to.be.true;
+                });
+            });
+
+            describe('accessing sibling parents of two elements', function() {
+                it('returns elements themself if they have direct common parent', function() {
+                    var c = canvas.fromXML('<section>\
+                        <div>\
+                            <div>A</div>\
+                            <div>B</div>\
+                        </div>\
+                    </section>'),
+                        section = c.doc(),
+                        wrappingDiv = c.doc().children()[0],
+                        divA = wrappingDiv.children()[0],
+                        divB = wrappingDiv.children()[1];
+
+                    var siblingParents = c.getSiblingParents({element1: divA, element2: divB});
+
+                    expect(siblingParents.element1.sameNode(divA)).to.equal(true, 'divA');
+                    expect(siblingParents.element2.sameNode(divB)).to.equal(true, 'divB');
+                });
+
+                it('returns sibling parents - example 1', function() {
+                    var c = canvas.fromXML('<section>Alice <span>has a cat</span></section>'),
+                        section = c.doc(),
+                        aliceText = section.children()[0],
+                        span = section.children()[1],
+                        spanText = span.children()[0];
+
+                    var siblingParents = c.getSiblingParents({element1: aliceText, element2: spanText});
+
+                    expect(siblingParents.element1.sameNode(aliceText)).to.equal(true, 'aliceText');
+                    expect(siblingParents.element2.sameNode(span)).to.equal(true, 'span');
+                });
+            })
+
+            describe('free text handling', function() {
+                    it('sees free text', function() {
+                        var c = canvas.fromXML('<section>Alice <span>has</span> a cat</section>'),
+                            children = c.doc().children();
+                        expect(children.length).to.equal(3);
+                        expect(children[0]).to.be.instanceOf(documentElement.DocumentTextElement);
+                        expect(children[1]).to.be.instanceOf(documentElement.DocumentNodeElement);
+                        expect(children[2]).to.be.instanceOf(documentElement.DocumentTextElement);
+                    });
+            });
+
+            describe('empty nodes handling', function() {
+                it('says empty element node from XML source has one empty DocumentTextElement', function() {
+                    var c = canvas.fromXML('<section></section>');
+                    expect(c.doc().children()).to.have.length(1);
+                    expect(c.doc().children()[0].getText()).to.equal('');
+                });
+
+                it('allows creation of an empty element node', function() {
+                    var c = canvas.fromXML('<section></section>'),
+                        section = c.doc(),
+                        header = section.append({tag: 'header'});
+                    expect(header.children()).to.have.length(0);
+                });
+            });
+            
+            describe('white characters handling', function() {
+
+                it('says element node with one space has one DocumentTextElement', function() {
+                    var c = canvas.fromXML('<section> </section>');
+                    expect(c.doc().children().length).to.equal(1);
+                    expect(c.doc().children()[0]).to.be.instanceOf(documentElement.DocumentTextElement);
+                    expect(c.doc().children()[0].getText()).to.equal(' ');
+                });
+                it('ignores white space surrounding block elements', function() {
+                    var c = canvas.fromXML('<section> <div></div> </section>');
+                    var children = c.doc().children();
+                    expect(children.length).to.equal(1);
+                    expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
+                });
+                it('ignores white space between block elements', function() {
+                    var c = canvas.fromXML('<section><div></div> <div></div></section>');
+                    var children = c.doc().children();
+                    expect(children.length === 2);
+                    [0,1].forEach(function(idx) {
+                        expect(children[idx]).to.be.instanceOf(documentElement.DocumentNodeElement);
+                    });
+                });
+
+                it('trims white space from the beginning and the end of the block elements', function() {
+                    var c = canvas.fromXML('<section> Alice <span>has</span> a cat </section>');
+                    expect(c.doc().children()[0].getText()).to.equal('Alice ');
+                    expect(c.doc().children()[2].getText()).to.equal(' a cat');
+                });
+
+                it('normalizes string of white characters to one space at the inline element boundries', function() {
+                    var c = canvas.fromXML('<span>   Alice has a cat   </span>');
+                    expect(c.doc().children()[0].getText()).to.equal(' Alice has a cat ');
+                });
+
+                it('normalizes string of white characters to one space before inline element', function() {
+                    var c = canvas.fromXML('<div>Alice has  <span>a cat</span></div>');
+                    expect(c.doc().children()[0].getText()).to.equal('Alice has ');
+                });
+                
+                it('normalizes string of white characters to one space after inline element', function() {
+                    var c = canvas.fromXML('<div>Alice has <span>a</span>  cat</div>');
+                    expect(c.doc().children()[2].getText()).to.equal(' cat');
+                });
+            });
+
+            describe('getting vertically first text element', function() {
+                it('returns the first child if it\'s text element, ignores metadata', function() {
+                    var c = canvas.fromXML('<section><metadata><dc:author>author</dc:author></metadata>Alice<div>has</div>a cat</section>'),
+                        first = c.doc().getVerticallyFirstTextElement();
+
+                    expect(first.sameNode(c.doc().children()[1])).to.be.true;
+                });
+
+                it('looks recursively inside node elements if they precede text element', function() {
+                    var c = canvas.fromXML('\
+                            <section>\
+                                <div>\
+                                    <div>\
+                                        Alice\
+                                    </div>\
+                                </div>\
+                                Some text\
+                            </section>'),
+                        textAlice = c.doc().children()[0].children()[0].children()[0],
+                        first = c.doc().getVerticallyFirstTextElement();
+
+                    expect(textAlice).to.be.instanceOf(documentElement.DocumentTextElement);
+                    expect(first.sameNode(textAlice)).to.be.true;
+                });
+            });
+        });
+
+        describe('manipulation api', function() {
+
+            describe('Basic Element inserting', function() {
+                it('can put new NodeElement at the end', function() {
+                    var c = canvas.fromXML('<section><div></div></section>'),
+                        appended = c.doc().append({tag: 'header', klass: 'some.class'}),
+                        children = c.doc().children();
+
+                    expect(children.length).to.equal(2);
+                    expect(children[1].sameNode(appended)).to.be.true;
+                });
+
+                it('can put new TextElement at the end', function() {
+                    var c = canvas.fromXML('<section><div><div></section>'),
+                        appended = c.doc().append({text: 'Alice'}),
+                        children = c.doc().children();
+
+                    expect(children.length).to.equal(2);
+                    expect(children[1].sameNode(appended)).to.be.true;
+                    expect(children[1].getText()).to.equal('Alice');
+                });
+
+                it('can put new NodeElement at the beginning', function() {
+                    var c = canvas.fromXML('<section><div></div></section>'),
+                        prepended = c.doc().prepend({tag: 'header', klass: 'some.class'}),
+                        children = c.doc().children();
+
+                    expect(children).to.have.length(2);
+                    expect(children[0].sameNode(prepended)).to.be.true;
+                });
+
+                it('can put new TextElement at the beginning', function() {
+                    var c = canvas.fromXML('<section><div></div></section>'),
+                        prepended = c.doc().prepend({text: 'Alice'}),
+                        children = c.doc().children();
+
+                    expect(children).to.have.length(2)
+                    expect(children[0].sameNode(prepended)).to.be.true;
+                    expect(children[0].getText()).to.equal('Alice');
+                });
+
+                it('can put new NodeElement after another NodeElement', function() {
+                    var c = canvas.fromXML('<section><div></div></section>'),
+                        div = c.doc().children()[0],
+                        added = div.after({tag: 'header', klass: 'some.class'}),
+                        children = c.doc().children();
+                    expect(children.length).to.equal(2);
+                    expect(children[1].sameNode(added)).to.be.true;
+                });
+
+                it('can put new Nodeelement before another element', function() {
+                    var c = canvas.fromXML('<section><div></div></section>'),
+                        div = c.doc().children()[0],
+                        added = div.before({tag: 'header', klass: 'some.class'}),
+                        children = c.doc().children();
+                    expect(children.length).to.equal(2);
+                    expect(children[0].sameNode(added)).to.be.true;
+                });
+
+                it('can put new DocumentNodeElement after DocumentTextElement', function() {
+                    var c = canvas.fromXML('<section>Alice</section>'),
+                        text = c.doc().children()[0],
+                        added = text.after({tag: 'p'}),
+                        children = c.doc().children();
+
+                    expect(children.length).to.equal(2);
+                    expect(children[0]).to.be.instanceOf(documentElement.DocumentTextElement);
+                    expect(children[0].getText()).to.equal('Alice');
+                    expect(children[1]).to.be.instanceOf(documentElement.DocumentNodeElement);
+                    expect(children[1].sameNode(added)).to.be.true;
+                });
+                it('can put new DocumentNodeElement before DocumentTextElement', function() {
+                    var c = canvas.fromXML('<section>Alice</section>'),
+                        text = c.doc().children()[0],
+                        added = text.before({tag: 'p'}),
+                        children = c.doc().children();
+
+                    expect(children.length).to.equal(2);
+                    expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
+                    expect(children[0].sameNode(added)).to.be.true;
+                    expect(children[1]).to.be.instanceOf(documentElement.DocumentTextElement);
+                    expect(children[1].getText()).to.equal('Alice');
+                });
+
+                it('can divide DocumentTextElement with a new DocumentNodeElement', function() {
+                    var c = canvas.fromXML('<section>Alice has a cat</section>'),
+                        section = c.doc(),
+                        text = section.children()[0];
+
+                    var returned = text.divide({tag: 'aside', klass: 'footnote', offset: 5}),
+                        sectionChildren = section.children(),
+                        lhsText = sectionChildren[0],
+                        rhsText = sectionChildren[2];
+
+                    expect(lhsText.getText()).to.equal('Alice');
+                    expect(returned.sameNode(sectionChildren[1]));
+                    expect(rhsText.getText()).to.equal(' has a cat');
+                });
+
+                it('treats dividing DocumentTextElement at the very end as appending after it', function() {
+                    var c = canvas.fromXML('<section>Alice has a cat</section>'),
+                        section = c.doc(),
+                        text = section.children()[0];
+
+                    var returned = text.divide({tag: 'aside', klass: 'footnote', offset: 15}),
+                        sectionChildren = section.children(),
+                        textElement = sectionChildren[0],
+                        nodeElement = sectionChildren[1];
+
+                    expect(sectionChildren.length).to.equal(2);
+                    expect(textElement.getText()).to.equal('Alice has a cat');
+                    expect(returned.sameNode(nodeElement)).to.be.true;
+                    expect(nodeElement.getWlxmlTag()).to.equal('aside');
+                });
+
+                it('treats dividing DocumentTextElement at the very beginning as appending before it', function() {
+                    var c = canvas.fromXML('<section>Alice has a cat</section>'),
+                        section = c.doc(),
+                        text = section.children()[0];
+
+                    var returned = text.divide({tag: 'aside', klass: 'footnote', offset: 0}),
+                        sectionChildren = section.children(),
+                        nodeElement = sectionChildren[0],
+                        textElement = sectionChildren[1];
+                        
+                    expect(sectionChildren.length).to.equal(2);
+                    expect(textElement.getText()).to.equal('Alice has a cat');
+                    expect(returned.sameNode(nodeElement)).to.be.true;
+                    expect(nodeElement.getWlxmlTag()).to.equal('aside');
+                });
+            });
+
+            describe('Removing elements', function() {
+                it('merges left and right DocumentTextElement sibling of a detached DocumentNodeElement', function() {
+                    var c = canvas.fromXML('<section>Alice<div>has</div>a cat</section>'),
+                        section = c.doc(),
+                        div = section.children()[1];
+
+                    div.detach();
+
+                    var sectionChildren = section.children(),
+                        textElement = sectionChildren[0];
+
+                    expect(sectionChildren).to.have.length(1);
+                    expect(textElement.getText()).to.equal('Alicea cat');
+                });
+            });
+
+            describe('Splitting text', function() {
+                
+                it('splits DocumentTextElement\'s parent into two DocumentNodeElements of the same type', function() {
+                    var c = canvas.fromXML('<section><header>Some header</header></section>'),
+                        section = c.doc(),
+                        text = section.children()[0].children()[0];
+
+                    var returnedValue = text.split({offset: 5});
+                    expect(section.children().length).to.equal(2, 'section has two children');
+                    
+                    var header1 = section.children()[0];
+                    var header2 = section.children()[1];
+
+                    expect(header1.getWlxmlTag()).to.equal('header', 'first section child represents wlxml header');
+                    expect(header1.children().length).to.equal(1, 'first header has one text child');
+                    expect(header1.children()[0].getText()).to.equal('Some ', 'first header has correct content');
+                    expect(header2.getWlxmlTag()).to.equal('header', 'second section child represents wlxml header');
+                    expect(header2.children().length).to.equal(1, 'second header has one text child');
+                    expect(header2.children()[0].getText()).to.equal('header', 'second header has correct content');
+
+                    expect(returnedValue.first.sameNode(header1)).to.equal(true, 'first node returnde');
+                    expect(returnedValue.second.sameNode(header2)).to.equal(true, 'second node returned');
+                });
+
+                it('leaves empty copy of DocumentNodeElement if splitting at the very beginning', function() {
+                        var c = canvas.fromXML('<section><header>Some header</header></section>'),
+                        section = c.doc(),
+                        text = section.children()[0].children()[0];
+
+                        text.split({offset: 0});
+                        
+                        var header1 = section.children()[0];
+                        var header2 = section.children()[1];
+
+                        expect(header1.children().length).to.equal(0);
+                        expect(header2.children()[0].getText()).to.equal('Some header');
+                });
+
+                it('leaves empty copy of DocumentNodeElement if splitting at the very end', function() {
+                        var c = canvas.fromXML('<section><header>Some header</header></section>'),
+                        section = c.doc(),
+                        text = section.children()[0].children()[0];
+
+                        text.split({offset: 11});
+                        
+                        var header1 = section.children()[0];
+                        var header2 = section.children()[1];
+
+                        expect(header1.children()[0].getText()).to.equal('Some header');
+                        expect(header2.children().length).to.equal(0);
+                });
+
+                it('keeps DocumentTextElement\'s parent\'s children elements intact', function() {
+                    var c = canvas.fromXML('\
+                            <section>\
+                                <header>\
+                                    A <span>fancy</span> and <span>nice</span> header\
+                                </header>\
+                            </section>'),
+                        section = c.doc(),
+                        header = section.children()[0],
+                        textAnd = header.children()[2];
+
+                    textAnd.split({offset: 2});
+                    
+                    var sectionChildren = section.children();
+                    expect(sectionChildren.length).to.equal(2, 'Section has two children');
+                    expect(sectionChildren[0].getWlxmlTag()).to.equal('header', 'First section element is a wlxml header');
+                    expect(sectionChildren[1].getWlxmlTag()).to.equal('header', 'Second section element is a wlxml header');
+
+                    var firstHeaderChildren = sectionChildren[0].children();
+                    expect(firstHeaderChildren.length).to.equal(3, 'First header has three children');
+                    expect(firstHeaderChildren[0].getText()).to.equal('A ', 'First header starts with a text');
+                    expect(firstHeaderChildren[1].getWlxmlTag()).to.equal('span', 'First header has span in the middle');
+                    expect(firstHeaderChildren[2].getText()).to.equal(' a', 'First header ends with text');
+
+                    var secondHeaderChildren = sectionChildren[1].children();
+                    expect(secondHeaderChildren.length).to.equal(3, 'Second header has three children');
+                    expect(secondHeaderChildren[0].getText()).to.equal('nd ', 'Second header starts with text');
+                    expect(secondHeaderChildren[1].getWlxmlTag()).to.equal('span', 'Second header has span in the middle');
+                    expect(secondHeaderChildren[2].getText()).to.equal(' header', 'Second header ends with text');
+                });
+            });
+
+            describe('wrapping', function() {
+                it('wraps DocumentNodeElement', function() {
+                    var c = canvas.fromXML('<section><div></div></section>'),
+                        div = c.doc().children()[0];
+                    
+                    var returned = div.wrapWithNodeElement({tag: 'header', klass: 'some.class'}),
+                        parent = div.parent(),
+                        parent2 = c.doc().children()[0];
+
+                    expect(returned.sameNode(parent)).to.be.true;
+                    expect(returned.sameNode(parent2)).to.be.true;
+                    expect(returned.getWlxmlTag()).to.equal('header');
+                    expect(returned.getWlxmlClass()).to.equal('some.class');
+                });
+                it('wraps DocumentTextElement', function() {
+                    var c = canvas.fromXML('<section>Alice</section>'),
+                        text = c.doc().children()[0];
+                    
+                    var returned = text.wrapWithNodeElement({tag: 'header', klass: 'some.class'}),
+                        parent = text.parent(),
+                        parent2 = c.doc().children()[0];
+
+                    expect(returned.sameNode(parent)).to.be.true;
+                    expect(returned.sameNode(parent2)).to.be.true;
+                    expect(returned.getWlxmlTag()).to.equal('header');
+                    expect(returned.getWlxmlClass()).to.equal('some.class');
+                });
+                
+                describe('wrapping part of DocumentTextElement', function() {
+                    [{start: 5, end: 12}, {start: 12, end: 5}].forEach(function(offsets) {
+                        it('wraps in the middle ' + offsets.start + '/' + offsets.end, function() {
+                            var c = canvas.fromXML('<section>Alice has a cat</section>'),
+                                text = c.doc().children()[0];
+                            
+                            var returned = text.wrapWithNodeElement({tag: 'header', klass: 'some.class', start: offsets.start, end: offsets.end}),
+                                children = c.doc().children();
+
+                            expect(children.length).to.equal(3);
+                            
+                            expect(children[0]).to.be.instanceOf(documentElement.DocumentTextElement);
+                            expect(children[0].getText()).to.equal('Alice');
+
+                            expect(children[1].sameNode(returned)).to.be.true;
+                            expect(returned.getWlxmlTag()).to.equal('header');
+                            expect(returned.getWlxmlClass()).to.equal('some.class');
+                            expect(children[1].children().length).to.equal(1);
+                            expect(children[1].children()[0].getText()).to.equal(' has a ');
+
+                            expect(children[2]).to.be.instanceOf(documentElement.DocumentTextElement);
+                            expect(children[2].getText()).to.equal('cat');
+                        });
+                    });
+
+                    it('wraps whole text inside DocumentTextElement if offsets span entire content', function() {
+                         var c = canvas.fromXML('<section>Alice has a cat</section>'),
+                             text = c.doc().children()[0];
+                         
+                         var returned = text.wrapWithNodeElement({tag: 'header', klass: 'some.class', start: 0, end: 15}),
+                             children = c.doc().children();
+
+                         expect(children.length).to.equal(1);
+                         expect(children[0]).to.be.instanceOf(documentElement.DocumentNodeElement);
+                         expect(children[0].children()[0].getText()).to.equal('Alice has a cat');
+                    });
+                });
+
+                it('wraps text spanning multiple sibling DocumentTextNodes', function() {
+                    var c = canvas.fromXML('<section>Alice has a <span>small</span> cat</section>'),
+                        section = c.doc(),
+                        wrapper = c.wrapText({
+                            inside: section, 
+                            _with: {tag: 'span', klass: 'some.class'},
+                            offsetStart: 6,
+                            offsetEnd: 4,
+                            textNodeIdx: [0,2]
+                        });
+
+                    expect(section.children().length).to.equal(2);
+                    expect(section.children()[0]).to.be.instanceOf(documentElement.DocumentTextElement);
+                    expect(section.children()[0].getText()).to.equal('Alice ');
+
+                    var wrapper2 = section.children()[1];
+                    expect(wrapper2.sameNode(wrapper)).to.be.true;
+
+                    var wrapperChildren = wrapper.children();
+                    expect(wrapperChildren.length).to.equal(3);
+                    expect(wrapperChildren[0].getText()).to.equal('has a ');
+
+                    expect(wrapperChildren[1]).to.be.instanceOf(documentElement.DocumentNodeElement);
+                    expect(wrapperChildren[1].children().length).to.equal(1);
+                    expect(wrapperChildren[1].children()[0].getText()).to.equal('small');
+
+                    expect(wrapperChildren[2].getText()).to.equal(' cat');
+                });
+
+                it('wraps multiple sibling Elements', function() {
+                    var c = canvas.fromXML('<section>Alice<div>has</div><div>a cat</div></section>'),
+                        section = c.doc(),
+                        aliceText = section.children()[0],
+                        firstDiv = section.children()[1],
+                        lastDiv = section.children()[section.children().length -1];
+
+                    var returned = c.wrapElements({
+                            element1: aliceText,
+                            element2: lastDiv,
+                            _with: {tag: 'header'}
+                        });
+
+                    var sectionChildren = section.children(),
+                        header = sectionChildren[0],
+                        headerChildren = header.children();
+
+                    expect(sectionChildren).to.have.length(1);
+                    expect(header.sameNode(returned)).to.equal(true, 'wrapper returned');
+                    expect(headerChildren).to.have.length(3);
+                    expect(headerChildren[0].sameNode(aliceText)).to.equal(true, 'first node wrapped');
+                    expect(headerChildren[1].sameNode(firstDiv)).to.equal(true, 'second node wrapped');
+                    expect(headerChildren[2].sameNode(lastDiv)).to.equal(true, 'third node wrapped');
+                });
+                it('wraps multiple sibling Elements - middle case', function() {
+                    var c = canvas.fromXML('<section><div></div>div></div><div></div><div></div></section>'),
+                        section = c.doc(),
+                        div1 = section.children()[0],
+                        div2 = section.children()[1],
+                        div3 = section.children()[2],
+                        div4 = section.children()[3];
+
+                    var returned = c.wrapElements({
+                            element1: div2,
+                            element2: div3,
+                            _with: {tag: 'header'}
+                        });
+
+                    var sectionChildren = section.children(),
+                        header = sectionChildren[1],
+                        headerChildren = header.children();
+
+                    expect(sectionChildren).to.have.length(3);
+                    expect(headerChildren).to.have.length(2);
+                    expect(headerChildren[0].sameNode(div2)).to.equal(true, 'first node wrapped');
+                    expect(headerChildren[1].sameNode(div3)).to.equal(true, 'second node wrapped');
+                });
+            });
+
+            describe('unwrapping DocumentTextElement from its parent DocumentNodeElement if it\'s its only child', function() {
+                it('unwraps text element from its parent and stays between its old parent siblings', function() {
+                    var c = canvas.fromXML('<section><div>Alice</div><div>has</div><div>a cat</div></section>'),
+                        section = c.doc(),
+                        sectionChildren = section.children(),
+                        divAlice = sectionChildren[0],
+                        divHas = sectionChildren[1],
+                        textHas = divHas.children()[0],
+                        divCat = sectionChildren[2];
+
+                    var newTextContainer = textHas.unwrap(),
+                        sectionChildren = section.children();
+
+                    expect(sectionChildren[0].sameNode(divAlice)).to.equal(true, 'divAlice ok');
+                    expect(newTextContainer.sameNode(section)).to.equal(true, 'unwrap returns new text parent DocumentNodeElement');
+                    expect(sectionChildren[1].getText()).to.equal('has');
+                    expect(sectionChildren[2].sameNode(divCat)).to.equal(true, 'divCat ok');
+
+                });
+                it('unwraps and join with its old parent adjacent text elements ', function() {
+                    var c = canvas.fromXML('<section>Alice <span>has a</span> cat</section>'),
+                    section = c.doc(),
+                    text = section.children()[1].children()[0];
+
+                    var newTextContainer = text.unwrap();
+
+                    expect(section.children().length).to.equal(1, 'section has one child');
+                    expect(section.children()[0].getText()).to.equal('Alice has a cat');
+                    expect(newTextContainer.sameNode(c.doc())).to.equal(true, 'unwrap returns new text parent DocumentNodeElement');
+                });
+
+                it('unwraps text element from its parent - first child case', function() {
+                    var c = canvas.fromXML('<section><span class="uri">Some</span>text</section>'),
+                        section = c.doc(),
+                        span = section.children()[0];
+
+                    span.children()[0].unwrap();
+
+                    var sectionChildren = section.children();
+
+                    expect(sectionChildren).to.have.length(1);
+                    expect(sectionChildren[0].getText()).to.equal('Sometext');
+                });
+            });
+
+            describe('unwrapping the whole content of a DocumentNodeElement', function() {
+                it('removes a DocumentNodeElement but keeps its content', function() {
+                    var c = canvas.fromXML('<section><div>Alice has<span>a</span> cat</div></section>'),
+                        section = c.doc(),
+                        div = c.doc().children()[0],
+                        span = div.children()[1];
+
+                    var range = div.unwrapContents(),
+                        sectionChildren = section.children();
+
+                    expect(sectionChildren).to.have.length(3);
+                    expect(sectionChildren[0].getText()).to.equal('Alice has');
+                    expect(sectionChildren[1].sameNode(span)).to.equal(true, 'span ok');
+                    expect(sectionChildren[2].getText()).to.equal(' cat');
+
+                    expect(range.element1.sameNode(sectionChildren[0])).to.equal(true, 'range start ok');
+                    expect(range.element2.sameNode(sectionChildren[2])).to.equal(true, 'range end ok');
+                });
+                it('merges text elements on the boundries', function() {
+                    var c = canvas.fromXML('<section>Alice<div>has a <span>cat</span>!</div>!!</section>'),
+                        section = c.doc(),
+                        div = c.doc().children()[1],
+                        span = div.children()[1];
+
+                    var range = div.unwrapContents(),
+                        sectionChildren = section.children();
+
+                    expect(sectionChildren).to.have.length(3);
+                    expect(sectionChildren[0].getText()).to.equal('Alicehas a ');
+                    expect(sectionChildren[1].sameNode(span)).to.equal(true, 'span ok');
+                    expect(sectionChildren[2].getText()).to.equal('!!!');
+
+                    expect(range.element1.sameNode(sectionChildren[0])).to.equal(true, 'range start ok');
+                    expect(range.element2.sameNode(sectionChildren[2])).to.equal(true, 'range end ok');
+                });
+
+                it('merges text elements on the boundries - single child case', function() {
+                    var c = canvas.fromXML('<section>Alice <span>has</span> a cat</section>'),
+                        section = c.doc(),
+                        span = section.children()[1];
+
+                    var range = span.unwrapContents(),
+                        sectionChildren = section.children();
+
+                    expect(sectionChildren).to.have.length(1);
+                    expect(sectionChildren[0].getText()).to.equal('Alice has a cat');
+                });
+            });
+            
+        });
+
+        describe('Lists api', function() {
+            describe('creating lists', function() {
+                it('allows creation of a list from existing sibling DocumentElements', function() {
+                    var c = canvas.fromXML('\
+                        <section>\
+                            Alice\
+                            <div>has</div>\
+                            a\
+                            <div>cat</div>\
+                        </section>'),
+                        section = c.doc(),
+                        textHas = section.children()[1],
+                        divA = section.children()[2]
+                    
+                    c.list.create({element1: textHas, element2: divA});
+
+                    expect(section.children().length).to.equal(3, 'section has three child elements');
+
+                    var child1 = section.children()[0],
+                        list = section.children()[1],
+                        child3 = section.children()[2];
+
+                    expect(child1.getText()).to.equal('Alice');
+                    expect(list.is('list')).to.equal(true, 'second child is a list');
+                    expect(list.children().length).to.equal(2, 'list contains two elements');
+                    list.children().forEach(function(child) {
+                        expect(child.getWlxmlClass()).to.equal('item', 'list childs have wlxml class of item');
+                    });
+                    expect(child3.children()[0].getText()).to.equal('cat');
+                });
+                
+                it('allows creating nested list from existing sibling list items', function() {
+                    var c = canvas.fromXML('\
+                        <section>\
+                            <div class="list-items">\
+                                <div class="item">A</div>\
+                                <div class="item">B</div>\
+                                <div class="item">C</div>\
+                                <div class="item">D</div>\
+                            </div>\
+                        </section>'),
+                        outerList = c.doc().children()[0],
+                        itemB = outerList.children()[1],
+                        itemC = outerList.children()[2];
+
+
+                        c.list.create({element1: itemB, element2: itemC});
+
+                    var outerListItems = outerList.children(),
+                        innerList = outerListItems[1].children()[0],
+                        innerListItems = innerList.children();
+
+                    expect(outerListItems.length).to.equal(3, 'outer list has three items');
+                    expect(outerListItems[0].children()[0].getText()).to.equal('A', 'first outer item ok');
+                    expect(outerListItems[1].getWlxmlClass()).to.equal('item', 'inner list is wrapped by item element');
+
+                    expect(innerList.is('list')).to.equal(true, 'inner list created');
+                    expect(innerListItems.length).to.equal(2, 'inner list has two items');
+                    expect(innerListItems[0].children()[0].getText()).to.equal('B', 'first inner item ok');
+                    expect(innerListItems[1].children()[0].getText()).to.equal('C', 'second inner item ok');
+
+                    expect(outerListItems[2].children()[0].getText()).to.equal('D', 'last outer item ok');
+
+                });
+
+            });
+
+            describe('extracting list items', function() {
+                it('creates two lists with extracted items in the middle if extracting from the middle of the list', function() {
+                    var c = canvas.fromXML('\
+                        <section>\
+                            <div class="list.items">\
+                                <div class="item">0</div>\
+                                <div class="item">1</div>\
+                                <div class="item">2</div>\
+                                <div class="item">3</div>\
+                            </div>\
+                        </section>'),
+                        list = c.doc().children()[0],
+                        item1 = list.children()[1],
+                        item2 = list.children()[2];
+
+                    c.list.extractItems({element1: item1, element2: item2});
+
+                    var section = c.doc(),
+                        list1 = section.children()[0],
+                        oldItem1 = section.children()[1],
+                        oldItem2 = section.children()[2],
+                        list2 = section.children()[3];
+
+                    expect(section.children().length).to.equal(4, 'section contains four children');
+                    
+                    expect(list1.is('list')).to.equal(true, 'first section child is a list');
+                    expect(list1.children().length).to.equal(1, 'first list has one child');
+                    expect(list1.children()[0].children()[0].getText()).to.equal('0', 'first item of the first list is a first item of the original list');
+
+                    expect(oldItem1.children()[0].getText()).to.equal('1', 'first item got extracted');
+                    expect(oldItem1.getWlxmlClass() === undefined).to.equal(true, 'first extracted element has no wlxml class');
+
+                    expect(oldItem2.children()[0].getText()).to.equal('2', 'second item got extracted');
+                    expect(oldItem2.getWlxmlClass() === undefined).to.equal(true, 'second extracted element has no wlxml class');
+
+                    expect(list2.is('list')).to.equal(true, 'last section child is a list');
+                    expect(list2.children().length).to.equal(1, 'second list has one child');
+                    expect(list2.children()[0].children()[0].getText()).to.equal('3', 'first item of the second list is a last item of the original list');
+                });
+
+                it('puts extracted items above the list if starting item is the first one', function() {
+                    var c = canvas.fromXML('\
+                        <section>\
+                            <div class="list.items">\
+                                <div class="item">0</div>\
+                                <div class="item">1</div>\
+                                <div class="item">2</div>\
+                            </div>\
+                        </section>'),
+                        list = c.doc().children()[0],
+                        item1 = list.children()[0],
+                        item2 = list.children()[1],
+                        item3 = list.children()[2];
+
+                    c.list.extractItems({element1: item1, element2: item2});
+
+                    var section = c.doc(),
+                        oldItem1 = section.children()[0],
+                        oldItem2 = section.children()[1],
+                        newList = section.children()[2];
+
+                    expect(section.children().length).to.equal(3, 'section has three children');
+                    expect(oldItem1.children()[0].getText()).to.equal('0', 'first item extracted');
+                    expect(oldItem2.children()[0].getText()).to.equal('1', 'second item extracted');
+                    expect(newList.is('list')).to.equal(true, 'list lies below extracted item');
+                    expect(newList.children().length).to.equal(1, 'list has now one child');
+                });
+
+                it('puts extracted items below the list if ending item is the last one', function() {
+                    var c = canvas.fromXML('\
+                        <section>\
+                            <div class="list.items">\
+                                <div class="item">0</div>\
+                                <div class="item">1</div>\
+                                <div class="item">2</div>\
+                            </div>\
+                        </section>'),
+                        list = c.doc().children()[0],
+                        item1 = list.children()[0],
+                        item2 = list.children()[1],
+                        item3 = list.children()[2];
+
+                    c.list.extractItems({element1: item2, element2: item3});
+
+                    var section = c.doc(),
+                        oldItem1 = section.children()[1],
+                        oldItem2 = section.children()[2],
+                        newList = section.children()[0];
+
+                    expect(section.children().length).to.equal(3, 'section has three children');
+                    expect(oldItem1.children()[0].getText()).to.equal('1', 'first item extracted');
+                    expect(oldItem2.children()[0].getText()).to.equal('2', 'second item extracted');
+                    expect(newList.is('list')).to.equal(true, 'list lies above extracted item');
+                    expect(newList.children().length).to.equal(1, 'list has now one child');
+                });
+
+                it('removes list if all its items are extracted', function() {
+                    var c = canvas.fromXML('\
+                        <section>\
+                            <div class="list.items">\
+                                <div class="item">some item</div>\
+                                <div class="item">some item 2</div>\
+                            </div>\
+                        </section>'),
+                        list = c.doc().children()[0],
+                        item1 = list.children()[0],
+                        item2 = list.children()[1];
+
+                    c.list.extractItems({element1: item1, element2: item2});
+
+                    var section = c.doc(),
+                        list1 = section.children()[0],
+                        oldItem1 = section.children()[0],
+                        oldItem2 = section.children()[1];
+
+                    expect(section.children().length).to.equal(2, 'section contains two children');
+                    expect(oldItem1.children()[0].getText()).to.equal('some item');
+                    expect(oldItem2.children()[0].getText()).to.equal('some item 2');
+                });
+
+                it('creates two lists with extracted items in the middle if extracting from the middle of the list - nested case' , function() {
+                    var c = canvas.fromXML('\
+                        <section>\
+                            <div class="list.items">\
+                                <div class="item">0</div>\
+                                <div class="item">\
+                                    <div class="list.items">\
+                                        <div class="item">1.1</div>\
+                                        <div class="item">1.2</div>\
+                                        <div class="item">1.3</div>\
+                                    </div>\
+                                </div>\
+                                <div class="item">2</div>\
+                            </div>\
+                        </section>'),
+                        list = c.doc().children()[0],
+                        nestedList = list.children()[1].children()[0],
+                        nestedListItem = nestedList.children()[1];
+
+                    c.list.extractItems({element1: nestedListItem, element2: nestedListItem});
+
+                    var section = c.doc(),
+                        list = section.children()[0],
+                        item1 = list.children()[0],
+                        item2 = list.children()[1], //
+                        item3 = list.children()[2],
+                        item4 = list.children()[3], //
+                        item5 = list.children()[4],
+                        nestedList1 = item2.children()[0],
+                        nestedList2 = item4.children()[0];
+
+                    expect(list.children().length).to.equal(5, 'top list has five items');
+                    
+                    expect(item1.children()[0].getText()).to.equal('0', 'first item ok');
+
+                    expect(item2.getWlxmlClass()).to.equal('item', 'first nested list is still wrapped in item element');
+                    expect(nestedList1.children().length).to.equal(1, 'first nested list is left with one child');
+                    expect(nestedList1.children()[0].children()[0].getText()).to.equal('1.1', 'first nested list item left alone');
+                    
+                    expect(item3.children()[0].getText()).to.equal('1.2', 'third item ok');
+
+                    expect(item4.getWlxmlClass()).to.equal('item', 'second nested list is still wrapped in item element');
+                    expect(nestedList2.children().length).to.equal(1, 'second nested list is left with one child');
+                    expect(nestedList2.children()[0].children()[0].getText()).to.equal('1.3', 'second nested list item left alone');
+
+                    expect(item5.children()[0].getText()).to.equal('2', 'last item ok');
+                });
+
+                it('puts extracted items below the list if ending item is the last one - nested case' , function() {
+                    var c = canvas.fromXML('\
+                        <section>\
+                            <div class="list.items">\
+                                <div class="item">0</div>\
+                                <div class="item">\
+                                    <div class="list.items">\
+                                        <div class="item">1.1</div>\
+                                        <div class="item">1.2</div>\
+                                        <div class="item">1.3</div>\
+                                    </div>\
+                                </div>\
+                                <div class="item">2</div>\
+                            </div>\
+                        </section>'),
+                        list = c.doc().children()[0],
+                        nestedList = list.children()[1].children()[0],
+                        nestedListItem1 = nestedList.children()[1],
+                        nestedListItem2 = nestedList.children()[2];
+
+                    c.list.extractItems({element1: nestedListItem1, element2: nestedListItem2});
+
+                    var section = c.doc(),
+                        list = section.children()[0],
+                        item1 = list.children()[0],
+                        item2 = list.children()[1],
+                        item3 = list.children()[2],
+                        item4 = list.children()[3],
+                        item5 = list.children()[4];
+                    nestedList = item2.children()[0];
+
+                    expect(list.children().length).to.equal(5, 'top list has five items');
+                    expect(item1.children()[0].getText()).to.equal('0', 'first item ok');
+                    expect(item2.getWlxmlClass()).to.equal('item', 'nested list is still wrapped in item element');
+                    expect(nestedList.children().length).to.equal(1, 'nested list is left with one child');
+                    expect(nestedList.children()[0].children()[0].getText()).to.equal('1.1', 'nested list item left alone');
+                    expect(item3.children()[0].getText()).to.equal('1.2', 'third item ok');
+                    expect(item4.children()[0].getText()).to.equal('1.3', 'fourth item ok');
+                    expect(item5.children()[0].getText()).to.equal('2', 'fifth item ok');
+                });
+
+                it('puts extracted items above the list if starting item is the first one - nested case' , function() {
+                    var c = canvas.fromXML('\
+                        <section>\
+                            <div class="list.items">\
+                                <div class="item">0</div>\
+                                <div class="item">\
+                                    <div class="list.items">\
+                                        <div class="item">1.1</div>\
+                                        <div class="item">1.2</div>\
+                                        <div class="item">1.3</div>\
+                                    </div>\
+                                </div>\
+                                <div class="item">2</div>\
+                            </div>\
+                        </section>'),
+                        list = c.doc().children()[0],
+                        nestedList = list.children()[1].children()[0],
+                        nestedListItem1 = nestedList.children()[0],
+                        nestedListItem2 = nestedList.children()[1];
+
+                    c.list.extractItems({element1: nestedListItem1, element2: nestedListItem2});
+
+                    var section = c.doc(),
+                        list = section.children()[0],
+                        item1 = list.children()[0],
+                        item2 = list.children()[1],
+                        item3 = list.children()[2],
+                        item4 = list.children()[3],
+                        item5 = list.children()[4];
+                    nestedList = item4.children()[0];
+
+                    expect(list.children().length).to.equal(5, 'top list has five items');
+                    expect(item1.children()[0].getText()).to.equal('0', 'first item ok');
+                    expect(item2.children()[0].getText()).to.equal('1.1', 'second item ok');
+                    expect(item3.children()[0].getText()).to.equal('1.2', 'third item ok');
+                    
+                    expect(item4.getWlxmlClass()).to.equal('item', 'nested list is still wrapped in item element');
+                    expect(nestedList.children().length).to.equal(1, 'nested list is left with one child');
+                    expect(nestedList.children()[0].children()[0].getText()).to.equal('1.3', 'nested list item left alone');
+                    expect(item5.children()[0].getText()).to.equal('2', 'fifth item ok');
+                });
+
+                it('removes list if all its items are extracted - nested case', function() {
+                    var c = canvas.fromXML('\
+                        <section>\
+                            <div class="list.items">\
+                                <div class="item">0</div>\
+                                <div class="item">\
+                                    <div class="list.items">\
+                                        <div class="item">1.1</div>\
+                                        <div class="item">1.2</div>\
+                                    </div>\
+                                </div>\
+                                <div class="item">2</div>\
+                            </div>\
+                        </section>'),
+                        list = c.doc().children()[0],
+                        nestedList = list.children()[1].children()[0],
+                        nestedListItem1 = nestedList.children()[0],
+                        nestedListItem2 = nestedList.children()[1];
+
+                    c.list.extractItems({element1: nestedListItem1, element2: nestedListItem2});
+
+                    var section = c.doc(),
+                        list = section.children()[0],
+                        item1 = list.children()[0],
+                        item2 = list.children()[1],
+                        item3 = list.children()[2],
+                        item4 = list.children()[3];
+
+                    expect(list.children().length).to.equal(4, 'top list has four items');
+                    expect(item1.children()[0].getText()).to.equal('0', 'first item ok');
+                    expect(item2.children()[0].getText()).to.equal('1.1', 'second item ok');
+                    expect(item3.children()[0].getText()).to.equal('1.2', 'third item ok');
+                    expect(item4.children()[0].getText()).to.equal('2', 'fourth item ok');
+                });
+
+                it('extracts items out of outer most list when merge flag is set to false', function() {
+                    var c = canvas.fromXML('\
+                        <section>\
+                            <div class="list.items">\
+                                <div class="item">0</div>\
+                                <div class="item">\
+                                    <div class="list.items">\
+                                        <div class="item">1.1</div>\
+                                        <div class="item">1.2</div>\
+                                    </div>\
+                                </div>\
+                                <div class="item">2</div>\
+                            </div>\
+                        </section>'),
+                        section = c.doc(),
+                        list = section.children()[0],
+                        nestedList = list.children()[1].children()[0],
+                        nestedListItem = nestedList.children()[0];
+
+                    var test = c.list.extractItems({element1: nestedListItem, element2: nestedListItem, merge: false});
+
+                    expect(test).to.equal(true, 'extraction status ok');
+
+                    var sectionChildren = section.children(),
+                        extractedItem = sectionChildren[1];
+
+                    expect(sectionChildren.length).to.equal(3, 'section has three children');
+                    expect(sectionChildren[0].is('list')).to.equal(true, 'first child is a list');
+
+                    expect(extractedItem.getWlxmlTag()).to.equal('div', 'extracted item is a wlxml div');
+                    expect(extractedItem.getWlxmlClass()).to.equal(undefined, 'extracted item has no wlxml class');
+                    expect(extractedItem.children()[0].getText()).to.equal('1.1', 'extracted item ok');
+                    expect(sectionChildren[2].is('list')).to.equal(true, 'second child is a list');
+                });
+            });
+        });
+
+    });
+
+    describe('Cursor', function() {
+
+        var getSelection;
+
+        var findTextNode = function(inside, text) {
+            var nodes = inside.find(':not(iframe)').addBack().contents().filter(function() {
+                return this.nodeType === Node.TEXT_NODE && this.data === text;
+            });
+            if(nodes.length)
+                return nodes[0];
+            return null;
+        }
+
+        beforeEach(function() {
+            getSelection = sinon.stub(window, 'getSelection');
+        });
+
+        afterEach(function() {
+            getSelection.restore();
+        });
+
+        it('returns position when browser selection collapsed', function() {
+            var c = canvas.fromXML('<section>Alice has a cat</section>'),
+                dom = c.doc().dom(),
+                text = findTextNode(dom, 'Alice has a cat');
+
+            expect(text.nodeType).to.equal(Node.TEXT_NODE, 'correct node selected');
+            expect($(text).text()).to.equal('Alice has a cat');
+
+            getSelection.returns({
+                anchorNode: text,
+                focusNode: text,
+                anchorOffset: 5,
+                focusOffset: 5,
+                isCollapsed: true
+            });
+            var cursor = c.getCursor(),
+                position = cursor.getPosition();
+
+            expect(cursor.isSelecting()).to.equal(false, 'cursor is not selecting anything');
+            expect(position.element.getText()).to.equal('Alice has a cat');
+            expect(position.offset).to.equal(5);
+            expect(position.offsetAtEnd).to.equal(false, 'offset is not at end');
+
+            getSelection.returns({
+                anchorNode: text,
+                focusNode: text,
+                anchorOffset: 15,
+                focusOffset: 15,
+                isCollapsed: true
+            });
+
+            expect(cursor.getPosition().offsetAtEnd).to.equal(true, 'offset at end');
+        });
+
+        it('returns boundries of selection when browser selection not collapsed', function() {
+            var c = canvas.fromXML('<section>Alice <span>has</span> a <span>big</span> cat</section>'),
+                dom = c.doc().dom(),
+                text = {
+                    alice: findTextNode(dom, 'Alice '),
+                    has: findTextNode(dom, 'has'),
+                    cat: findTextNode(dom, ' cat')
+                },
+                cursor = c.getCursor(),
+                aliceElement = c.getDocumentElement(text.alice),
+                catElement = c.getDocumentElement(text.cat);
+
+
+                [
+                    {focus: text.alice, focusOffset: 1, anchor: text.cat,   anchorOffset: 2, selectionAnchor: catElement},
+                    {focus: text.cat,   focusOffset: 2, anchor: text.alice, anchorOffset: 1, selectionAnchor: aliceElement}
+                ].forEach(function(s, idx) {
+                    getSelection.returns({isColapsed: false, anchorNode: s.anchor, anchorOffset: s.anchorOffset, focusNode: s.focus, focusOffset: s.focusOffset});
+
+                    var selectionStart = cursor.getSelectionStart(),
+                        selectionEnd = cursor.getSelectionEnd(),
+                        selectionAnchor = cursor.getSelectionAnchor();
+
+                    expect(cursor.isSelecting()).to.equal(true, 'cursor is selecting');
+                    expect(selectionStart.element.sameNode(aliceElement)).to.equal(true, '"Alice" is the start of the selection ' + idx);
+                    expect(selectionStart.offset).to.equal(1, '"Alice" offset ok' + idx);
+                    expect(selectionEnd.element.sameNode(catElement)).to.equal(true, '"Cat" is the start of the selection ' + idx);
+                    expect(selectionEnd.offset).to.equal(2, '"Cat" offset ok' + idx);
+                    expect(selectionAnchor.element.sameNode(s.selectionAnchor)).to.equal(true, 'anchor ok');
+                    expect(selectionAnchor.offset).to.equal(s.anchorOffset, 'anchor offset ok');
+                });
+        });
+
+        it('recognizes when browser selection boundries lies in sibling DocumentTextElements', function() {
+            var c = canvas.fromXML('<section>Alice <span>has</span> a <span>big</span> cat</section>'),
+                dom = c.doc().dom(),
+                text = {
+                    alice: findTextNode(dom, 'Alice '),
+                    has: findTextNode(dom, 'has'),
+                    a: findTextNode(dom, ' a '),
+                    big: findTextNode(dom, 'big'),
+                    cat: findTextNode(dom, ' cat'),
+                },
+                cursor = c.getCursor();
+
+            expect($(text.alice).text()).to.equal('Alice ');
+            expect($(text.has).text()).to.equal('has');
+            expect($(text.a).text()).to.equal(' a ');
+            expect($(text.big).text()).to.equal('big');
+            expect($(text.cat).text()).to.equal(' cat');
+
+            getSelection.returns({anchorNode: text.alice, focusNode: text.a});
+            expect(cursor.isSelectingSiblings()).to.equal(true, '"Alice" and "a" are children');
+
+            getSelection.returns({anchorNode: text.alice, focusNode: text.cat});
+            expect(cursor.isSelectingSiblings()).to.equal(true, '"Alice" and "cat" are children');
+
+            getSelection.returns({anchorNode: text.alice, focusNode: text.has});
+            expect(cursor.isSelectingSiblings()).to.equal(false, '"Alice" and "has" are not children');
+
+            getSelection.returns({anchorNode: text.has, focusNode: text.big});
+            expect(cursor.isSelectingSiblings()).to.equal(false, '"has" and "big" are not children');
+            
+        })
+    });
+
+    describe('Serializing document to WLXML', function() {
+        it('keeps document intact when no changes have been made', function() {
+            var xmlIn = '<section>Alice<div>has</div>a <span class="uri" meta-uri="http://cat.com">cat</span>!</section>',
+                c = canvas.fromXML(xmlIn),
+                xmlOut = c.toXML();
+
+            var parser = new DOMParser(),
+                input = parser.parseFromString(xmlIn, "application/xml").childNodes[0],
+                output = parser.parseFromString(xmlOut, "application/xml").childNodes[0];
+            
+            expect(input.isEqualNode(output)).to.be.true;
+        });
+
+        it('keeps arbitrary node attributes intact', function() {
+            var xmlIn = '<section a="1" xmlns:dcterms="http://purl.org/dc/terms/"></section>',
+                $xmlOut = $(canvas.fromXML(xmlIn).toXML());
+
+            expect($xmlOut.attr('a')).to.equal('1');
+            expect($xmlOut.attr('xmlns:dcterms')).to.equal('http://purl.org/dc/terms/');
+        });
+
+        it('doesn\' serialize meta attribute if its empty', function() {
+            var c;
+
+            c = canvas.fromXML('<section class="uri" meta-uri="some.uri"></section>');
+            c.doc().setWlxmlMetaAttr('uri', '');
+            expect($(c.toXML()).attr('meta-uri')).to.equal(undefined, 'overriding attribute with zero length string');
+
+            c = canvas.fromXML('<section class="uri"></section>');
+            c.doc().setWlxmlMetaAttr('uri', '');
+            expect($(c.toXML()).attr('meta-uri')).to.equal(undefined, 'setting attribute to zero length string');
+        });
+
+        describe('output xml', function() {
+            it('keeps entities intact', function() {
+                var xmlIn = '<section>&lt; &gt;</section>',
+                    c = canvas.fromXML(xmlIn),
+                    xmlOut = c.toXML();
+                expect(xmlOut).to.equal(xmlIn);
+            });
+            it('keeps entities intact when they form html/xml', function() {
+                var xmlIn = '<section>&lt;abc&gt;</section>',
+                    c = canvas.fromXML(xmlIn),
+                    xmlOut = c.toXML();
+                expect(xmlOut).to.equal(xmlIn);
+            });
+        });
+
+        describe('formatting output xml', function() {
+            /*it('keeps white spaces at the edges of input xml', function() {
+                var xmlIn = '  <section></section>  ',
+                c = canvas.fromXML(xmlIn),
+                xmlOut = c.toXML();
+
+                expect(xmlOut.substr(4)).to.equal('   <', 'start');
+                expect(xmlOut.substr(-2)).to.equal('>  ', 'end');
+            });*/
+            it('keeps white space between XML nodes', function() {
+                var xmlIn = '<section>\n\n\n<div></div>\n\n\n<div></div>\n\n\n</section>',
+                c = canvas.fromXML(xmlIn),
+                xmlOut = c.toXML();
+
+                var partsIn = xmlIn.split('\n\n\n'),
+                    partsOut = xmlOut.split('\n\n\n');
+                
+                expect(partsIn).to.deep.equal(partsOut);
+            });
+
+            it('keeps white space between XML nodes - inline case', function() {
+                var xmlIn = '<section>\n\n\n<span></span>\n\n\n<span></span>\n\n\n</section>',
+                c = canvas.fromXML(xmlIn);
+                
+                var xmlOut = c.toXML();
+
+                var partsIn = xmlIn.split('\n\n\n'),
+                    partsOut = xmlOut.split('\n\n\n');
+                
+                expect(partsIn).to.deep.equal(partsOut);
+            });
+
+            it('keeps white space at the beginning of text', function() {
+                var xmlIn = '<section>    abc<div>some div</div>    abc</section>',
+                    c = canvas.fromXML(xmlIn),
+                    xmlOut = c.toXML();
+
+                expect(xmlOut).to.equal(xmlIn);
+            });
+
+            it('nests new children block elements', function() {
+                var c = canvas.fromXML('<section></section>');
+    
+                c.doc().append({tag: 'header'});
+
+                var xmlOut = c.toXML();
+                expect(xmlOut.split('\n  ')[0]).to.equal('<section>', 'nesting start ok');
+                expect(xmlOut.split('\n').slice(-1)[0]).to.equal('</section>', 'nesting end ok');
+
+            });
+
+            it('doesn\'t nest new children inline elements', function() {
+                var c = canvas.fromXML('<section></section>');
+    
+                c.doc().append({tag: 'span'});
+
+                var xmlOut = c.toXML();
+                expect(xmlOut).to.equal('<section><span></span></section>');
+            });
+
+            it('keeps original white space at the end of text', function() {
+                
+                var xmlIn = '<header>    Some text ended with white space \
+                \
+                <span class="uri">Some text</span> some text\
+            \
+            </header>',
+                    c = canvas.fromXML(xmlIn);
+
+            var xmlOut = c.toXML();
+            console.log(xmlOut);
+            expect(xmlOut).to.equal(xmlIn);
+            });
+
+            it('keeps white space around text node', function() {
+                var xmlIn = '<section>\
+                <header>header1</header>\
+                Some text surrounded by white space\
+                <header>header2</header>\
+            </section>',
+                    c = canvas.fromXML(xmlIn);
+
+                var xmlOut = c.toXML();
+                expect(xmlOut).to.equal(xmlIn);
+            });
+
+            it('keeps white space around text node - last node case', function() {
+                var xmlIn = '<section>\
+                <header>header</header>\
+                    \
+                Some text surrounded by white space\
+                    \
+            </section>',
+                    c = canvas.fromXML(xmlIn);
+
+                var xmlOut = c.toXML();
+                expect(xmlOut).to.equal(xmlIn);
+            });
+
+            it('keeps white space after detaching text element', function() {
+                var xmlIn = '<section><header>header</header>\n\
+                    \n\
+                text1\n\
+                    \n\
+            </section>',
+                    expectedXmlOut = '<section><header>header</header>\n\
+                    \n\
+                \n\
+                    \n\
+            </section>',
+                    c = canvas.fromXML(xmlIn),
+                    children = c.doc().children(),
+                    text = children[children.length-1];
+                
+                expect(text.getText()).to.equal('text1');
+
+                text.detach();
+
+                var xmlOut = c.toXML();
+                expect(xmlOut).to.equal(expectedXmlOut);
+            });
+
+        })
+    })
+});
+
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/documentCanvas/canvas/documentElement.js b/src/editor/modules/documentCanvas/canvas/documentElement.js
new file mode 100644 (file)
index 0000000..0627bc1
--- /dev/null
@@ -0,0 +1,669 @@
+define([
+'libs/jquery',
+'libs/underscore',
+'modules/documentCanvas/classAttributes',
+'modules/documentCanvas/canvas/utils',
+'modules/documentCanvas/canvas/widgets',
+'modules/documentCanvas/canvas/wlxmlManagers'
+], function($, _, classAttributes, utils, widgets, wlxmlManagers) {
+    
+'use strict';
+
+
+// DocumentElement represents a text or an element node from WLXML document rendered inside Canvas
+var DocumentElement = function(htmlElement, canvas) {
+    if(arguments.length === 0)
+        return;
+    this.canvas = canvas;
+    this._setupDOMHandler(htmlElement);
+}
+
+var elementTypeFromParams = function(params) {
+    return params.text !== undefined ? DocumentTextElement : DocumentNodeElement;
+
+};
+
+$.extend(DocumentElement, {
+    create: function(params, canvas) {
+        return elementTypeFromParams(params).create(params);
+    },
+
+    createDOM: function(params) {
+        return elementTypeFromParams(params).createDOM(params);
+    },
+
+    fromHTMLElement: function(htmlElement, canvas) {
+        var $element = $(htmlElement);
+        if(htmlElement.nodeType === Node.ELEMENT_NODE && $element.attr('document-node-element') !== undefined)
+            return DocumentNodeElement.fromHTMLElement(htmlElement, canvas);
+        if($element.attr('document-text-element') !== undefined || (htmlElement.nodeType === Node.TEXT_NODE && $element.parent().attr('document-text-element') !== undefined))
+            return DocumentTextElement.fromHTMLElement(htmlElement, canvas);
+        return undefined;
+    }
+});
+
+$.extend(DocumentElement.prototype, {
+    _setupDOMHandler: function(htmlElement) {
+        this.$element = $(htmlElement);
+    },
+    bound: function() {
+        return $.contains(document.documentElement, this.dom()[0]);
+    },
+    dom: function() {
+        return this.$element;
+    },
+    parent: function() {
+        var parents = this.$element.parents('[document-node-element]');
+        if(parents.length)
+            return DocumentElement.fromHTMLElement(parents[0], this.canvas);
+        return null;
+    },
+
+    parents: function() {
+        var parents = [],
+            parent = this.parent();
+        while(parent) {
+            parents.push(parent);
+            parent = parent.parent();
+        }
+        return parents;
+    },
+
+    sameNode: function(other) {
+        return other && (typeof other === typeof this) && other.dom()[0] === this.dom()[0];
+    },
+
+    wrapWithNodeElement: function(wlxmlNode) {
+        var wrapper = DocumentNodeElement.create({tag: wlxmlNode.tag, klass: wlxmlNode.klass}, this);
+        this.dom().replaceWith(wrapper.dom());
+        wrapper.append(this);
+        return wrapper;
+    },
+
+    markAsCurrent: function() {
+        this.canvas.markAsCurrent(this);
+    },
+
+    getVerticallyFirstTextElement: function() {
+        var toret;
+        this.children().some(function(child) {
+            if(!child.isVisible())
+                return false; // continue
+            if(child instanceof DocumentTextElement) {
+                toret = child;
+                return true; // break
+            } else {
+                toret = child.getVerticallyFirstTextElement();
+                if(toret)
+                    return true; // break
+            }
+        });
+        return toret;
+    },
+
+    getPreviousTextElement: function(includeInvisible) {
+        return this.getNearestTextElement('above', includeInvisible);
+    },
+
+    getNextTextElement: function(includeInvisible) {
+        return this.getNearestTextElement('below', includeInvisible);
+    },
+
+    getNearestTextElement: function(direction, includeInvisible) {
+        includeInvisible = includeInvisible !== undefined ? includeInvisible : false;
+        var selector = '[document-text-element]' + (includeInvisible ? '' : ':visible');
+        return this.canvas.getDocumentElement(utils.nearestInDocumentOrder(selector, direction, this.dom()[0]));
+    },
+
+    isVisible: function() {
+        return this instanceof DocumentTextElement || this.getWlxmlTag() !== 'metadata';
+    },
+
+    isInsideList: function() {
+        return this.parents().some(function(parent) {
+            return parent.is('list');
+        });
+    },
+
+    exec: function(method) {
+        var manager = this.data('_wlxmlManager');
+        if(manager[method])
+            return manager[method].apply(manager, Array.prototype.slice.call(arguments, 1));
+    }
+});
+
+
+// DocumentNodeElement represents an element node from WLXML document rendered inside Canvas
+var DocumentNodeElement = function(htmlElement, canvas) {
+    DocumentElement.call(this, htmlElement, canvas);
+};
+
+$.extend(DocumentNodeElement, {
+    createDOM: function(params, canvas) {
+        var dom = $('<div>')
+                .attr('document-node-element', ''),
+            widgetsContainer = $('<div>')
+                .addClass('canvas-widgets')
+                .attr('contenteditable', false),
+            container = $('<div>')
+                .attr('document-element-content', '');
+        
+        dom.append(widgetsContainer, container);
+        // Make sure widgets aren't navigable with arrow keys
+        widgetsContainer.find('*').add(widgetsContainer).attr('tabindex', -1);
+
+        var element = this.fromHTMLElement(dom[0], canvas);
+
+        element.setWlxml({tag: params.tag, klass: params.klass});
+        if(params.meta) {
+            _.keys(params.meta).forEach(function(key) {
+                element.setWlxmlMetaAttr(key, params.meta[key]);
+            });
+        }
+        element.data('other-attrs', params.others);
+
+        if(params.rawChildren && params.rawChildren.length) {
+            container.append(params.rawChildren);
+        } else if(params.prepopulateOnEmpty) {
+            element.append(DocumentTextElement.create({text: ''}));
+        }
+        return dom;
+    },
+
+    create: function(params, canvas) {
+        return this.fromHTMLElement(this.createDOM(params, canvas)[0], canvas);
+    },
+
+    fromHTMLElement: function(htmlElement, canvas) {
+        return new this(htmlElement, canvas);
+    }
+});
+
+var manipulate = function(e, params, action) {
+    var element;
+    if(params instanceof DocumentElement) {
+        element = params;
+    } else {
+        element = DocumentElement.create(params);
+    }
+    var target = (action === 'append' || action === 'prepend') ? e._container() : e.dom();
+    target[action](element.dom());
+    return element;
+};
+
+DocumentNodeElement.prototype = new DocumentElement();
+
+
+$.extend(DocumentNodeElement.prototype, {
+    _container: function() {
+        return this.dom().children('[document-element-content]');
+    },
+    detach: function() {
+        var parent = this.parent();
+        if(!parent)
+            return;
+
+        var parentChildren = parent.children(),
+            myIdx = parent.childIndex(this);
+
+        if(myIdx > 0 && myIdx < parentChildren.length) {
+            if((parentChildren[myIdx-1] instanceof DocumentTextElement) && (parentChildren[myIdx+1] instanceof DocumentTextElement)) {
+                parentChildren[myIdx-1].appendText(parentChildren[myIdx+1].getText());
+                parentChildren[myIdx+1].detach();
+            }
+        }
+        this.dom().detach();
+        this.canvas = null;
+        return this;
+    },
+    unwrapContents: function() {
+        var parent = this.parent();
+        if(!parent)
+            return;
+
+        var parentChildren = parent.children(),
+            myChildren = this.children(),
+            myIdx = parent.childIndex(this);
+
+        if(myChildren.length === 0)
+            return this.detach();
+
+        var moveLeftRange, moveRightRange, leftMerged;
+
+        if(myIdx > 0 && (parentChildren[myIdx-1] instanceof DocumentTextElement) && (myChildren[0] instanceof DocumentTextElement)) {
+            parentChildren[myIdx-1].appendText(myChildren[0].getText());
+            myChildren[0].detach();
+            moveLeftRange = true;
+            leftMerged = true;
+        } else {
+            leftMerged = false;
+        }
+
+        if(!(leftMerged && myChildren.length === 1)) {
+            if(myIdx < parentChildren.length - 1 && (parentChildren[myIdx+1] instanceof DocumentTextElement) && (myChildren[myChildren.length-1] instanceof DocumentTextElement)) {
+                parentChildren[myIdx+1].prependText(myChildren[myChildren.length-1].getText());
+                myChildren[myChildren.length-1].detach();
+                moveRightRange = true;
+            }
+        }
+
+        var childrenLength = this.children().length;
+        this.children().forEach(function(child) {
+            this.before(child);
+        }.bind(this));
+
+        this.detach();
+
+        return {
+            element1: parent.children()[myIdx + (moveLeftRange ? -1 : 0)],
+            element2: parent.children()[myIdx + childrenLength-1 + (moveRightRange ? 1 : 0)]
+        };
+    },
+    data: function() {
+        var dom = this.dom(),
+            args = Array.prototype.slice.call(arguments, 0);
+        if(args.length === 2 && args[1] === undefined)
+            return dom.removeData(args[0]);
+        return dom.data.apply(dom, arguments);
+    },
+    toXML: function(level) {
+        var node = $('<' + this.getWlxmlTag() + '>');
+
+        if(this.getWlxmlClass())
+            node.attr('class', this.getWlxmlClass());
+        var meta = this.getWlxmlMetaAttrs();
+        meta.forEach(function(attr) {
+            if(attr.value)
+                node.attr('meta-' + attr.name, attr.value);
+        });
+        _.keys(this.data('other-attrs') || {}).forEach(function(key) {
+            node.attr(key, this.data('other-attrs')[key]);
+        }, this);
+
+        var addFormatting = function() {
+            var toret = $('<div>');
+            var formattings = {};
+
+            if(this.data('orig-before') !== undefined) {
+                if(this.data('orig-before')) {
+                    toret.prepend(document.createTextNode(this.data('orig-before')));
+                }
+            } else if(level && this.getWlxmlTag() !== 'span') {
+                toret.append('\n' + (new Array(level * 2 + 1)).join(' '));
+            }
+
+            toret.append(node);
+
+            if(this.data('orig-after')) {
+                toret.append(document.createTextNode(this.data('orig-after')));
+            }
+
+            /* Inside node */
+            if(this.data('orig-begin')) {
+                node.prepend(this.data('orig-begin'));
+                formattings.begin = true;
+            }
+
+            if(this.data('orig-end') !== undefined) {
+                if(this.data('orig-end')) {
+                    node.append(this.data('orig-end'));
+                }
+            } else if(this.getWlxmlTag() !== 'span' && children.length){
+                node.append('\n' + (new Array(level * 2 + 1)).join(' '));
+            }
+           
+            return {parts: toret.contents(), formattings: formattings};
+        }.bind(this);
+
+        
+        
+        var children = this.children(),
+            childParts;
+
+        var formatting = addFormatting(node);
+
+        for(var i = children.length - 1; i >= 0; i--) {
+            childParts = children[i].toXML(level + 1);
+            if(typeof childParts === 'string')
+                childParts = [document.createTextNode(childParts)];
+
+            if(formatting.formattings.begin) {
+                $(node.contents()[0]).after(childParts);
+            } else
+                node.prepend(childParts);
+        }
+        return formatting.parts;
+    },
+    append: function(params) {
+        if(params.tag !== 'span')
+            this.data('orig-end', undefined);
+        return manipulate(this, params, 'append');
+    },
+    prepend: function(params) {
+        return manipulate(this, params, 'prepend');
+    },
+    before: function(params) {
+        return manipulate(this, params, 'before');
+
+    },
+    after: function(params) {
+        return manipulate(this, params, 'after');
+    },
+    children: function() {
+        var toret = [];
+        if(this instanceof DocumentTextElement)
+            return toret;
+
+
+        var elementContent = this._container().contents();
+        var element = this;
+        elementContent.each(function(idx) {
+            var childElement = DocumentElement.fromHTMLElement(this, element.canvas);
+            if(childElement === undefined)
+                return true;
+            if(idx === 0 && elementContent.length > 1 && elementContent[1].nodeType === Node.ELEMENT_NODE && (childElement instanceof DocumentTextElement) && $.trim($(this).text()) === '')
+                return true;
+            if(idx > 0 && childElement instanceof DocumentTextElement) {
+                if(toret[toret.length-1] instanceof DocumentNodeElement && $.trim($(this).text()) === '')
+                    return true;
+            }
+            toret.push(childElement);
+        });
+        return toret;
+    },
+    childIndex: function(child) {
+        var children = this.children(),
+            toret = null;
+        children.forEach(function(c, idx) {
+            if(c.sameNode(child)) {
+                toret = idx;
+                return false;
+            }
+        });
+        return toret;
+    },
+    getWlxmlTag: function() {
+        return this._container().attr('wlxml-tag');
+    },
+    setWlxmlTag: function(tag) {
+        if(tag === this.getWlxmlTag())
+            return;
+
+        this._container().attr('wlxml-tag', tag);
+        if(!this.__updatingWlxml)
+            this._updateWlxmlManager();
+    },
+    getWlxmlClass: function() {
+        var klass = this._container().attr('wlxml-class');
+        if(klass)
+            return klass.replace(/-/g, '.');
+        return undefined;
+    },
+    setWlxmlClass: function(klass) {
+        if(klass === this.getWlxmlClass())
+            return;
+
+        this.getWlxmlMetaAttrs().forEach(function(attr) {
+            if(!classAttributes.hasMetaAttr(klass, attr.name))
+                this.dom().removeAttr('wlxml-meta-' + attr.name);
+        }, this);
+
+        if(klass)
+            this._container().attr('wlxml-class', klass.replace(/\./g, '-'));
+        else
+            this._container().removeAttr('wlxml-class');
+        if(!this.__updatingWlxml)
+            this._updateWlxmlManager();
+    },
+    setWlxml: function(params) {
+        this.__updatingWlxml = true;
+        if(params.tag !== undefined)
+            this.setWlxmlTag(params.tag);
+        if(params.klass !== undefined)
+            this.setWlxmlClass(params.klass);
+        this._updateWlxmlManager();
+        this.__updatingWlxml = false;
+    },
+    _updateWlxmlManager: function() {
+        var manager = wlxmlManagers.getFor(this);
+        this.data('_wlxmlManager', manager);
+        manager.setup();
+    },
+    is: function(what) {
+        if(what === 'list' && _.contains(['list.items', 'list.items.enum'], this.getWlxmlClass()))
+            return true;
+        return false;
+    },
+
+    getWlxmlMetaAttr: function(attr) {
+        return this.dom().attr('wlxml-meta-'+attr);
+    },
+
+    getWlxmlMetaAttrs: function() {
+        var toret = [];
+        var attrList = classAttributes.getMetaAttrsList(this.getWlxmlClass());
+        attrList.all.forEach(function(attr) {
+            toret.push({name: attr.name, value: this.getWlxmlMetaAttr(attr.name) || ''});
+        }, this);
+        return toret;
+    },
+
+    setWlxmlMetaAttr: function(attr, value) {
+        this.dom().attr('wlxml-meta-'+attr, value);
+    },
+    
+    toggleLabel: function(toggle) {
+        var displayCss = toggle ? 'inline-block' : 'none';
+        var label = this.dom().children('.canvas-widgets').find('.canvas-widget-label');
+        label.css('display', displayCss);
+        this.toggleHighlight(toggle);
+    },
+
+    toggleHighlight: function(toggle) {
+        this._container().toggleClass('highlighted-element', toggle);
+    },
+
+    toggle: function(toggle) {
+        var mng = this.data('_wlxmlManager');
+        if(mng) {
+            mng.toggle(toggle);
+        }
+    }
+});
+
+
+// DocumentNodeElement represents a text node from WLXML document rendered inside Canvas
+var DocumentTextElement = function(htmlElement, canvas) {
+    DocumentElement.call(this, htmlElement, canvas);
+};
+
+$.extend(DocumentTextElement, {
+    createDOM: function(params) {
+        return $('<div>')
+            .attr('document-text-element', '')
+            .text(params.text || utils.unicode.ZWS);
+    },
+
+    create: function(params, canvas) {
+        return this.fromHTMLElement(this.createDOM(params)[0]);
+    },
+
+    fromHTMLElement: function(htmlElement, canvas) {
+        return new this(htmlElement, canvas);
+    },
+    isContentContainer: function(htmlElement) {
+        return htmlElement.nodeType === Node.TEXT_NODE && $(htmlElement).parent().is('[document-text-element]');
+    }
+});
+
+DocumentTextElement.prototype = new DocumentElement();
+
+$.extend(DocumentTextElement.prototype, {
+    toXML: function(parent) {
+        return this.getText();
+    },
+    _setupDOMHandler: function(htmlElement) {
+        var $element = $(htmlElement);
+        if(htmlElement.nodeType === Node.TEXT_NODE)
+            this.$element = $element.parent();
+        else
+            this.$element = $element;
+    },
+    detach: function() {
+        this.dom().detach();
+        this.canvas = null;
+        return this;
+    },
+    setText: function(text) {
+        this.dom().contents()[0].data = text;
+    },
+    appendText: function(text) {
+        this.dom().contents()[0].data += text;
+    },
+    prependText: function(text) {
+        this.dom().contents()[0].data = text + this.dom().contents()[0].data;
+    },
+    getText: function() {
+        return this.dom().text().replace(utils.unicode.ZWS, '');
+    },
+    isEmpty: function() {
+        // Having at least Zero Width Space is guaranteed be Content Observer
+        return this.dom().contents()[0].data === utils.unicode.ZWS;
+    },
+    after: function(params) {
+        if(params instanceof DocumentTextElement || params.text)
+            return false;
+        var element;
+        if(params instanceof DocumentNodeElement) {
+            element = params;
+        } else {
+            element = DocumentNodeElement.create(params, this.canvas);
+        }
+        this.dom().wrap('<div>');
+        this.dom().parent().after(element.dom());
+        this.dom().unwrap();
+        return element;
+    },
+    before: function(params) {
+        if(params instanceof DocumentTextElement || params.text)
+            return false;
+        var element;
+        if(params instanceof DocumentNodeElement) {
+            element = params;
+        } else {
+            element = DocumentNodeElement.create(params, this.canvas);
+        }
+        this.dom().wrap('<div>');
+        this.dom().parent().before(element.dom());
+        this.dom().unwrap();
+        return element;
+    },
+    wrapWithNodeElement: function(wlxmlNode) {
+        if(typeof wlxmlNode.start === 'number' && typeof wlxmlNode.end === 'number') {
+            return this.canvas.wrapText({
+                inside: this.parent(),
+                textNodeIdx: this.parent().childIndex(this),
+                offsetStart: Math.min(wlxmlNode.start, wlxmlNode.end),
+                offsetEnd: Math.max(wlxmlNode.start, wlxmlNode.end),
+                _with: {tag: wlxmlNode.tag, klass: wlxmlNode.klass}
+            });
+        } else {
+            return DocumentElement.prototype.wrapWithNodeElement.call(this, wlxmlNode);
+        }
+    },
+    unwrap: function() {
+        var parent = this.parent(),
+            toret;
+        if(parent.children().length === 1) {
+            toret = parent.parent();
+            var grandParent = parent.parent();
+            if(grandParent) {
+                var grandParentChildren = grandParent.children(),
+                    idx = grandParent.childIndex(parent),
+                    prev = idx - 1 > -1 ? grandParentChildren[idx-1] : null,
+                    next = idx + 1 < grandParentChildren.length ? grandParentChildren[idx+1] : null;
+                
+                prev = (prev instanceof DocumentTextElement) ? prev : null;
+                next = (next instanceof DocumentTextElement) ? next : null;
+
+                if(prev && next) {
+                    prev.setText(prev.getText() + this.getText() + next.getText());
+                    next.detach();
+                } else if (prev || next) {
+                    var target = prev ? prev : next,
+                        newText = prev ? target.getText() + this.getText() : this.getText() + target.getText();
+                    target.setText(newText);
+                } else {
+                    parent.after(this);
+                }
+            } else {
+                parent.after(this);
+            }
+            parent.detach();
+            return toret;
+        }
+    },
+    split: function(params) {
+        var parentElement = this.parent(),
+            myIdx = parentElement.childIndex(this),
+            myCanvas = this.canvas,
+            passed = false,
+            succeedingChildren = [],
+            thisElement = this,
+            prefix = this.getText().substr(0, params.offset),
+            suffix = this.getText().substr(params.offset);
+
+        parentElement.children().forEach(function(child) {
+            if(passed)
+                succeedingChildren.push(child);
+            if(child.sameNode(thisElement))
+                passed = true;
+        });
+
+        if(prefix.length > 0)
+            this.setText(prefix);
+        else
+            this.detach();
+        
+        var newElement = DocumentNodeElement.create({tag: parentElement.getWlxmlTag(), klass: parentElement.getWlxmlClass()}, myCanvas);
+        parentElement.after(newElement);
+
+        if(suffix.length > 0)
+            newElement.append({text: suffix});
+        succeedingChildren.forEach(function(child) {
+            newElement.append(child);
+        });
+
+        return {first: parentElement, second: newElement};
+    },
+    divide: function(params) {
+        var myText = this.getText();
+
+        if(params.offset === myText.length)
+            return this.after(params);
+        if(params.offset === 0)
+            return this.before(params);
+
+        var lhsText = myText.substr(0, params.offset),
+            rhsText = myText.substr(params.offset),
+            newElement = DocumentNodeElement.create({tag: params.tag, klass: params.klass}, this.canvas),
+            rhsTextElement = DocumentTextElement.create({text: rhsText});
+
+        this.setText(lhsText);
+        this.after(newElement);
+        newElement.after(rhsTextElement);
+        return newElement;
+    },
+
+    toggleHighlight: function() {
+        // do nothing for now
+    }
+});
+
+return {
+    DocumentElement: DocumentElement,
+    DocumentNodeElement: DocumentNodeElement,
+    DocumentTextElement: DocumentTextElement
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/documentCanvas/canvas/keyboard.js b/src/editor/modules/documentCanvas/canvas/keyboard.js
new file mode 100644 (file)
index 0000000..1830017
--- /dev/null
@@ -0,0 +1,197 @@
+define([
+'modules/documentCanvas/canvas/documentElement',
+'modules/documentCanvas/canvas/utils'
+], function(documentElement, utils) {
+    
+'use strict';
+
+
+var KEYS = {
+    ENTER: 13,
+    ARROW_LEFT: 37,
+    ARROW_UP: 38,
+    ARROW_RIGHT: 39,
+    ARROW_DOWN: 40,
+    BACKSPACE: 8,
+    DELETE: 46,
+    X: 88
+};
+
+var handleKey = function(event, canvas) {
+    handlers.some(function(handler) {
+        if(handles(handler, event) && handler[event.type]) {
+            handler[event.type](event, canvas);
+            return true;
+        }
+    });
+};
+
+var handles = function(handler, event) {
+    if(handler.key === event.which)
+        return true;
+    if(handler.keys && handler.keys.indexOf(event.which) !== -1)
+        return true;
+    return false;
+};
+
+var handlers = [];
+
+
+handlers.push({key: KEYS.ENTER,
+    keydown: function(event, canvas) {
+        event.preventDefault();
+        var cursor = canvas.getCursor(),
+            position = cursor.getPosition(),
+            element = position.element;
+
+        if(!cursor.isSelecting()) {
+            if(event.ctrlKey) {
+                var added = element.after({tag: 'block'});
+                added.append({text:''});
+                canvas.setCurrentElement(added, {caretTo: 'start'});
+
+            } else {
+
+                if(!(element.parent().parent())) {
+                    return false; // top level element is unsplittable
+                }
+
+                var elements = position.element.split({offset: position.offset}),
+                    newEmpty,
+                    goto,
+                    gotoOptions;
+
+                if(position.offsetAtBeginning)
+                    newEmpty = elements.first;
+                else if(position.offsetAtEnd)
+                    newEmpty = elements.second;
+                
+                if(newEmpty) {
+                    goto = newEmpty.append(documentElement.DocumentTextElement.create({text: ''}, this));
+                    gotoOptions = {};
+                } else {
+                    goto = elements.second;
+                    gotoOptions = {caretTo: 'start'};
+                }
+
+                canvas.setCurrentElement(goto, gotoOptions);
+            }
+        }
+    }
+});
+
+handlers.push({keys: [KEYS.ARROW_UP, KEYS.ARROW_DOWN, KEYS.ARROW_LEFT, KEYS.ARROW_RIGHT],
+    keydown: function(event, canvas) {
+        var position = canvas.getCursor().getPosition(),
+            element = position.element;
+        if(element && (element instanceof documentElement.DocumentTextElement) && element.isEmpty()) {
+            var direction, caretTo;
+            if(event.which === KEYS.ARROW_LEFT  || event.which === KEYS.ARROW_UP) {
+                direction = 'above';
+                caretTo = 'end';
+            } else {
+                direction = 'below';
+                caretTo = 'start';
+            }
+            var el = canvas.getDocumentElement(utils.nearestInDocumentOrder('[document-text-element]', direction, element.dom()[0]));
+            canvas.setCurrentElement(el, {caretTo: caretTo});
+        }
+    },
+    keyup: function(event, canvas) {
+        var element = canvas.getCursor().getPosition().element,
+            caretTo = false;
+        if(!element) {
+            // Chrome hack
+            var direction;
+            if(event.which === KEYS.ARROW_LEFT  || event.which === KEYS.ARROW_UP) {
+                direction = 'above';
+                caretTo = 'end';
+            } else {
+                direction = 'below';
+                caretTo = 'start';
+            }
+            element = canvas.getDocumentElement(utils.nearestInDocumentOrder('[document-text-element]:visible', direction, window.getSelection().focusNode));
+        }
+        canvas.setCurrentElement(element, {caretTo: caretTo});
+    }
+});
+
+
+var selectsWholeTextElement = function(cursor) {
+    if(cursor.isSelecting() && cursor.getSelectionStart().offsetAtBeginning && cursor.getSelectionEnd().offsetAtEnd)
+        return true;
+    return false;
+}
+
+handlers.push({key: KEYS.X,
+    keydown: function(event, canvas) {
+        if(event.ctrlKey && selectsWholeTextElement(canvas.getCursor()))
+            event.preventDefault();
+    }
+});
+
+handlers.push({keys: [KEYS.BACKSPACE, KEYS.DELETE],
+    keydown: function(event, canvas) {
+        var cursor = canvas.getCursor(),
+            position = canvas.getCursor().getPosition(),
+            element = position.element;
+
+        if(cursor.isSelecting() && !cursor.isSelectingWithinElement()) {
+            event.preventDefault();
+            return;
+        }
+            
+        var cursorAtOperationEdge = position.offsetAtBeginning;
+        if(event.which === KEYS.DELETE) {
+            cursorAtOperationEdge = position.offsetAtEnd;
+        }
+
+        var willDeleteWholeText = function() {
+            return element.getText().length === 1 || selectsWholeTextElement(cursor);
+        }
+
+        if(willDeleteWholeText()) {
+            event.preventDefault();
+            element.setText('');
+        }
+        else if(element.isEmpty()) {
+
+            var direction = 'above',
+                caretTo = 'end';
+                
+            if(event.which === KEYS.DELETE) {
+                direction = 'below';
+                caretTo = 'start';
+            }
+
+            event.preventDefault();
+
+            var parent = element.parent(),
+                grandParent = parent ? parent.parent() : null,
+                goto;
+            if(parent.children().length === 1 && parent.children()[0].sameNode(element)) {
+                if(grandParent && grandParent.children().length === 1) {
+                    goto = grandParent.append({text: ''});
+                } else {
+                    goto = element.getNearestTextElement(direction);
+                }
+                parent.detach();
+            } else {
+                goto = element.getNearestTextElement(direction);
+                element.detach();
+            }
+            canvas.setCurrentElement(goto, {caretTo: caretTo});
+            canvas.publisher('contentChanged');
+        }
+        else if(cursorAtOperationEdge) {
+            // todo
+            event.preventDefault();
+        }
+    }
+});
+
+return {
+    handleKey: handleKey
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/documentCanvas/canvas/utils.js b/src/editor/modules/documentCanvas/canvas/utils.js
new file mode 100644 (file)
index 0000000..0eb19d0
--- /dev/null
@@ -0,0 +1,29 @@
+define([
+'libs/jquery',
+], function($) {
+    
+'use strict';
+
+
+var nearestInDocumentOrder = function(selector, direction, element) {
+    var parents = $(element).parents(),
+        parent = parents.length ? $(parents[parents.length-1]) : element;
+
+    var adj = parent.find(selector).filter(function() {
+        return this.compareDocumentPosition(element) & (direction === 'above' ? Node.DOCUMENT_POSITION_FOLLOWING : Node.DOCUMENT_POSITION_PRECEDING);
+    });
+
+    if(adj.length) {
+        return adj[direction === 'above' ? adj.length-1 : 0];
+    }
+    return null;
+}
+
+return {
+    nearestInDocumentOrder: nearestInDocumentOrder,
+    unicode: {
+        ZWS: '\u200B'
+    }
+};
+
+});
diff --git a/src/editor/modules/documentCanvas/canvas/utils.test.js b/src/editor/modules/documentCanvas/canvas/utils.test.js
new file mode 100644 (file)
index 0000000..c93e74b
--- /dev/null
@@ -0,0 +1,70 @@
+define([
+'libs/chai',
+'modules/documentCanvas/canvas/utils'
+
+], function(chai, utils) {
+
+'use strict';
+
+var expect = chai.expect;
+
+
+describe('utils.nearestInDocumentOrder', function() {
+
+
+    var tests = [
+        ['return null if no match found',
+            '<span>\
+                <span></span>\
+                <div id="b">\
+                <span></span>\
+            </span>'
+        ],
+        ['returns nearest sibling if applicable',
+            '<div n1>\
+                <div n2></div>\
+                <div n3>\
+                    <div id="a"></div>\
+                    <div id="b"></div>\
+                    <div id="c"></div>\
+                </div>\
+                <div n4></div>\
+            </div>'
+        ],
+        ['looks inside siblings children',
+            '<div>\
+                <div></div>\
+                <div>\
+                    <div></div>\
+                    <span>\
+                        <div id="a"></div>\
+                    </span>\
+                    <div id="b"></div>\
+                    <span>\
+                        <div id="c"></div>\
+                    </span>\
+                    <div></div>\
+                </div>\
+                <div></div>\
+            </div>'
+        ]
+
+
+    ];
+
+    tests.forEach(function(test) {
+        var description = test[0],
+            html = test[1];
+        it(description, function() {
+            var dom = $(html),
+                a = dom.find('#a').length ? dom.find('#a')[0] : null,
+                b = dom.find('#b')[0],
+                c = dom.find('#c').length ? dom.find('#c')[0] : null;
+            expect(utils.nearestInDocumentOrder('div', 'above', b)).to.equal(a, 'above');
+            expect(utils.nearestInDocumentOrder('div', 'below', b)).to.equal(c, 'below');
+        });
+    });
+
+});
+
+});
diff --git a/src/editor/modules/documentCanvas/canvas/widgets.js b/src/editor/modules/documentCanvas/canvas/widgets.js
new file mode 100644 (file)
index 0000000..408d2c0
--- /dev/null
@@ -0,0 +1,41 @@
+define([
+'libs/jquery',
+'utils/wlxml'
+], function($, wlxmlUtils) {
+    
+'use strict';
+
+return {
+    labelWidget: function(tag, klass) {
+        return $('<span>')
+            .addClass('canvas-widget canvas-widget-label')
+            .text(wlxmlUtils.wlxmlTagNames[tag] + (klass ? ' / ' + wlxmlUtils.wlxmlClassNames[klass] : ''));
+    },
+
+    footnoteHandler: function(clickHandler) {
+        var mydom = $('<span>')
+            .addClass('canvas-widget canvas-widget-footnote-handle')
+            .css('display', 'inline')
+            .show();
+
+        mydom.click(function(e) {
+            e.stopPropagation();
+            clickHandler();
+        });
+
+        return mydom;
+    },
+
+    hideButton: function(clickHandler) {
+        var mydom = $('<span>x</span>')
+            .addClass('canvas-widget canvas-widget-hide-button');
+        mydom.click(function(e) {
+            e.stopPropagation();
+            clickHandler();
+        });
+        return mydom;
+    }
+
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/documentCanvas/canvas/widgets.less b/src/editor/modules/documentCanvas/canvas/widgets.less
new file mode 100644 (file)
index 0000000..0f43c89
--- /dev/null
@@ -0,0 +1,60 @@
+[document-node-element] {
+    .canvas-widgets {
+        display: inline;
+        text-indent: 0;
+    }
+
+    .canvas-widget {
+        display: none;
+    }
+
+    .canvas-widget-label {
+        position: absolute;
+        display: none;
+        top: -20px;
+        left:0;
+        background-color: red;
+        color: white;
+        font-size:12px;
+        font-weight: bold;
+        padding: 1px 3px;
+        //width:300px;
+        opacity: 0.65;
+        font-family: monospace;
+        z-index:9999;
+        white-space: nowrap;
+    }
+
+
+
+    .canvas-widget-footnote-handle {
+        display: inline;
+        outline: 0px solid transparent;
+        cursor: pointer;
+        counter-increment: footnote;
+        vertical-align: super;
+        color: blue;
+        font-size: .8em;
+
+        &::before, {
+            content: "[" counter(footnote) "]";
+        }
+    }
+
+    .canvas-widget-hide-button {
+        @line_height: 12px;
+        @padding: 2px;
+        @temporary_footnote_hack: 10px;
+
+        position: absolute;
+        top: -(@line_height + 2 * @padding) + @temporary_footnote_hack;
+        right: 0;
+        line-height: @line_height;
+        padding: @padding;
+        font-weight: bold;
+        color: white;
+        background-image: linear-gradient(to bottom,#ee5f5b,#bd362f);
+        border-radius: 1px;
+        cursor: pointer;
+    }
+}
\ No newline at end of file
diff --git a/src/editor/modules/documentCanvas/canvas/wlxmlManagers.js b/src/editor/modules/documentCanvas/canvas/wlxmlManagers.js
new file mode 100644 (file)
index 0000000..d566686
--- /dev/null
@@ -0,0 +1,147 @@
+define([
+'libs/jquery',
+'modules/documentCanvas/canvas/widgets'
+], function($, widgets) {
+    
+'use strict';
+
+
+var DocumentElementWrapper = function(documentElement) {
+    
+    this.documentElement = documentElement;
+
+    this.addWidget = function(widget) {
+        documentElement.dom().children('.canvas-widgets').append(widget);
+    };
+
+    this.clearWidgets = function() {
+        documentElement.dom().children('.canvas-widgets').empty();
+    }
+
+    this.setDisplayStyle = function(displayStyle) {
+        documentElement.dom().css('display', displayStyle || '');
+        documentElement._container().css('display', displayStyle || '');
+    };
+
+    this.tag = function() {
+        return documentElement.getWlxmlTag();
+    };
+
+    this.klass = function() {
+        return documentElement.getWlxmlClass();
+    };
+
+    this.toggle = function(toggle) {
+        documentElement._container().toggle(toggle);
+    }
+
+    var eventBus = documentElement.canvas ? documentElement.canvas.eventBus :
+        {trigger: function() {}};
+    this.trigger = function() {
+        eventBus.trigger.apply(eventBus, arguments);
+    }
+
+}
+
+var getDisplayStyle = function(tag, klass) {
+    if(tag === 'metadata')
+        return 'none';
+    if(tag === 'span')
+        return 'inline';
+    if(klass === 'item')
+        return null;
+    return 'block';
+}
+
+var GenericManager = function(wlxmlElement) {
+    this.el = wlxmlElement;
+};
+
+$.extend(GenericManager.prototype, {
+    setup: function() {
+        this.el.setDisplayStyle(getDisplayStyle(this.el.tag(), this.el.klass()));
+
+        this.el.clearWidgets();
+        this.el.addWidget(widgets.labelWidget(this.el.tag(), this.el.klass()));
+
+    },
+    toggle: function(toggle) {
+        this.el.toggle(toggle);
+    }
+
+})
+
+var managers = {
+    _m: {},
+    set: function(tag, klass, manager) {
+        if(!this._m[tag])
+            this._m[tag] = {};
+        this._m[tag][klass] = manager;
+    },
+    get: function(tag,klass) {
+        if(this._m[tag] && this._m[tag][klass])
+            return this._m[tag][klass];
+        return GenericManager;
+    }
+}
+
+var FootnoteManager = function(wlxmlElement) {
+    this.el = wlxmlElement;
+};
+$.extend(FootnoteManager.prototype, {
+    setup: function() {
+        this.el.clearWidgets();
+
+        var clickHandler = function() {
+            this.toggle(true);
+        }.bind(this);
+        this.footnoteHandler = widgets.footnoteHandler(clickHandler);
+        this.el.addWidget(this.footnoteHandler);
+
+        var closeHandler = function() {
+            this.toggle(false);
+
+        }.bind(this);
+        this.hideButton = widgets.hideButton(closeHandler);
+        this.el.addWidget(this.hideButton);
+
+        this.toggle(false, {silent: true});
+    },
+    toggle: function(toggle, options) {
+        options = options || {};
+        this.hideButton.toggle(toggle);
+        this.footnoteHandler.toggle(!toggle);
+        
+        this.el.setDisplayStyle(toggle ? 'block' : 'inline');
+        this.el.toggle(toggle);
+        if(!options.silent)
+            this.el.trigger('elementToggled', toggle, this.el.documentElement);
+    }
+})
+managers.set('aside', 'footnote', FootnoteManager);
+
+
+var ListItemManager = function(wlxmlElement) {
+    this.el = wlxmlElement;
+};
+$.extend(ListItemManager.prototype, {
+    setup: function() {
+        this.el.clearWidgets();
+        this.el.addWidget(widgets.labelWidget(this.el.tag(), this.el.klass()));
+        this.el.documentElement._container().css({display: 'list-item'});
+    },
+    toggleBullet: function(toggle) {
+        this.el.documentElement._container().css({display : toggle ? 'list-item' : 'block'});
+    }
+});
+managers.set('div', 'item', ListItemManager);
+
+return {
+    getFor: function(documentElement) {
+        var wlxmlElement = new DocumentElementWrapper(documentElement);
+        return new (managers.get(wlxmlElement.tag(), wlxmlElement.klass()))(wlxmlElement);
+
+    }
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/documentCanvas/classAttributes.js b/src/editor/modules/documentCanvas/classAttributes.js
new file mode 100644 (file)
index 0000000..06c9485
--- /dev/null
@@ -0,0 +1,64 @@
+define([], function() {
+    
+'use strict';
+
+var wlxmlDict = {
+    'uri': {
+        'uri': 'string'
+    }
+};
+
+var hasMetaAttr = function(klass, attrName, dict) {
+    dict = dict || wlxmlDict;
+    if(!klass)
+        return false;
+
+    var parts = klass.split('.');
+    var partialClass = '';
+    for(var i = 0; i < parts.length; i++) {
+        partialClass += (partialClass === '' ? '' : '.') + parts[i];
+        if(dict[partialClass] && dict[partialClass][attrName])
+            return true;
+    }
+    return false;
+};
+
+var getMetaAttrsList = function(klass, dict) {
+    dict = dict || wlxmlDict;
+    klass = klass || '';
+
+    var toret = {own: [], inheritedFrom: {}, all: []};
+    var parts = klass.split('.');
+    var partialClass = '';
+    
+    var generate = function(klass) {
+        var toret = [],
+            desc = dict[klass];
+
+        if(!desc)
+            return toret;
+        
+        _.keys(desc).forEach(function(key) {
+            toret.push({name: key, type: desc[key]});
+        });
+        return toret;
+    };
+
+    toret.own = generate(klass);
+    for(var i = 0; i < parts.length; i++) {
+        partialClass += (partialClass === '' ? '' : '.') + parts[i];
+        var list = generate(partialClass);
+        if(list.length > 0) {
+            toret.inheritedFrom[partialClass] = generate(partialClass);
+            toret.all = toret.all.concat(toret.inheritedFrom[partialClass]);
+        }
+    }
+    return toret;
+};
+
+return {
+    hasMetaAttr: hasMetaAttr,
+    getMetaAttrsList: getMetaAttrsList
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/documentCanvas/commands.js b/src/editor/modules/documentCanvas/commands.js
new file mode 100644 (file)
index 0000000..043d6b7
--- /dev/null
@@ -0,0 +1,170 @@
+define([
+'modules/documentCanvas/canvas/documentElement'
+], function(documentElement) {
+    
+'use strict';
+
+
+var gridToggled = false;
+
+var commands = {
+    _cmds: {},
+    register: function(name, command) {
+        this._cmds[name] = command;
+    },
+
+    run: function(name, params, canvas) {
+        return this._cmds[name](canvas, params);
+    }
+};
+
+commands.register('unwrap-node', function(canvas) {
+    var cursor = canvas.getCursor(),
+        selectionStart = cursor.getSelectionStart(),
+        selectionEnd = cursor.getSelectionEnd(),
+        parent1 = selectionStart.element.parent() || undefined,
+        parent2 = selectionEnd.element.parent() || undefined;
+
+    if(canvas.list.areItemsOfTheSameList({element1: parent1, element2: parent2})) {
+        var selectionAnchor = cursor.getSelectionAnchor();
+        canvas.list.extractItems({element1: parent1, element2: parent2});
+        canvas.setCurrentElement(selectionAnchor.element, {caretTo: selectionAnchor.offset});
+    } else if(!cursor.isSelecting()) {
+        var toUnwrap = cursor.getPosition().element,
+            parent = toUnwrap.unwrap();
+        canvas.setCurrentElement(parent);
+    }
+});
+
+commands.register('wrap-node', function(canvas) {
+    var cursor = canvas.getCursor(),
+        selectionStart = cursor.getSelectionStart(),
+        selectionEnd = cursor.getSelectionEnd(),
+        parent1 = selectionStart.element.parent() || undefined,
+        parent2 = selectionEnd.element.parent() || undefined;
+
+    if(canvas.list.areItemsOfTheSameList({element1: parent1, element2: parent2})) {
+        canvas.list.create({element1: parent1, element2: parent2});
+    }
+});
+
+commands.register('list', function(canvas, params) {
+    var cursor = canvas.getCursor(),
+        selectionStart = cursor.getSelectionStart(),
+        selectionEnd = cursor.getSelectionEnd(),
+        parent1 = selectionStart.element.parent() || undefined,
+        parent2 = selectionEnd.element.parent() || undefined;
+
+    var selectionFocus = cursor.getSelectionFocus();
+
+    if(selectionStart.element.isInsideList() || selectionEnd.element.isInsideList()) {
+        return;
+    }
+
+    canvas.list.create({element1: parent1, element2: parent2});
+
+    canvas.setCurrentElement(selectionFocus.element, {caretTo: selectionFocus.offset});
+});
+
+commands.register('toggle-grid', function(canvas, params) {
+    canvas.doc().dom().find('[wlxml-tag]').toggleClass('rng-common-hoveredNode', params.toggle);
+    gridToggled = params.toggle;
+});
+
+commands.register('newNodeRequested', function(canvas, params) {
+    var cursor = canvas.getCursor(),
+        selectionStart = cursor.getSelectionStart(),
+        selectionEnd = cursor.getSelectionEnd();
+
+    if(cursor.isSelecting()) {
+        if(cursor.isSelectingSiblings()) {
+            if(cursor.isSelectingWithinElement()) {
+                var newElement = selectionStart.element.wrapWithNodeElement({tag: params.wlxmlTag, klass: params.wlxmlClass, start: selectionStart.offset, end: selectionEnd.offset}),
+                    caretTo = selectionStart.offset < selectionEnd.offset ? 'start' : 'end';
+                canvas.setCurrentElement(newElement.children()[0], {caretTo: caretTo});
+            }
+            else {
+                var parent = selectionStart.element.parent(),
+                    caretTo = selectionStart.element.sameNode(cursor.getSelectionAnchor().element) ? 'end' : 'start';
+
+                var wrapper = canvas.wrapText({
+                    inside: parent,
+                    _with: {tag: params.wlxmlTag, klass: params.wlxmlClass},
+                    offsetStart: selectionStart.offset,
+                    offsetEnd: selectionEnd.offset,
+                    textNodeIdx: [parent.childIndex(selectionStart.element), parent.childIndex(selectionEnd.element)]
+                });
+
+                canvas.setCurrentElement(wrapper.children()[caretTo === 0 ? 0 : wrapper.children().length - 1], {caretTo: caretTo});
+            }
+        } else {
+            var siblingParents = canvas.getSiblingParents({element1: selectionStart.element, element2: selectionEnd.element})
+            if(siblingParents) {
+                canvas.wrapElements({
+                    element1: siblingParents.element1,
+                    element2: siblingParents.element2,
+                    _with: {tag: params.wlxmlTag, klass: params.wlxmlClass}
+                });
+            }
+        }
+    } else if(canvas.getCurrentNodeElement()) {
+        var el = canvas.getCurrentNodeElement().wrapWithNodeElement({tag: params.wlxmlTag, klass: params.wlxmlClass});
+        canvas.setCurrentElement(el);
+    }
+
+
+});
+
+commands.register('footnote', function(canvas, params) {
+    var cursor = canvas.getCursor(),
+        position = cursor.getPosition(),
+        asideElement;
+        
+
+    if(cursor.isSelectingWithinElement()) {
+        asideElement = position.element.wrapWithNodeElement({tag: 'aside', klass: 'footnote', start: cursor.getSelectionStart().offset, end: cursor.getSelectionEnd().offset});
+    } else {
+        asideElement = position.element.divide({tag: 'aside', klass: 'footnote', offset: position.offset});
+        asideElement.append({text: ''});
+    }
+
+    asideElement.toggle(true);
+    canvas.setCurrentElement(asideElement);
+});
+
+commands.register('take-away-node', function(canvas) {
+    var position = canvas.getCursor().getPosition(),
+        element = position.element,
+        nodeElement = element ? element.parent() : canvas.getCurrentNodeElement();
+
+    if(!nodeElement || !(nodeElement.parent()))
+        return;
+
+
+    var range = nodeElement.unwrapContents();
+
+    if(element) {
+        var elementIsFirstChild = nodeElement.childIndex(element);
+        if(element.bound()) {
+            canvas.setCurrentElement(element, {caretTo: position.offset});
+        } else {
+            if(elementIsFirstChild) {
+                canvas.setCurrentElement(range.element1, {caretTo: 'end'});
+            } else {
+                canvas.setCurrentElement(range.element2, {caretTo: 'end'});
+            }
+        }
+    } else {
+        canvas.setCurrentElement(range.element1, {caretTo: 'start'});
+    }
+
+});
+
+
+return {
+    run: function(name, params, canvas) {
+        return commands.run(name, params, canvas);
+    }
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/documentCanvas/documentCanvas.js b/src/editor/modules/documentCanvas/documentCanvas.js
new file mode 100644 (file)
index 0000000..3ebab2e
--- /dev/null
@@ -0,0 +1,74 @@
+// Module that implements main WYSIWIG edit area
+
+define([
+'libs/underscore',
+'./canvas/canvas',
+'./commands',
+'libs/text!./template.html'], function(_, canvas3, commands, template) {
+
+'use strict';
+
+return function(sandbox) {
+
+    var canvas = canvas3.fromXML('', sandbox.publish);
+    var canvasWrapper = $(template);
+    var shownAlready = false;
+    var scrollbarPosition = 0,
+        cursorPosition;
+    
+    canvasWrapper.onShow = function() {
+        if(!shownAlready) {
+            shownAlready = true;
+            canvas.setCurrentElement(canvas.doc().getVerticallyFirstTextElement());
+        } else {
+            canvas.setCursorPosition(cursorPosition);
+            this.find('#rng-module-documentCanvas-contentWrapper').scrollTop(scrollbarPosition);
+        }
+    };
+    
+    canvasWrapper.onHide = function() {
+       scrollbarPosition = this.find('#rng-module-documentCanvas-contentWrapper').scrollTop();
+       cursorPosition = canvas.getCursor().getPosition();
+    };
+
+    /* public api */
+    return {
+        start: function() { sandbox.publish('ready'); },
+        getView: function() { 
+            return canvasWrapper;
+        },
+        setDocument: function(xml) {
+            canvas.loadWlxml(xml);
+            canvasWrapper.find('#rng-module-documentCanvas-content').empty().append(canvas.view());
+            sandbox.publish('documentSet');
+        },
+        getDocument: function() {
+            return canvas.toXML();
+        },
+        modifyCurrentNodeElement: function(attr, value) {
+            var currentNodeElement = canvas.getCurrentNodeElement();
+            if(attr === 'class' || attr === 'tag') {
+                currentNodeElement['setWlxml'+(attr[0].toUpperCase() + attr.substring(1))](value);
+            } else {
+                currentNodeElement.setWlxmlMetaAttr(attr, value);
+            }
+            sandbox.publish('currentNodeElementChanged', currentNodeElement);
+        },
+        highlightElement: function(element) {
+            element.toggleHighlight(true);
+        },
+        dimElement: function(element) {
+            element.toggleHighlight(false);
+        },
+        jumpToElement: function(element) {
+            canvas.setCurrentElement(element);
+        },
+        command: function(command, params) {
+            commands.run(command, params, canvas);
+            sandbox.publish('contentChanged');
+        }
+    };
+    
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/documentCanvas/documentCanvas.less b/src/editor/modules/documentCanvas/documentCanvas.less
new file mode 100644 (file)
index 0000000..e17dce4
--- /dev/null
@@ -0,0 +1,82 @@
+@import 'nodes.less';
+@import 'canvas/widgets.less';
+
+#rng-module-documentCanvas {
+   height: 100%;
+}
+
+#rng-module-documentCanvas-mainArea {
+   height: 100%;
+   margin-bottom: 20px;
+}
+
+#rng-module-documentCanvas-contentWrapper {
+    border-color: #ddd;
+    border-style: solid;
+    border-width: 1px;
+    float:left;
+    width: 100%;
+    height: 100%;
+    overflow-y: scroll;
+    padding: 0 10px;
+    
+    &::-webkit-scrollbar {
+        .rng-mixin-scrollbar;
+    }
+    &::-webkit-scrollbar-track {
+        .rng-mixin-scrollbar-track;
+    }
+    &::-webkit-scrollbar-thumb {
+        .rng-mixin-scrollbar-thumb;
+    }
+    &::-webkit-scrollbar-thumb:window-inactive {
+        .rng-mixin-scrollbar-thumb-window-inactive;
+    }
+    
+    .canvas-wrapper {
+        outline: 0px solid transparent;
+    }
+
+    .current-text-element {
+        outline: 1px dashed black;
+    }
+
+    .current-node-element {
+        border-color: lighten(#000, 15%);
+        border-style: solid;
+        border-width: 1px;
+    }
+
+    .highlighted-element {
+        border: 1px solid red;
+    }
+
+    counter-reset: footnote;
+}
+
+.rng-module-documentCanvas-currentNode {
+    background: #fffacd;
+    border-color: grey;
+    border-style:dashed;
+    border-width:1px;
+}
+
+.rng-module-documentCanvas-hoveredNodeTag {
+    position:absolute;
+    height:20px;
+    top:-20px;
+    left:0;
+    background: #bd362f;
+    color: white;
+    font-size:9px;
+    font-weight: normal;
+    font-style: normal;
+    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+    padding: 0 5px;
+    text-indent: 0;
+}
+
+[document-node-element] {
+    position:relative;
+    border: 1px solid white;
+}
\ No newline at end of file
diff --git a/src/editor/modules/documentCanvas/nodes.less b/src/editor/modules/documentCanvas/nodes.less
new file mode 100644 (file)
index 0000000..d02e872
--- /dev/null
@@ -0,0 +1,137 @@
+[document-text-element] {
+    font-size: 14px;
+    display: inline;
+    margin: 0 1px;
+    border: 1px solid white;
+    white-space: pre-wrap;
+}
+
+[wlxml-tag] {
+    float: none !important; /* temporaty workaround for Bootstrap's influence via [class*="span"] { float: left; } */
+    border-color: white;
+    border-style:solid;
+    border-width:1px;
+    min-height:20px;
+    position:relative;
+    text-indent: 0;
+    padding: 1px 1px;
+}
+
+[wlxml-tag=span] {
+    min-width: 10px;
+}
+
+[wlxml-tag=header] [document-text-element] {
+    font-size: 13px;
+    font-weight: bold;
+    margin: 10px 0;
+}
+
+[wlxml-tag=section] {
+    margin-top: 10px;
+    margin-bottom: 10px;
+}
+
+[wlxml-tag=section] [wlxml-tag=section] {
+    margin-left:10px;
+}
+
+[wlxml-class|="cite"] {
+    font-style: italic;
+}
+
+[wlxml-class|="cite-code"] {
+    font-family: monospace;
+}
+
+[wlxml-class|="cite-code-xml"] {
+    color: blue;
+}
+
+[wlxml-tag=header] [wlxml-class=author] [document-text-element] {
+    font-size: 14px;
+}
+
+[wlxml-tag=header] [wlxml-class=title] [document-text-element] {
+    font-size:18px;
+}
+
+[wlxml-class|="uri"] {
+    color: blue;
+    text-decoration: underline;
+}
+
+[wlxml-class|="p"] {
+    text-indent: 1.5em;
+}
+
+[wlxml-class|="emph-tech"] {
+    font-style: italic;
+}
+
+[wlxml-tag=metadata] {
+    display:none;
+}
+
+[wlxml-class="list-items"] {
+
+     [wlxml-class="item"] {
+        display: list-item;
+        margin-left: 10px;
+        padding-left: 5px;
+    }
+}
+
+[wlxml-class="item"] {
+    [wlxml-class="list-items"] {
+        display: block;
+    }
+}
+
+
+[wlxml-class="list-items-enum"] {
+
+    counter-reset: myitem;
+
+    > [wlxml-class="item"] {
+        counter-increment: myitem;
+        margin-left: 10px;
+        padding-left: 5px;
+        
+        &:before {
+            content: counter(myitem) '. ';
+            margin-right:10px;
+            padding-right:10px;
+        }
+    }
+}
+
+.canvas-silent-item {
+    display: block !important;
+    counter-increment: none !important;
+    &:before {
+        content: normal !important;
+    }
+}
+
+[wlxml-class="table"] {
+
+    display: table;
+    border: 1px solid black;
+
+    [wlxml-class="row"] {
+        display: table-row;
+        border: 1px solid black;
+    }
+    [wlxml-class="cell"] {
+        display: table-cell;
+        border: 1px solid black;
+        padding: 5px;
+    }
+
+}
+
+[wlxml-tag="aside"] {
+    margin-top: 10px;
+    margin-bottom: 10px;
+}
\ No newline at end of file
diff --git a/src/editor/modules/documentCanvas/template.html b/src/editor/modules/documentCanvas/template.html
new file mode 100644 (file)
index 0000000..63dc331
--- /dev/null
@@ -0,0 +1,7 @@
+<div id="rng-module-documentCanvas">
+    <div id="rng-module-documentCanvas-mainArea">
+        <div id="rng-module-documentCanvas-contentWrapper">
+            <div id="rng-module-documentCanvas-content"></div>
+        </div>
+    </div>
+</div>
\ No newline at end of file
diff --git a/src/editor/modules/documentCanvas/tests/classAttributes.test.js b/src/editor/modules/documentCanvas/tests/classAttributes.test.js
new file mode 100644 (file)
index 0000000..49e0be0
--- /dev/null
@@ -0,0 +1,58 @@
+define([
+'libs/chai',
+'modules/documentCanvas/classAttributes'
+], function(chai, classAttributes) {
+   
+var stubDict = {
+    'klass': {
+        'prop': 'string'
+    },
+    'klass.sub1': {
+        'prop1': 'string'
+    },
+    'klass.sub1.sub2': {
+        'prop2': 'string'
+    }
+};
+
+var assert = chai.assert;
+
+describe('Class attributes', function() {
+    it('class has own attribute', function() {
+        assert.ok(classAttributes.hasMetaAttr('klass.sub1.sub2', 'prop2', stubDict));
+    });
+
+    it('class has attributes from parent classes', function() {
+        assert.ok(classAttributes.hasMetaAttr('klass.sub1.sub2', 'prop', stubDict));
+        assert.ok(classAttributes.hasMetaAttr('klass.sub1.sub2', 'prop1', stubDict));
+    });
+
+    it('list of class meta attributes', function() {
+        var attrList = classAttributes.getMetaAttrsList('klass.sub1.sub2', stubDict);
+
+        assert.deepEqual(attrList.own, [{name: 'prop2', type: 'string'}]);
+        assert.deepEqual(attrList.inheritedFrom['klass.sub1'], [{name: 'prop1', type: 'string'}]);
+        assert.deepEqual(attrList.inheritedFrom.klass, [{name: 'prop', type: 'string'}]);
+        assert.deepEqual(attrList.all.sort(), [
+            {name: 'prop', type: 'string'},
+            {name: 'prop1', type: 'string'},
+            {name: 'prop2', type: 'string'}
+            ].sort(), 'all values');
+    });
+
+    it('class without meta attrs', function() {
+        var attrList = classAttributes.getMetaAttrsList('some.class', {});
+        assert.deepEqual(attrList.own, [], 'empty own list');
+        assert.deepEqual(attrList.inheritedFrom, {}, 'empty inherited dict');
+        assert.deepEqual(attrList.all, [], 'empty all list');
+    });
+
+    it('empty class', function() {
+        var attrList = classAttributes.getMetaAttrsList('', {});
+        assert.deepEqual(attrList.own, [], 'empty own list');
+        assert.deepEqual(attrList.inheritedFrom, {}, 'empty inherited dict');
+        assert.deepEqual(attrList.all, [], 'empty all list');
+    });
+});
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/documentHistory/documentHistory.js b/src/editor/modules/documentHistory/documentHistory.js
new file mode 100644 (file)
index 0000000..f595594
--- /dev/null
@@ -0,0 +1,146 @@
+define([
+'libs/jquery',
+'libs/underscore',
+'./restoreDialog',
+'libs/text!./templates/main.html',
+'libs/text!./templates/item.html'
+], function($, _, restoreDialog, mainTemplateSrc, itemTemplateSrc) {
+
+'use strict';
+    
+return function(sandbox) {
+    
+    var dom = $(_.template(mainTemplateSrc)());
+    var domNodes = {
+        itemList: dom.find('.rng-module-documentHistory-itemsList'),
+    };
+    var itemViews = [];
+    
+    
+    dom.find('.btn.compare').click(function(e) {
+        var selected = historyItems.getSelected();
+        sandbox.publish('compare', selected[0], selected[1]);
+    });
+    
+    dom.find('.btn.restore').click(function(e) {
+        var dialog = restoreDialog.create();
+        dialog.on('restore', function(event) {
+            sandbox.publish('restoreVersion', {version: historyItems.getSelected()[0], description: event.data.description});
+            event.success();
+        });
+        dialog.show();
+    });
+    
+    dom.find('.btn.display').click(function(e) {
+        sandbox.publish('displayVersion', {version: historyItems.getSelected()[0]});
+    });
+        
+    var addHistoryItem = function(item, options) {
+        historyItems.add(item);
+        var view = new ItemView(item);
+        itemViews.push(view);
+        domNodes.itemList.prepend(view.dom);
+        if(options.animate) {
+            view.dom.hide().slideDown();
+        }
+    };
+    
+    var toggleItemViews = function(toggle) {
+        itemViews.forEach(function(view) {
+            if(!historyItems.isSelected(view.item))
+                view.toggle(toggle);
+        });
+    };
+    
+    var toggleButton = function(btn, toggle) {
+        dom.find('button.'+btn).toggleClass('disabled', !toggle);
+    };
+    
+    var historyItems = {
+        _itemsById: {},
+        _selected: [],
+        select: function(item) {
+            if(this._selected.length < 2) {
+                this._selected.push(item.version);
+                this._updateUI();
+                return true;
+            }
+            return false;
+        },
+        unselect: function(item) {
+            this._selected = _.without(this._selected, item.version);
+            this._updateUI();
+        },
+        add: function(item) {
+            this._itemsById[item.version] = item;
+        },
+        isSelected: function(item) {
+            return _.contains(this._selected, item.version);
+        },
+        getSelected: function() {
+            return this._selected;
+        },
+        _updateUI: function() {
+            var len = this._selected.length;
+            if(len === 0) {
+                toggleButton('compare', false);
+                toggleButton('display', false);
+                toggleButton('restore', false);
+            }
+            if(len === 1) {
+                toggleButton('compare', false);
+                toggleButton('display', true);
+                toggleButton('restore', true);
+            }
+            if(len === 2) {
+                toggleItemViews(false);
+                toggleButton('compare', true);
+                toggleButton('display', false);
+                toggleButton('restore', false);
+            } else {
+                toggleItemViews(true);
+            }
+        }
+    };
+    historyItems._updateUI();
+    
+    var ItemView = function(item) {
+        this.item = item;
+        this.dom = $(this.template(item));
+        this.dom.on('click', _.bind(this.onItemClicked, this));
+    };
+    ItemView.prototype.template = _.template(itemTemplateSrc);
+    ItemView.prototype.onItemClicked = function() {
+        if(historyItems.isSelected(this.item)) {
+            historyItems.unselect(this.item);
+            this.dimItem();
+        } else if(historyItems.select(this.item)) {
+            this.highlightItem();
+        }            
+    };
+    ItemView.prototype.highlightItem = function() {
+        this.dom.addClass('highlighted');
+    };
+    ItemView.prototype.dimItem = function() {
+        this.dom.removeClass('highlighted');
+    };
+    ItemView.prototype.toggle = function(toggle) {
+        this.dom.toggleClass('disabled', !toggle);
+    };
+
+    
+    
+    return {
+        start: function() { sandbox.publish('ready'); },
+        addHistory: function(history, options) {
+            history.forEach(function(historyItem) {
+                addHistoryItem(historyItem, options || {});
+            });
+        },
+        getView: function() {
+            return dom;
+        }
+    };
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/documentHistory/documentHistory.less b/src/editor/modules/documentHistory/documentHistory.less
new file mode 100644 (file)
index 0000000..608c9df
--- /dev/null
@@ -0,0 +1,62 @@
+.rng-module-documentHistory {
+    
+    .item {
+        padding: 5px 5px;
+        margin: 0 0 15px 0;
+        cursor: pointer;
+        
+        .version {
+            float: left;
+            width: 30px;
+            font-weight: bold;
+        }
+        
+        .date, .author, .description {
+            margin: 5px 10px 0 40px;
+        }
+        
+        .description {
+            font-size: .9em;
+        }
+    }
+    
+    .item.highlighted {
+        background: #ffec63;
+    }
+    
+    .item.disabled {
+        cursor: default;
+    }
+    
+    .toolbar {
+        margin: 0 0 15px 0;
+        white-space:nowrap; 
+        word-spacing:0;
+        min-height: 22px;
+        button {
+            margin-right: 10px;
+        }
+    }
+    
+    .rng-module-documentHistory-itemsList {
+        overflow-y: scroll;
+        position: absolute;
+        top: 30px;
+        bottom: 0;
+        left: 0;
+        right: 0;
+        &::-webkit-scrollbar {
+            .rng-mixin-scrollbar;
+        }
+        &::-webkit-scrollbar-track {
+            .rng-mixin-scrollbar-track;
+        }
+        &::-webkit-scrollbar-thumb {
+            .rng-mixin-scrollbar-thumb;
+        }
+        &::-webkit-scrollbar-thumb:window-inactive {
+            .rng-mixin-scrollbar-thumb-window-inactive;
+        }
+    }
+}
+
diff --git a/src/editor/modules/documentHistory/restoreDialog.js b/src/editor/modules/documentHistory/restoreDialog.js
new file mode 100644 (file)
index 0000000..2bf16f6
--- /dev/null
@@ -0,0 +1,55 @@
+define([
+'libs/text!./templates/restoreDialog.html',
+'libs/underscore',
+'libs/backbone',
+'libs/jquery'
+], function(restoreDialogTemplate, _, Backbone, $) {
+
+    var DialogView = Backbone.View.extend({
+        template: _.template(restoreDialogTemplate),
+        events: {
+            'click .restore-btn': 'onSave',
+            'click .cancel-btn': 'close',
+            'click .close': 'close'
+        },
+        initialize: function() {
+            _.bindAll(this);
+            this.actionsDisabled = false;
+        },
+        show: function() {           
+            this.setElement(this.template());
+            this.$el.modal({backdrop: 'static'});
+            this.$el.modal('show');
+            this.$('textarea').focus();
+        },
+        onSave: function(e) {
+            e.preventDefault();
+            var view = this;
+            this.trigger('restore', {
+                data: {description: view.$el.find('textarea').val()},
+                success: function() { view.actionsDisabled = false; view.close(); },
+                error: function() { view.actionsDisabled = false; view.close(); },
+            });
+        },
+        close: function(e) {
+            if(e)
+                e.preventDefault();
+            if(!this.actionsDisabled) {
+                this.$el.modal('hide');
+                this.$el.remove();
+            }
+        },
+        toggleButtons: function(toggle) {
+            this.$('.btn, button').toggleClass('disabled', !toggle);
+            this.$('textarea').attr('disabled', !toggle);
+            this.actionsDisabled = !toggle;
+        }
+    });
+
+    return {
+        create: function() {
+            return new DialogView();
+        }
+    };
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/documentHistory/templates/item.html b/src/editor/modules/documentHistory/templates/item.html
new file mode 100644 (file)
index 0000000..1629226
--- /dev/null
@@ -0,0 +1,6 @@
+<div class="item">
+    <div class="version"><%= version %></div>
+    <div class="date"><%= date %></div>
+    <div class="author"><%= author %></div>
+    <div class="description"><%= description %></div>
+</div>
\ No newline at end of file
diff --git a/src/editor/modules/documentHistory/templates/main.html b/src/editor/modules/documentHistory/templates/main.html
new file mode 100644 (file)
index 0000000..83cd627
--- /dev/null
@@ -0,0 +1,11 @@
+<div class="rng-module-documentHistory">
+    <div class="toolbar">
+        <div class="group">
+            <button class="btn btn-mini compare">Porównaj</button>
+            <button class="btn btn-mini restore">Przywróć</button>
+            <button class="btn btn-mini display">Zobacz</button>
+        </div>
+    </div>
+    <div style="clear:both;"></div>
+    <div class="rng-module-documentHistory-itemsList"></div>
+<div>
\ No newline at end of file
diff --git a/src/editor/modules/documentHistory/templates/restoreDialog.html b/src/editor/modules/documentHistory/templates/restoreDialog.html
new file mode 100644 (file)
index 0000000..ded0a11
--- /dev/null
@@ -0,0 +1,14 @@
+<div class="rng-module-data-saveDialog modal hide static">
+    <div class="modal-header">
+        <button type="button" class="close">&times;</button>
+        <h1>Przywróć wersję</h1>
+    </div>
+    <div class="modal-body">
+        <label>Opisz powód przywrócenia</label>
+        <textarea rows="5"></textarea>
+    </div>
+    <div class="modal-footer">
+        <a href="#" class="btn btn-info btn-mini restore-btn">Przywróć</a>
+        <a href="#" class="btn btn-danger btn-mini cancel-btn">Anuluj</a>
+    </div>
+</div>
\ No newline at end of file
diff --git a/src/editor/modules/documentToolbar/documentToolbar.js b/src/editor/modules/documentToolbar/documentToolbar.js
new file mode 100644 (file)
index 0000000..7fbf6c1
--- /dev/null
@@ -0,0 +1,56 @@
+define(['libs/jquery', 'libs/underscore', 'utils/wlxml', 'libs/text!./template.html'], function($, _, wlxmlUtils, template) {
+
+'use strict';
+
+return function(sandbox) {
+    
+    var view = {
+        node: $(_.template(template)({tagNames: wlxmlUtils.wlxmlTagNames, classNames: wlxmlUtils.wlxmlClassNames})),
+        setup: function() {
+            var view = this;
+            
+            this.node.find('button').click(function(e) {
+                e.stopPropagation();
+                var btn = $(e.currentTarget),
+                    btnName = btn.attr('data-name'),
+                    meta = btn.attr('data-meta'),
+                    params = {},
+                    command = btnName;
+
+                if(btn.attr('data-btn-type') === 'toggle') {
+                    command = 'toggle-' + command;
+                    btn.toggleClass('active');
+                    params.toggle = btn.hasClass('active');
+                }
+
+                if(btnName === 'new-node') {
+                    command = 'newNodeRequested';
+                    params.wlxmlTag = view.getOption('newTag-tag');
+                    params.wlxmlClass = view.getOption('newTag-class');
+                    if(meta) {
+                        var split = meta.split('/');
+                        params.wlxmlTag = split[0];
+                        params.wlxmlClass = split[1];
+                    }
+                } else {
+                    params.meta = meta;
+                }
+
+                sandbox.publish('command', command, params);
+            });
+        },
+        getOption: function(option) {
+            return this.node.find('.rng-module-documentToolbar-toolbarOption[data-option=' + option +']').val();
+        }
+    };
+    
+    view.setup();
+
+    return {
+        start: function() { sandbox.publish('ready'); },
+        getView: function() { return view.node; },
+        getOption: function(option) { return view.getOption(option); }
+    };
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/documentToolbar/documentToolbar.less b/src/editor/modules/documentToolbar/documentToolbar.less
new file mode 100644 (file)
index 0000000..559afde
--- /dev/null
@@ -0,0 +1,28 @@
+.rng-module-documentToolbar {
+    margin: 0;
+    white-space:nowrap; 
+    word-spacing:0;
+    
+    select {
+        line-height: 14px;
+        font-size:9px;
+        height: auto;
+        width: 50px;
+        padding: 1px;
+    -webkit-appearance: button;
+    -moz-appearance: button;
+    appearance: button;
+        margin-bottom: 0;
+    }
+
+    .rng-module-documentToolbar-toolbarGroup {
+        border-width: 0 1px 0 0;
+        border-style: solid;
+        border-color: #ddd;
+        padding: 0 8px 0 0;
+        margin: 0 8px 0 0;
+        float:left;
+    }
+    
+}
+
diff --git a/src/editor/modules/documentToolbar/template.html b/src/editor/modules/documentToolbar/template.html
new file mode 100644 (file)
index 0000000..7ba2bc5
--- /dev/null
@@ -0,0 +1,37 @@
+<div class="rng-module-documentToolbar">
+    <div class="rng-module-documentToolbar-toolbarGroup">
+        <button data-name="new-node" data-meta="header/" data-btn-type="cmd" class="btn btn-mini">nagłówek</button>
+        <button data-name="new-node" data-meta="div/p" data-btn-type="cmd" class="btn btn-mini">paragraf</button>
+        <button data-name="new-node" data-meta="span/cite" data-btn-type="cmd" class="btn btn-mini">cytat</i></button>
+        <button data-name="new-node" data-meta="span/uri" data-btn-type="cmd" class="btn btn-mini">uri</i></button>
+        <select class="rng-module-documentToolbar-toolbarOption" data-option="newTag-tag">
+            <% var options = ['', 'section', 'header', 'div', 'span', 'aside']; %>
+            <% options.forEach(function(option) { %>
+                <option value="<%= option %>" <% if(option==='span') { %>selected<% } %>><%= tagNames[option] %></option>
+            <% }); %>
+        </select>
+        <select class="rng-module-documentToolbar-toolbarOption" data-option="newTag-class">
+            <% var options = ['', 'author', 'title', 'cite', 'cite.code', 'cite.code.xml', 'list.items', 'item', 'uri', 'p', 'footnote', 'todo', 'emp'] %>
+            <% options.forEach(function(option) { %>
+                <option value="<%= option.replace(/\./g, '-') %>" <% if(option==='cite') { %>selected<% } %>><%= classNames[option] %></option>
+            <% }); %>
+        </select>
+        <button data-name="new-node" data-btn-type="cmd" class="btn btn-mini"><i class="icon-plus"></i></button>
+    </div>
+    <div class="rng-module-documentToolbar-toolbarGroup">
+        <button data-name="take-away-node" data-btn-type="cmd" class="btn btn-mini"><i class="icon-remove-circle"></i></button>
+    </div>    
+    <div class="rng-module-documentToolbar-toolbarGroup">
+        <button data-name="list" data-btn-type="cmd" data-meta="bullets" class="btn btn-mini"><i class="icon-list"></i></button>
+        <button data-name="unwrap-node" data-btn-type="cmd" class="btn btn-mini"><i class="icon-arrow-up"></i></button>
+        <button data-name="wrap-node" data-btn-type="cmd" class="btn btn-mini"><i class="icon-arrow-down"></i></button>
+    </div>
+    <div class="rng-module-documentToolbar-toolbarGroup">
+        <button data-name="footnote" data-btn-type="cmd" class="btn btn-mini"><i class="icon-comment"></i></button>
+    </div>
+    <div class="rng-module-documentToolbar-toolbarGroup">
+        <button data-name="grid" data-btn-type="toggle" class="btn btn-mini"><i class="icon-th-large"></i></button>
+    </div>
+
+    <div style="clear: both;"></div>
+</div>
\ No newline at end of file
diff --git a/src/editor/modules/indicator/indicator.js b/src/editor/modules/indicator/indicator.js
new file mode 100644 (file)
index 0000000..827a4b7
--- /dev/null
@@ -0,0 +1,35 @@
+define([
+'libs/jquery',
+'libs/underscore',
+'libs/text!./template.html'
+], function($, _, template) {
+
+'use strict';
+
+return function(sandbox) {
+
+    var view = {
+        dom: $(_.template(template)()),
+        setup: function() {
+        
+        }
+    };
+
+    return {
+        start: function() { sandbox.publish('ready'); },
+        getView: function() { return view.dom; },
+        showMessage: function(msg) {
+            view.dom.html('<span>' + msg + '</span>').show();
+        },
+        clearMessage: function(report) {
+            view.dom.empty();
+            if(report && report.message) {
+                view.dom.html('<span class="success">' + report.message + '</span>').show().fadeOut(4000);
+            }
+        }
+        
+    };
+
+};
+
+});
diff --git a/src/editor/modules/indicator/indicator.less b/src/editor/modules/indicator/indicator.less
new file mode 100644 (file)
index 0000000..48f6e3b
--- /dev/null
@@ -0,0 +1,14 @@
+.rng-module-indicator {
+     span {
+        font-weight: bold;
+        background: #f9edbe;
+        padding: 2px 5px;
+        border: solid 1px #f6e39c;
+        font-size:12px;
+    }
+    
+    span.success {
+        background: #cef9be;
+        border-color: darken(#cef9be, 10%);
+    }
+}
\ No newline at end of file
diff --git a/src/editor/modules/indicator/template.html b/src/editor/modules/indicator/template.html
new file mode 100644 (file)
index 0000000..e4e449b
--- /dev/null
@@ -0,0 +1,3 @@
+<div class="rng-module-indicator">
+
+</div>
\ No newline at end of file
diff --git a/src/editor/modules/mainBar/mainBar.js b/src/editor/modules/mainBar/mainBar.js
new file mode 100644 (file)
index 0000000..ecf6812
--- /dev/null
@@ -0,0 +1,29 @@
+define([
+'libs/jquery',
+'libs/underscore',
+'libs/text!./template.html'], function($, _, template) {
+
+'use strict';
+
+return function(sandbox) {
+
+    var view = $(_.template(template)());
+    view.find('button').click(function(e) {
+        e.preventDefault();
+        sandbox.publish('cmd.' + $(e.target).attr('data-cmd'));
+    });
+
+    return {
+        start: function() { sandbox.publish('ready'); },
+        getView: function() {return view;},
+        setCommandEnabled: function(cmd, enabled) {
+            view.find('[data-cmd='+cmd+']').toggleClass('disabled', !enabled);
+        },
+        setVersion: function(version) {
+            view.find('.version').text(version);
+        }
+    };
+
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/mainBar/mainBar.less b/src/editor/modules/mainBar/mainBar.less
new file mode 100644 (file)
index 0000000..0e2a899
--- /dev/null
@@ -0,0 +1,21 @@
+/*#rng-skelton-menu {
+    float: right;
+}*/
+
+.rng-module-mainBar {
+
+    font-size: 10px;
+
+    li {
+        display: inline;
+        border-width: 0 1px 0 0;
+        border-color: #ddd;
+        border-style: solid;
+        margin: 0 5px 0 0;
+        padding: 0 5px 0 0;
+    }
+
+    ul {
+        list-style-type: none;
+    }
+}
\ No newline at end of file
diff --git a/src/editor/modules/mainBar/template.html b/src/editor/modules/mainBar/template.html
new file mode 100644 (file)
index 0000000..3c43ee6
--- /dev/null
@@ -0,0 +1,7 @@
+<div class="rng-module-mainBar">
+    <ul>
+        <li>wersja: <span class="version"></span></li>
+        <li><button class="btn btn-mini btn-info" data-cmd="save">Zapisz</button></li>
+        <li><a class="btn btn-mini btn-danger" href="/">Wyjdź</a></li>
+    </ul>
+</div>
\ No newline at end of file
diff --git a/src/editor/modules/metadataEditor/metadataEditor.js b/src/editor/modules/metadataEditor/metadataEditor.js
new file mode 100644 (file)
index 0000000..94ad85b
--- /dev/null
@@ -0,0 +1,115 @@
+define([
+'libs/jquery',
+'libs/underscore',
+'./transformations',
+'libs/text!./templates/main.html',
+'libs/text!./templates/item.html'
+], function($, _, transformations, mainTemplate, itemTemplate) {
+
+'use strict';
+
+return function(sandbox) {
+
+    
+    var view = {
+        node: $(_.template(mainTemplate)()),
+        setup: function() {
+            var view = this;
+            var metaTable = this.metaTable = this.node.find('table');
+            
+            this.node.find('.rng-module-metadataEditor-addBtn').click(function() {
+                var newRow = view._addMetaRow('', '');
+                $(newRow.find('td div')[0]).focus();
+                sandbox.publish('metadataChanged', view.getMetadata());
+            });
+            
+            this.metaTable.on('click', '.rng-visualEditor-metaRemoveBtn', function(e) {
+                $(e.target).closest('tr').remove();
+                sandbox.publish('metadataChanged', view.getMetadata());
+            });
+            
+            this.metaTable.on('keydown', '[contenteditable]', function(e) {
+                console.log(e.which);
+                if(e.which === 13) { 
+                    if($(document.activeElement).hasClass('rng-module-metadataEditor-metaItemKey')) {
+                        metaTable.find('.rng-module-metadataEditor-metaItemValue').focus();
+                    } else {
+                        var input = $('<input>');
+                        input.appendTo('body').focus();
+                        view.node.find('.rng-module-metadataEditor-addBtn').focus();
+                        input.remove();
+                    }
+                    e.preventDefault();
+                }
+            });
+            
+            
+            var onKeyUp = function(e) {
+                if(e.which !== 13)
+                    sandbox.publish('metadataChanged', view.getMetadata());
+            };
+            this.metaTable.on('keyup', '[contenteditable]', _.throttle(onKeyUp, 500));
+        },
+        getMetadata: function() {
+            var toret = {};
+            this.node.find('tr').each(function() {
+                var tr = $(this);
+                var inputs = $(this).find('td [contenteditable]');
+                var key = $(inputs[0]).text();
+                var value = $(inputs[1]).text();
+                toret[key] = value;
+            });
+            return toret;
+        },
+        setMetadata: function(metadata) {
+            var view = this;
+            this.metaTable.find('tr').remove();
+            _.each(_.keys(metadata), function(key) {    
+                view._addMetaRow(key, metadata[key]);
+            });
+        },
+        _addMetaRow: function(key, value) {
+            var newRow = $(_.template(itemTemplate)({key: key || '', value: value || ''}));
+            newRow.appendTo(this.metaTable);
+            return newRow;
+        }
+    };
+    
+    view.setup();
+    
+    return {
+        start: function() {
+            sandbox.publish('ready');
+        },
+        setDocument: function(xml) {
+            view.setMetadata(transformations.getMetadata(xml));
+            sandbox.publish('metadataSet');
+        },
+        getMetadata: function() {
+            return transformations.getXML(view.getMetadata());
+        },
+        getView: function() {
+            return view.node;
+        },
+        attachMetadata: function(document) {
+            var toret = $('<div>');
+            toret.append($(document));
+            var meta = $('<metadata></metadata>\n').append(transformations.getXML(view.getMetadata()));
+            
+            var metadata = toret.find('metadata');
+            if(metadata.length === 0) {
+                var section = toret.find('section');
+                section = section.length ? $(section[0]) : null;
+                if(section) {
+                    section.prepend(meta);
+                }
+            } else {
+                metadata.replaceWith(meta);
+            }
+            return toret.html();
+        }
+        
+    };
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/metadataEditor/metadataEditor.less b/src/editor/modules/metadataEditor/metadataEditor.less
new file mode 100644 (file)
index 0000000..42bf5c0
--- /dev/null
@@ -0,0 +1,32 @@
+.rng-module-metadataEditor {
+
+    table {
+        margin-bottom:10px;
+    
+        [contenteditable] {
+            cursor: pointer;
+        }
+
+        li:last-child {
+            border-bottom: none !important;
+        }
+
+        tr td:nth-child(1){
+            width: 20%;
+        }
+
+        tr td:nth-child(2)  {
+            width:80%;
+        }
+    }
+
+    .rng-module-metadataEditor-addBtn {
+        float:right;
+        margin-right:6px;
+    }
+
+    .btn {
+        padding:3px;
+        line-height:10px;
+    }
+}
\ No newline at end of file
diff --git a/src/editor/modules/metadataEditor/templates/item.html b/src/editor/modules/metadataEditor/templates/item.html
new file mode 100644 (file)
index 0000000..3920f68
--- /dev/null
@@ -0,0 +1,5 @@
+<tr>
+    <td><div contenteditable="true" class="rng-module-metadataEditor-metaItemKey"><%= key %></div></td>
+    <td><div contenteditable="true" class="rng-module-metadataEditor-metaItemValue"><%= value %></div></td>
+    <td><button class="rng-visualEditor-metaRemoveBtn btn btn-mini btn-danger">x</button></td>
+</tr>
\ No newline at end of file
diff --git a/src/editor/modules/metadataEditor/templates/main.html b/src/editor/modules/metadataEditor/templates/main.html
new file mode 100644 (file)
index 0000000..5826f82
--- /dev/null
@@ -0,0 +1,8 @@
+<div class="rng-module-metadataEditor">
+    <fieldset>
+    <legend>Meta dane</legend>
+    <table class="table table-condensed table-bordered">
+    </table>
+    <button class="rng-module-metadataEditor-addBtn btn btn-mini btn-info">+</button></td>
+    </fieldset>
+</div>
\ No newline at end of file
diff --git a/src/editor/modules/metadataEditor/transformations.js b/src/editor/modules/metadataEditor/transformations.js
new file mode 100644 (file)
index 0000000..a09ee38
--- /dev/null
@@ -0,0 +1,24 @@
+define(['libs/jquery'], function($) {
+
+    'use strict';
+
+    return {
+        getMetadata: function(xml) {
+            var toret = {};
+            $(xml).find('metadata').children().each(function() {
+                var node = $(this);
+                toret[this.nodeName.split(':')[1].toLowerCase()] = node.text();
+            });
+            return toret;
+        },
+        getXML: function(metadata) {
+            var meta = $('<metadata></metadata>\n');
+            _.each(_.keys(metadata), function(key) {
+                meta.append('\n\t<dc:'+key+'>' + metadata[key] + '</dc:'+key+'>');
+            });
+            meta.append('\n');
+            return vkbeautify.xml(meta.html());
+        }
+    };
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/nodeBreadCrumbs/nodeBreadCrumbs.js b/src/editor/modules/nodeBreadCrumbs/nodeBreadCrumbs.js
new file mode 100644 (file)
index 0000000..320a1e8
--- /dev/null
@@ -0,0 +1,65 @@
+define([
+'libs/jquery',
+'libs/underscore',
+'utils/wlxml',
+'libs/text!./template.html'], function($, _, wlxmlUtils, templateSrc) {
+
+'use strict';
+
+return function(sandbox) {
+    
+    var template = _.template(templateSrc);
+    
+    var view = {
+        dom: $('<div>' + template({node:null, parents: null}) + '</div>'),
+        setup: function() {
+            var view = this;
+            this.dom.on('mouseenter', 'a', function(e) {
+                var target = $(e.target);
+                sandbox.publish('elementEntered', target.data('element'));
+            });
+            this.dom.on('mouseleave', 'a', function(e) {
+                var target = $(e.target);
+                sandbox.publish('elementLeft', target.data('element'));
+            });
+            this.dom.on('click', 'a', function(e) {
+                e.preventDefault();
+                var target = $(e.target);
+                sandbox.publish('elementClicked', target.data('element'));
+            });
+        },
+        
+        setNodeElement: function(nodeElement) {
+            this.dom.empty();
+            this.currentNodeElement = nodeElement;
+            var parents = nodeElement.parents();
+            this.dom.html(template({node: nodeElement, parents: parents, tagNames: wlxmlUtils.wlxmlTagNames, classNames: wlxmlUtils.wlxmlClassNames}));
+
+            this.dom.find('li > a[href="#"]').each(function(idx, a) {
+                $(a).data('element', parents[parents.length - 1 - idx]);
+            });
+            this.dom.find('a.active').data('element', nodeElement);
+        },
+        
+        highlightNode: function(node) {
+            this.dom.find('a[data-id="'+node.id+'"]').addClass('rng-common-hoveredNode');
+        },
+        dimNode: function(node) {
+            this.dom.find('a[data-id="'+node.id+'"]').removeClass('rng-common-hoveredNode');
+        }
+    };
+    
+    view.setup();
+    
+    return {
+        start: function() { sandbox.publish('ready'); },
+        getView: function() { return view.dom; },
+        setNodeElement: function(nodeElement) {
+            view.setNodeElement(nodeElement);
+        },
+        highlightNode: function(id) { view.highlightNode(id); },
+        dimNode: function(id) { view.dimNode(id); }
+    };
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/nodeBreadCrumbs/template.html b/src/editor/modules/nodeBreadCrumbs/template.html
new file mode 100644 (file)
index 0000000..afe44de
--- /dev/null
@@ -0,0 +1,10 @@
+<div class="rng-module-nodeBreadCrumbs">
+    <ul class="breadcrumb">
+    <% if(node && parents) { %>
+        <% for(var i = parents.length - 1; i >= 0; i--) { %>
+            <li><a href="#"> <%= tagNames[parents[i].getWlxmlTag()] %><% if(parents[i].getWlxmlClass()) { %>.<%= classNames[parents[i].getWlxmlClass()] %> <% } %></a><span class="divider">/</span></li>
+        <% } %>
+            <li class="active"><%= tagNames[node.getWlxmlTag()] %><% if(node.getWlxmlClass()) { %>.<%= classNames[node.getWlxmlClass()] %> <% } %></span></li>
+    <% } %>
+    </ul>
+</div>
\ No newline at end of file
diff --git a/src/editor/modules/nodeFamilyTree/nodeFamilyTree.js b/src/editor/modules/nodeFamilyTree/nodeFamilyTree.js
new file mode 100644 (file)
index 0000000..45f47c0
--- /dev/null
@@ -0,0 +1,105 @@
+define([
+'libs/jquery',
+'libs/underscore',
+'utils/wlxml',
+'libs/text!./template.html'
+], function($, _, wlxmlUtils, templateSrc) {
+
+'use strict';
+
+return function(sandbox) {
+    
+    var template = _.template(templateSrc);
+    
+    var view = {
+        dom: $('<div>' + template({children: null, parent: null}) + '</div>'),
+        setup: function() {
+            var view = this;
+            this.dom.on('click', 'a', function(e) {
+                var target = $(e.target);
+                sandbox.publish('elementClicked', target.data('element'));
+            });
+            
+            this.dom.on('mouseenter', 'a', function(e) {
+                var target = $(e.target);
+                sandbox.publish('elementEntered', target.data('element'));
+            });
+            this.dom.on('mouseleave', 'a', function(e) {
+                var target = $(e.target);
+                sandbox.publish('elementLeft', target.data('element'));
+            });
+        },
+        setElement: function(element) {
+            console.log('familyTree sets node');
+            var textElement = element.getText ? element : null,
+                nodeElement = element.getText ? element.parent() : element, // TODO: better type detection
+                nodeElementParent = nodeElement.parent(),
+                parent;
+            
+            this.currentNodeElement = nodeElement;
+
+            if(nodeElementParent) {
+                parent = {
+                    repr: wlxmlUtils.wlxmlTagNames[nodeElementParent.getWlxmlTag()] + (nodeElementParent.getWlxmlClass() ? ' / ' + wlxmlUtils.wlxmlClassNames[nodeElementParent.getWlxmlClass()] : '')
+                };
+            }
+        
+            var nodeChildren = nodeElement.children(),
+                children = [];
+            nodeChildren.forEach(function(child) {
+                if(child.getText) {
+                    var text = child.getText();
+                    if(!text)
+                        text = '<pusty tekst>';
+                    else {
+                        if(text.length > 13) {
+                            text = text.substr(0,13) + '...';
+                        }
+                        text = '"' + text + '"';
+                    }
+                    children.push({repr: _.escape(text), bold: child.sameNode(textElement)});
+                } else {
+                    children.push({repr: wlxmlUtils.wlxmlTagNames[child.getWlxmlTag()] + (child.getWlxmlClass() ? ' / ' + wlxmlUtils.wlxmlClassNames[child.getWlxmlClass()] : '')});
+                }
+            });
+            this.dom.empty();
+            this.dom.append($(template({parent: parent, children: children})));
+
+            if(parent) {
+                this.dom.find('.rng-module-nodeFamilyTree-parent').data('element', nodeElementParent)
+            }
+            this.dom.find('li a').each(function(idx, a) {
+                $(a).data('element', nodeChildren[idx]);
+            });
+        },
+        highlightNode: function(canvasNode) {
+            this.dom.find('a[data-id="'+canvasNode.getId()+'"]').addClass('rng-common-hoveredNode');
+        },
+        dimNode: function(canvasNode) {
+            this.dom.find('a[data-id="'+canvasNode.getId()+'"]').removeClass('rng-common-hoveredNode');
+        }
+    };
+    
+    view.setup();
+    
+    return {
+        start: function() {
+            sandbox.publish('ready');
+        },
+        setElement: function(element) {
+            if(!(element.sameNode(view.currentNodeElement)))
+                view.setElement(element);
+        },
+        getView: function() {
+            return view.dom;
+        },
+        highlightNode: function(canvasNode) {
+            view.highlightNode(canvasNode);
+        },
+        dimNode: function(canvasNode) {
+            view.dimNode(canvasNode);
+        }
+    };
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/nodeFamilyTree/nodeFamilyTree.less b/src/editor/modules/nodeFamilyTree/nodeFamilyTree.less
new file mode 100644 (file)
index 0000000..6b4dc77
--- /dev/null
@@ -0,0 +1,38 @@
+.rng-module-nodeFamilyTree {
+    overflow-y: scroll;
+    max-height: 150px;
+    width:100%;
+    margin-top:10px;
+
+    table {
+        width: 90%;
+        margin: 0;
+    
+        tr {
+            td:nth-child(1) {
+                width: 30%;
+            }
+            td:nth-child(2) {
+                width: 70%;
+            }
+            td ul {
+                list-style-type: none;
+                margin: 0;
+            }
+            
+        }
+    }
+    
+    &::-webkit-scrollbar {
+        .rng-mixin-scrollbar;
+    }
+    &::-webkit-scrollbar-track {
+        .rng-mixin-scrollbar-track;
+    }
+    &::-webkit-scrollbar-thumb {
+        .rng-mixin-scrollbar-thumb;
+    }
+    &::-webkit-scrollbar-thumb:window-inactive {
+        .rng-mixin-scrollbar-thumb-window-inactive;
+    }
+}
\ No newline at end of file
diff --git a/src/editor/modules/nodeFamilyTree/template.html b/src/editor/modules/nodeFamilyTree/template.html
new file mode 100644 (file)
index 0000000..9bccfc3
--- /dev/null
@@ -0,0 +1,19 @@
+<div class="rng-module-nodeFamilyTree">
+    <table class="table table-bordered">
+        <tr>
+            <td>powyżej</td>
+            <td><% if(parent) { %><a href="#" class="rng-module-nodeFamilyTree-parent"><%= parent.repr %></a><% } else { %>-<% } %></td>
+        </tr>
+        <tr>
+            <td rowspan="0">poniżej</td>
+            <td>
+                <ul>
+                <% if(!children || children.length === 0) { %>-<% } else { %>
+                    <% children.forEach(function(child) { %>
+                        <li><% if(child.bold) { %><strong><% } %><a href="#"><%= child.repr %></a><% if(child.bold) { %></strong><% } %></li>
+                    <% }); %>
+                <% } %>
+                </ul>
+            </td>
+        </tr>
+</div>
\ No newline at end of file
diff --git a/src/editor/modules/nodePane/metaWidget/metaWidget.js b/src/editor/modules/nodePane/metaWidget/metaWidget.js
new file mode 100644 (file)
index 0000000..14ba7b6
--- /dev/null
@@ -0,0 +1,43 @@
+define([
+'libs/jquery',
+'libs/underscore',
+'libs/backbone',
+'libs/text!./stringField.html'
+], function($, _, Backbone, stringFieldTpl) {
+
+'use strict';
+
+var templates = {
+    string: _.template(stringFieldTpl)
+};
+
+var getAttrElement = function(attr) {
+    var toret = $('<div>');   
+    toret.append(templates.string({name: attr.name, value: attr.value}));
+    return toret;
+};
+
+var MetaWidget = Backbone.View.extend({
+    events: {
+        'change [metaField-name]': 'onMetaFieldChange' 
+    },
+    initialize: function() {
+        var view = this;
+        this.options.attrs.forEach(function(attr) {
+            view.$el.append(getAttrElement(attr));
+        });
+    },
+    onMetaFieldChange: function(e) {
+        var target = $(e.target);
+        this.trigger('valueChanged', target.attr('metaField-name'), target.val());
+    }
+});
+
+
+return {
+    create: function(options) {
+        return new MetaWidget(options);
+    }
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/nodePane/metaWidget/metaWidget.test.js b/src/editor/modules/nodePane/metaWidget/metaWidget.test.js
new file mode 100644 (file)
index 0000000..05b75dc
--- /dev/null
@@ -0,0 +1,35 @@
+define([
+'libs/chai',
+'libs/sinon',
+'modules/nodePane/metaWidget/metaWidget'
+], function(chai, sinon, metaWidget) {
+
+'use strict';
+
+var assert = chai.assert;
+
+describe('metaWidget', function() {
+    it('calls calls registered callback on value change', function() {
+        var dom = $('<div>');
+        var widget = metaWidget.create({
+                el: dom,
+                attrs: [{name: 'uri', type: 'string', value: 'test string'}],
+            });
+
+        var spy = sinon.spy();
+        widget.on('valueChanged', spy);
+        var input = dom.find('input');
+
+        input.change();
+        assert.ok(spy.calledOnce, 'called once');
+        assert.ok(spy.calledWith('uri', 'test string'), 'called with');
+
+        spy.reset();
+        input.val('new val').change();
+        assert.ok(spy.calledOnce, 'called once');
+        assert.ok(spy.calledWith('uri', 'new val'), 'called with new val');
+    });
+});
+
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/nodePane/metaWidget/stringField.html b/src/editor/modules/nodePane/metaWidget/stringField.html
new file mode 100644 (file)
index 0000000..a5156a2
--- /dev/null
@@ -0,0 +1,2 @@
+<label><%= name %></label>
+<input type="text" metaField-name="<%= name %>" value="<%= value %>">
\ No newline at end of file
diff --git a/src/editor/modules/nodePane/nodePane.js b/src/editor/modules/nodePane/nodePane.js
new file mode 100644 (file)
index 0000000..f3fb68a
--- /dev/null
@@ -0,0 +1,44 @@
+define([
+'libs/text!./template.html',
+'libs/jquery',
+'libs/underscore',
+'modules/nodePane/metaWidget/metaWidget',
+'utils/wlxml'
+], function(templateSrc, $, _, metaWidget, wlxmlUtils) {
+
+'use strict';
+
+return function(sandbox) {
+    
+    var view = $(_.template(templateSrc)({tagNames: wlxmlUtils.wlxmlTagNames, classNames: wlxmlUtils.wlxmlClassNames}));
+    
+    view.on('change', 'select', function(e) {
+        var target = $(e.target);
+        var attr = target.attr('class').split('-')[3] === 'tagSelect' ? 'tag' : 'class';
+        sandbox.publish('nodeElementChange', attr, target.val().replace(/-/g, '.'));
+    });
+    
+    return {
+        start: function() {
+            sandbox.publish('ready');
+        },
+        getView: function() {
+            return view;
+        },
+        setNodeElement: function(nodeElement) {
+            view.find('.rng-module-nodePane-tagSelect').val(nodeElement.getWlxmlTag());
+
+            var escapedClassName = (nodeElement.getWlxmlClass() || '').replace(/\./g, '-')
+            view.find('.rng-module-nodePane-classSelect').val(escapedClassName);
+
+            var widget = metaWidget.create({attrs:nodeElement.getWlxmlMetaAttrs()});
+            widget.on('valueChanged', function(key, value) {
+                sandbox.publish('nodeElementChange', key, value);
+            });
+            view.find('.metaFields').empty().append(widget.el);
+        }
+    };
+    
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/nodePane/nodePane.less b/src/editor/modules/nodePane/nodePane.less
new file mode 100644 (file)
index 0000000..b7e4012
--- /dev/null
@@ -0,0 +1,14 @@
+.rng-module-nodePane {
+    label {
+        width: 50px;
+        display: inline-block;
+    }
+    select {
+        width: 100px;
+    }
+
+    input {
+        width: 80px;
+        padding: 0 10px;
+    }
+}
\ No newline at end of file
diff --git a/src/editor/modules/nodePane/template.html b/src/editor/modules/nodePane/template.html
new file mode 100644 (file)
index 0000000..b9a1254
--- /dev/null
@@ -0,0 +1,25 @@
+<div class="rng-module-nodePane">
+    <fieldset>
+        <legend><%= gettext('Current node') %></legend>
+        <div>
+            <label>Tag</label>
+            <select class="rng-module-nodePane-tagSelect">
+                <% var options = ['', 'section', 'header', 'div', 'span', 'aside']; %>
+                <% options.forEach(function(option) { %>
+                    <option value="<%= option %>" <% if(option === '') { %>selected<% } %>><%= tagNames[option] %></option>
+                <% }); %>
+            </select>
+        </div>
+        <div>
+            <label>Klasa</label>
+            <select  class="rng-module-nodePane-classSelect">
+                <% var options = ['', 'author', 'title', 'cite', 'cite.code', 'cite.code.xml', 'list.items', 'list.items.enum', 'item', 'uri', 'p', 'footnote', 'todo', 'emp'] %>
+                <% options.forEach(function(option) { %>
+                    <option value="<%= option.replace(/\./g, '-') %>" <% if(option === '') { %>selected<% } %>><%= classNames[option] %></option>
+                <% }); %>
+            </select>
+        </div>
+        <div class="metaFields">
+        </div>
+    </fieldset>
+</div>
\ No newline at end of file
diff --git a/src/editor/modules/rng/diffLayout.html b/src/editor/modules/rng/diffLayout.html
new file mode 100644 (file)
index 0000000..8b03929
--- /dev/null
@@ -0,0 +1,4 @@
+<div class="rng-module-rng-diffLayout">
+    <div fnpjs-place="left"></div>
+    <div fnpjs-place="right"></div>
+</div>
\ No newline at end of file
diff --git a/src/editor/modules/rng/diffLayout.less b/src/editor/modules/rng/diffLayout.less
new file mode 100644 (file)
index 0000000..0f51948
--- /dev/null
@@ -0,0 +1,20 @@
+.rng-module-rng-diffLayout {
+    [fnpjs-place=left] {
+        width: 300px;
+        margin: 0 20px 0 0px;
+        left: 0;
+    }
+    [fnpjs-place=right] {
+        left: 320px;
+        right: 0;
+    }
+    
+    [fnpjs-place=left], [fnpjs-place=right] {
+        position: absolute;
+        top: 0;
+        bottom: 0;
+        
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/editor/modules/rng/editingLayout.html b/src/editor/modules/rng/editingLayout.html
new file mode 100644 (file)
index 0000000..54a29aa
--- /dev/null
@@ -0,0 +1,7 @@
+<div class="fnp-module-rng-editingLayout">
+    <div fnpjs-place="toolbar"></div>
+    <div class="rng-module-rng2-left" fnpjs-place="leftColumn"></div>
+    <div class="rng-module-rng2-right" fnpjs-place="rightColumn"></div>
+    <div style="clear:both;"></div>
+    <div class="rng-module-rng2-statusBar" fnpjs-place="statusBar"></div>
+</div>
\ No newline at end of file
diff --git a/src/editor/modules/rng/editingLayout.less b/src/editor/modules/rng/editingLayout.less
new file mode 100644 (file)
index 0000000..19d165f
--- /dev/null
@@ -0,0 +1,88 @@
+.rng-module-rng2-left {
+    /*float: left;
+    width: 600px;*/
+}
+
+.rng-module-rng2-right {
+    /*float: right;
+    position: relative;
+    width: 258px;
+    margin-left: 50px;*/
+
+    border-width: 1px 1px 1px 1px;
+    border-style: solid;
+    border-color: #ddd;
+    padding: 5px 15px;
+    
+    p, td, label, input, select {
+        font-size: 11px;
+        line-height:13px;
+    }
+    
+    select {
+        -webkit-appearance: button;
+        -moz-appearance: button;
+        appearance: button;
+        height: auto;
+        line-height: 14px;
+    }
+    
+    legend {
+        font-size:11px;
+        height:30px;
+    }
+    
+    
+    .rng-view-tabs-tabBar {
+        position:absolute;
+        top:-1px;
+        right:-50px;
+        border-width: 1px 1px 1px 0px;
+        border-style: solid;
+        border-color: #ddd;
+        padding: 5px;
+        background: #ededed;
+    }
+    
+    label + select {
+        position:relative;
+        top: 5px;
+    }
+
+}
+
+.rng-module-rng2-statusBar {
+    margin: 10px 5px;
+    font-size:0.9em;
+}
+
+.fnp-module-rng-editingLayout {
+
+    [fnpjs-place="statusBar"] {
+        position: absolute;
+        bottom: 0;
+        left: 0;
+        right: 0;
+        height: 30px;
+        
+    }
+    
+    [fnpjs-place="leftColumn"], [fnpjs-place="rightColumn"] {
+        position: absolute;
+        top: 30px; //
+        bottom: 50px; //
+    }
+    
+    [fnpjs-place="leftColumn"] {
+        left:0;
+        right: 320px;
+    }
+    
+    [fnpjs-place="rightColumn"] {
+        right: 0px;
+        width: 250px;
+        
+    }
+
+
+}
\ No newline at end of file
diff --git a/src/editor/modules/rng/mainLayout.html b/src/editor/modules/rng/mainLayout.html
new file mode 100644 (file)
index 0000000..0cccb9c
--- /dev/null
@@ -0,0 +1,8 @@
+<div id="rng-module-rng-mainLayout">
+    <div fnpjs-place="messages"></div>
+    <div fnpjs-place="topPanel">
+    </div>
+    <div style="clear:both"></div>
+    <div fnpjs-place="mainView">
+    </div>
+</div>
\ No newline at end of file
diff --git a/src/editor/modules/rng/mainLayout.less b/src/editor/modules/rng/mainLayout.less
new file mode 100644 (file)
index 0000000..ac28f02
--- /dev/null
@@ -0,0 +1,43 @@
+#rng-module-rng-mainLayout {
+    position: fixed;
+    top: 5px;
+    bottom: 5px;
+    left: 80px;
+    right: 80px;
+    
+    [fnpjs-place="messages"] {
+        position: absolute;
+        top: 5px;
+        width:100%;
+        text-align:center;
+    }
+    
+    [fnpjs-place="topPanel"] {
+        float: right;
+        position: relative;
+        z-index: 2;
+    }
+    
+    [fnpjs-place="mainView"] {
+        position: absolute;
+        top: 0;
+        left:0;
+        right:0;
+        bottom:0;
+        z-index: 1;
+        
+        > .rng-view-tabs {
+            position: relative;
+            height: 100%;
+
+            > .rng-view-tabs-content {
+                position: absolute;
+                top: 45px;
+                left: 0;
+                right: 0;
+                bottom: 0;
+            }    
+        }
+        
+    }
+}
\ No newline at end of file
diff --git a/src/editor/modules/rng/rng.js b/src/editor/modules/rng/rng.js
new file mode 100644 (file)
index 0000000..59652a7
--- /dev/null
@@ -0,0 +1,326 @@
+define([
+'fnpjs/layout',
+'fnpjs/vbox',
+'views/tabs/tabs',
+'libs/text!./mainLayout.html',
+'libs/text!./editingLayout.html',
+'libs/text!./diffLayout.html',
+], function(layout, vbox, tabs, mainLayoutTemplate, visualEditingLayoutTemplate, diffLayoutTemplate) {
+
+'use strict';
+
+return function(sandbox) {
+    
+    function addMainTab(title, slug, view) {
+        views.mainTabs.addTab(title, slug, view);
+    }
+    
+    var dirty = {
+        sourceEditor: false,
+        documentCanvas: false,
+        metadataEditor: false,
+    };
+    
+    var synchronizeTab = function(slug) {
+        function tabIsDirty(slug) {
+            if(slug === 'editor' && (dirty.documentCanvas || dirty.metadataEditor))
+                return true;
+            if(slug === 'sourceEditor' && dirty.sourceEditor)
+                return true;
+            return false;
+        }
+    
+        if(tabIsDirty(slug)) {
+            var reason, doc;
+            if(slug === 'sourceEditor') {
+                doc = sandbox.getModule('sourceEditor').getDocument();
+                reason = 'source_edit';
+                dirty.sourceEditor = false;
+            }
+            if(slug === 'editor') {
+                doc = dirty.documentCanvas ? sandbox.getModule('documentCanvas').getDocument() : sandbox.getModule('data').getDocument();
+                if(dirty.metadataEditor) {
+                    doc = sandbox.getModule('metadataEditor').attachMetadata(doc);
+                }
+                reason = 'edit';
+                dirty.documentCanvas = dirty.metadataEditor = false;
+            }
+            sandbox.getModule('data').commitDocument(doc, reason);
+        }
+    };
+    
+    var commands = {
+        highlightDocumentElement: function(element, origin) {
+            ///'nodeBreadCrumbs', 'nodeFamilyTree'
+            ['documentCanvas', ].forEach(function(moduleName) {
+                if(!origin || moduleName != origin)
+                    sandbox.getModule(moduleName).highlightElement(element);
+            });
+        },
+        dimDocumentElement: function(element, origin) {
+            //'nodeBreadCrumbs', 'nodeFamilyTree'
+            ['documentCanvas'].forEach(function(moduleName) {
+                if(!origin || moduleName != origin)
+                    sandbox.getModule(moduleName).dimElement(element);
+            });
+        },
+        jumpToDocumentElement: function(element) {
+            sandbox.getModule('documentCanvas').jumpToElement(element);
+        },
+        updateCurrentNodeElement: function(nodeElement) {
+            sandbox.getModule('nodePane').setNodeElement(nodeElement);
+            sandbox.getModule('nodeFamilyTree').setElement(nodeElement);
+            sandbox.getModule('nodeBreadCrumbs').setNodeElement(nodeElement);
+        },
+        updateCurrentTextElement: function(textElement) {
+            sandbox.getModule('nodeFamilyTree').setElement(textElement);
+        },
+        resetDocument: function(document, reason) {
+            var modules = [];
+            if(reason === 'source_edit')
+                modules = ['documentCanvas', 'metadataEditor'];
+            else if (reason === 'edit')
+                modules = ['sourceEditor'];
+            else if (reason === 'revert')
+                modules = ['documentCanvas', 'metadataEditor', 'sourceEditor'];
+                
+            modules.forEach(function(moduleName) {
+                sandbox.getModule(moduleName).setDocument(document);
+            });
+        }
+    };
+    
+
+    var views = {
+        mainLayout: new layout.Layout(mainLayoutTemplate),
+        mainTabs: (new tabs.View()).render(),
+        visualEditing: new layout.Layout(visualEditingLayoutTemplate),
+        visualEditingSidebar: (new tabs.View({stacked: true})).render(),
+        currentNodePaneLayout: new vbox.VBox(),
+        diffLayout: new layout.Layout(diffLayoutTemplate)
+    };
+    
+    views.visualEditing.setView('rightColumn', views.visualEditingSidebar.getAsView());
+    addMainTab('Edytor', 'editor', views.visualEditing.getAsView());
+    addMainTab(gettext('Source'), 'sourceEditor',  '');
+    addMainTab('Historia', 'history', views.diffLayout.getAsView());
+    
+    sandbox.getDOM().append(views.mainLayout.getAsView());
+    
+    views.visualEditingSidebar.addTab({icon: 'pencil'}, 'edit', views.currentNodePaneLayout.getAsView());
+
+    views.mainTabs.on('tabSelected', function(event) {
+        if(event.prevSlug) {
+            synchronizeTab(event.prevSlug);
+        }
+    });
+    
+    /* Events handling */
+    
+    var eventHandlers = {};
+     
+    eventHandlers.sourceEditor = {
+        ready: function() {
+            addMainTab(gettext('Source'), 'sourceEditor',  sandbox.getModule('sourceEditor').getView());
+            sandbox.getModule('sourceEditor').setDocument(sandbox.getModule('data').getDocument());
+        },
+        xmlChanged: function() {
+            dirty.sourceEditor = true;
+        },
+        documentSet: function() {
+            dirty.sourceEditor = false;
+        }
+    };
+    
+    eventHandlers.data = {
+        ready: function() {
+            views.mainLayout.setView('mainView', views.mainTabs.getAsView());
+            
+            _.each(['sourceEditor', 'documentCanvas', 'documentToolbar', 'nodePane', 'metadataEditor', 'nodeFamilyTree', 'nodeBreadCrumbs', 'mainBar', 'indicator', 'documentHistory', 'diffViewer'], function(moduleName) {
+                sandbox.getModule(moduleName).start();
+            });
+        },
+        documentChanged: function(document, reason) {
+            commands.resetDocument(document, reason);
+        },
+        savingStarted: function() {
+            sandbox.getModule('mainBar').setCommandEnabled('save', false);
+            sandbox.getModule('indicator').showMessage(gettext('Saving...'));
+        },
+        savingEnded: function(status) {
+            sandbox.getModule('mainBar').setCommandEnabled('save', true);
+            sandbox.getModule('indicator').clearMessage({message:'Dokument zapisany'});
+        },
+        restoringStarted: function(event) {
+            sandbox.getModule('mainBar').setCommandEnabled('save', false);
+            sandbox.getModule('indicator').showMessage(gettext('Restoring version ') + event.version + '...');
+        },
+        historyItemAdded: function(item) {
+            sandbox.getModule('documentHistory').addHistory([item], {animate: true});
+        },
+        diffFetched: function(diff) {
+            sandbox.getModule('diffViewer').setDiff(diff);
+        },
+        documentReverted: function(event) {
+            commands.resetDocument(event.document, 'revert');
+            sandbox.getModule('mainBar').setCommandEnabled('save', true);
+            sandbox.getModule('indicator').clearMessage({message:'Wersja ' + event.reverted_version + ' przywrócona'});
+            sandbox.getModule('mainBar').setVersion(event.current_version);
+        }
+    };
+    
+    eventHandlers.mainBar = {
+        ready: function() {
+            sandbox.getModule('mainBar').setVersion(sandbox.getModule('data').getDocumentVersion());
+            views.mainLayout.setView('topPanel', sandbox.getModule('mainBar').getView());
+        },
+        'cmd.save': function() {
+            synchronizeTab(views.mainTabs.getCurrentSlug());
+            sandbox.getModule('data').saveDocument();
+        }
+    };
+    
+    eventHandlers.indicator = {
+        ready: function() {
+            views.mainLayout.setView('messages', sandbox.getModule('indicator').getView());
+        }
+    };
+    
+
+    
+    eventHandlers.documentCanvas = {
+        ready: function() {
+            sandbox.getModule('documentCanvas').setDocument(sandbox.getModule('data').getDocument());
+            views.visualEditing.setView('leftColumn', sandbox.getModule('documentCanvas').getView());
+        },
+        documentSet: function() {
+            dirty.documentCanvas = false;
+        },
+        
+        currentTextElementSet: function(textElement) {
+            commands.updateCurrentTextElement(textElement);
+        },
+
+        currentNodeElementSet: function(nodeElement) {
+            commands.updateCurrentNodeElement(nodeElement);
+        },
+        
+        currentNodeElementChanged: function(nodeElement) {
+            commands.updateCurrentNodeElement(nodeElement);
+            dirty.documentCanvas = true;
+        },
+
+        contentChanged: function() {
+            dirty.documentCanvas = true;
+        },
+
+        nodeHovered: function(canvasNode) {
+            commands.highlightDocumentNode(canvasNode);
+        },
+        
+        nodeBlured: function(canvasNode) {
+            commands.dimDocumentNode(canvasNode);
+        }
+    };
+
+    eventHandlers.nodePane = {
+        ready: function() {
+            views.currentNodePaneLayout.appendView(sandbox.getModule('nodePane').getView());
+        },
+        
+        nodeElementChange: function(attr, value) {
+            sandbox.getModule('documentCanvas').modifyCurrentNodeElement(attr, value);
+        }
+    };
+    
+    eventHandlers.metadataEditor = {
+        ready: function() {
+            sandbox.getModule('metadataEditor').setDocument(sandbox.getModule('data').getDocument());
+            views.visualEditingSidebar.addTab({icon: 'info-sign'}, 'metadataEditor', sandbox.getModule('metadataEditor').getView());
+        },
+        metadataChanged: function(metadata) {
+            dirty.metadataEditor = true;
+        },
+        metadataSet: function() {
+            dirty.metadataEditor = false;
+        },
+    };
+    
+    eventHandlers.nodeFamilyTree = {
+        ready: function() {
+            views.currentNodePaneLayout.appendView(sandbox.getModule('nodeFamilyTree').getView());
+        },
+        elementEntered: function(element) {
+            commands.highlightDocumentElement(element, 'nodeFamilyTree');
+        },
+        elementLeft: function(element) {
+            commands.dimDocumentElement(element, 'nodeFamilyTree');
+        },
+        elementClicked: function(element) {
+            commands.jumpToDocumentElement(element);
+        }
+    };
+    
+    eventHandlers.documentToolbar = {
+        ready: function() {
+            views.visualEditing.setView('toolbar', sandbox.getModule('documentToolbar').getView());
+        },
+        command: function(cmd, params) {
+            sandbox.getModule('documentCanvas').command(cmd, params);
+        }
+    };
+    
+    eventHandlers.nodeBreadCrumbs = {
+        ready: function() {
+            views.visualEditing.setView('statusBar', sandbox.getModule('nodeBreadCrumbs').getView());
+        },
+        elementEntered: function(element) {
+            commands.highlightDocumentElement(element, 'nodeBreadCrumbs');
+        },
+        elementLeft: function(element) {
+            commands.dimDocumentElement(element, 'nodeBreadCrumbs');
+        },
+        elementClicked: function(element) {
+            commands.jumpToDocumentElement(element);
+        }        
+    };
+    
+    eventHandlers.documentHistory = {
+        ready: function() {
+            sandbox.getModule('documentHistory').addHistory(sandbox.getModule('data').getHistory());
+            views.diffLayout.setView('left', sandbox.getModule('documentHistory').getView());
+        },
+        compare: function(ver1, ver2) {
+            sandbox.getModule('data').fetchDiff(ver1, ver2);
+        },
+        restoreVersion: function(event) {
+            sandbox.getModule('data').restoreVersion(event);
+        },
+        displayVersion: function(event) {
+            window.open('/' + gettext('editor') + '/' + sandbox.getModule('data').getDocumentId() + '?version=' + event.version, _.uniqueId());
+        }
+    };
+    
+    eventHandlers.diffViewer = {
+        ready: function() {
+            views.diffLayout.setView('right', sandbox.getModule('diffViewer').getView());
+        }
+    };
+    
+    /* api */
+    
+    return {
+        start: function() {
+            sandbox.getModule('data').start();
+        },
+        handleEvent: function(moduleName, eventName, args) {
+            if('')
+                wysiwigHandler.handleEvent(moduleName, eventName, args);
+            else if(eventHandlers[moduleName] && eventHandlers[moduleName][eventName]) {
+                eventHandlers[moduleName][eventName].apply(eventHandlers, args);
+            }
+        }
+    };
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/rng/rng.less b/src/editor/modules/rng/rng.less
new file mode 100644 (file)
index 0000000..196be1f
--- /dev/null
@@ -0,0 +1,3 @@
+@import 'mainLayout.less';
+@import 'editingLayout.less';
+@import 'diffLayout.less';
\ No newline at end of file
diff --git a/src/editor/modules/sourceEditor/sourceEditor.js b/src/editor/modules/sourceEditor/sourceEditor.js
new file mode 100644 (file)
index 0000000..e88e5e1
--- /dev/null
@@ -0,0 +1,40 @@
+define(function() {
+
+'use strict';
+
+return function(sandbox) {
+
+    var view = $(sandbox.getTemplate('main')());
+    
+    var editor = ace.edit(view.find('#rng-sourceEditor-editor')[0]),
+        session = editor.getSession();
+    editor.setTheme("ace/theme/chrome");
+    session.setMode("ace/mode/xml")
+    session.setUseWrapMode(true);
+    
+    $('textarea', view).on('keyup', function() {
+        sandbox.publish('xmlChanged');
+    });
+    
+    editor.getSession().on('change', function() {
+        sandbox.publish('xmlChanged');
+    });
+    return {
+        start: function() {
+            sandbox.publish('ready');
+        },
+        getView: function() {
+            return view;
+        },
+        setDocument: function(document) {
+            editor.setValue(document);
+            editor.gotoLine(0);
+            sandbox.publish('documentSet');
+        },
+        getDocument: function() {
+            return editor.getValue();
+        }
+    };
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/modules/sourceEditor/sourceEditor.less b/src/editor/modules/sourceEditor/sourceEditor.less
new file mode 100644 (file)
index 0000000..003ee6e
--- /dev/null
@@ -0,0 +1,7 @@
+#rng-sourceEditor-editor {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+}
\ No newline at end of file
diff --git a/src/editor/rng.js b/src/editor/rng.js
new file mode 100644 (file)
index 0000000..04a8d4f
--- /dev/null
@@ -0,0 +1,14 @@
+define(function() {
+    
+'use strict';
+    
+return {
+    modules: {}, 
+    initModules: ['rng'],
+    permissions: {
+        'skelton': ['getDOM'],
+        'rng': ['getModule', 'handleEvents', 'getDOM']
+    }
+}
+
+});
\ No newline at end of file
diff --git a/src/editor/styles/common.less b/src/editor/styles/common.less
new file mode 100644 (file)
index 0000000..00e1525
--- /dev/null
@@ -0,0 +1,10 @@
+body {
+    padding-top: 5px;
+}
+
+.rng-common-hoveredNode {
+    border-color: red !important;
+    border-style:solid;
+    border-width:1px;
+}
+
diff --git a/src/editor/styles/main.less b/src/editor/styles/main.less
new file mode 100644 (file)
index 0000000..48fcda9
--- /dev/null
@@ -0,0 +1,15 @@
+@import 'mixins.less';
+@import 'common.less';
+
+@import '../modules/data/data.less';
+@import '../modules/rng/rng.less';
+@import '../modules/documentCanvas/documentCanvas.less';
+@import '../modules/sourceEditor/sourceEditor.less';
+@import '../modules/mainBar/mainBar.less';
+@import '../modules/documentToolbar/documentToolbar.less';
+@import '../modules/documentHistory/documentHistory.less';
+@import '../modules/indicator/indicator.less';
+@import '../modules/nodePane/nodePane.less';
+@import '../modules/nodeFamilyTree/nodeFamilyTree.less';
+@import '../modules/metadataEditor/metadataEditor.less';
+@import '../modules/diffViewer/diffViewer.less';
diff --git a/src/editor/styles/mixins.less b/src/editor/styles/mixins.less
new file mode 100644 (file)
index 0000000..3a14f31
--- /dev/null
@@ -0,0 +1,20 @@
+.rng-mixin-scrollbar {
+    width: 9px;
+}
+
+.rng-mixin-scrollbar-track {
+    -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); 
+    -webkit-border-radius: 10px;
+    border-radius: 10px;
+}
+
+.rng-mixin-scrollbar-thumb {
+    -webkit-border-radius: 10px;
+    border-radius: 10px;
+    background: rgba(73,175,205,0.8); 
+    -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.5); 
+}
+
+.rng-mixin-scrollbar-thumb-window-inactive {
+    background: rgba(73,175,205,0.4); 
+}
\ No newline at end of file
diff --git a/src/editor/utils/wlxml.js b/src/editor/utils/wlxml.js
new file mode 100644 (file)
index 0000000..5f1b84d
--- /dev/null
@@ -0,0 +1,36 @@
+define([
+
+], function() {
+    
+'use strict';
+
+
+
+return {
+    wlxmlTagNames: {
+        '': '',
+        section: 'sekcja',
+        header: 'nagłówek',
+        div: 'blok',
+        span: 'tekst',
+        aside: 'poboczny'
+    },
+    wlxmlClassNames: {
+        '': '',
+        author: 'autor',
+        title: 'tytuł',
+        cite: 'cytat',
+        'cite.code': 'cytat.kod',
+        'cite.code.xml': 'cytat.kod.xml',
+        'list.items': 'lista',
+        'list.items.enum': 'lista.numerowana',
+        item: 'element',
+        uri: 'uri',
+        p: 'paragraf',
+        footnote: 'przypis',
+        todo: 'todo',
+        emp: 'wyróżnienie'
+    }
+};
+
+});
\ No newline at end of file
diff --git a/src/editor/views/tabs/tabs.js b/src/editor/views/tabs/tabs.js
new file mode 100644 (file)
index 0000000..b910b92
--- /dev/null
@@ -0,0 +1,109 @@
+define([
+'libs/text!./templates/main.html',
+'libs/text!./templates/handle.html',
+'libs/underscore',
+'libs/backbone'
+], function(mainTemplate, handleTemplate, _, Backbone) {
+    'use strict';
+    
+    var View = Backbone.View.extend({
+        className: 'rng-view-tabs',
+        
+        events: {
+            'click ul a, i': '_onTabTitleClicked'
+        },
+        
+        initialize: function(options) {
+            this.options = options || {};
+            this.template = _.template(mainTemplate);
+            this.handleTemplate = _.template(handleTemplate);
+            this.contents = {};
+            this.selectedTab = null;
+        },
+        
+        render: function() {
+            this.$el.html(this.template());
+            this.nodes = {
+                tabBar: this.$('.rng-view-tabs-tabBar'),
+                content: this.$('.rng-view-tabs-content')
+            };
+            
+            if(this.options.stacked) {
+                this.nodes.tabBar.addClass('nav-stacked nav-pills').removeClass('nav-tabs');
+            }
+            if(this.options.position === 'right') {
+                this.$el.addClass('tabs-right');
+                this.nodes.content.addClass('tab-content');
+            }
+            return this;
+        },
+        
+        addTab: function(title, slug, content) {
+            if(this.contents[slug]) {
+                this.contents[slug].detach();
+            }
+            this.contents[slug] = content;
+            
+            var text = (typeof title === 'string') ? title : (title.text || '');
+            var icon = title.icon || null;
+            
+            if(!this.tabExists(slug))
+                this.nodes.tabBar.append(this.handleTemplate({text: text, icon: icon, slug: slug}));
+            if(!this.selectedTab)
+                this.selectTab(slug);
+        },
+        
+        selectTab: function(slug) {
+            if(slug !== this.selectedTab && this.contents[slug]) {
+                this.trigger('leaving', this.selectedTab);
+                
+                if(this.selectedTab) {
+                    var toDetach = this.contents[this.selectedTab];
+                    if(toDetach.onHide)
+                        toDetach.onHide();
+                    toDetach.detach();
+                }
+                this.nodes.content.append(this.contents[slug]);
+                if(this.contents[slug].onShow) {
+                    this.contents[slug].onShow();
+                }
+                this.nodes.tabBar.find('.active').removeClass('active');
+                this.nodes.tabBar.find('a[href="#'+slug+'"]').parent().addClass('active');
+                
+                var prevSlug = this.selectedTab;
+                this.selectedTab = slug;
+                this.trigger('tabSelected', {slug: slug, prevSlug: prevSlug});
+            }
+        },
+        
+        getAsView: function() {
+            return this.$el;
+        },
+        
+        getCurrentSlug: function() {
+            return this.selectedTab;
+        },
+        
+        tabExists: function(slug) {
+            return this.nodes.tabBar.find('a[href="#'+ slug + '"]').length > 0;
+        },
+        
+        /* Events */
+        
+        _onTabTitleClicked: function(e) {
+            e.preventDefault();
+            var target = $(e.target);
+            if(target.is('i'))
+                target = target.parent();
+            var slug = target.attr('href').substr(1);
+            this.selectTab(slug);
+        }
+    });
+
+    
+    return {
+        View: View
+    };
+    
+
+});
\ No newline at end of file
diff --git a/src/editor/views/tabs/templates/handle.html b/src/editor/views/tabs/templates/handle.html
new file mode 100644 (file)
index 0000000..b828ce4
--- /dev/null
@@ -0,0 +1 @@
+<li><a href="#<%= slug %>"><% if(icon) { %><i class="icon-<%= icon %>"></i><% } %><%= text %></a></li>
\ No newline at end of file
diff --git a/src/editor/views/tabs/templates/main.html b/src/editor/views/tabs/templates/main.html
new file mode 100644 (file)
index 0000000..66efc99
--- /dev/null
@@ -0,0 +1,4 @@
+<ul class="rng-view-tabs-tabBar nav nav-tabs">
+</ul>
+<div class="rng-view-tabs-content">
+</div>
diff --git a/src/fnpjs/layout.js b/src/fnpjs/layout.js
new file mode 100644 (file)
index 0000000..c1d110e
--- /dev/null
@@ -0,0 +1,37 @@
+define(['libs/jquery', 'libs/underscore'], function($ ,_) {
+    'use strict';
+      
+    var Layout = function(template) {
+        var layout = this;
+        this.dom = $(_.template(template)());
+        this.views = {};
+        
+        this.dom.onShow = function() {
+            _.values(layout.views).forEach(function(view) {
+                if(view.onShow)
+                    view.onShow();
+            });
+        };
+        this.dom.onHide = function() {
+            _.values(layout.views).forEach(function(view) {
+                if(view.onHide)
+                    view.onHide();
+            });
+        };
+        
+    };
+    
+    Layout.prototype.setView = function(place, view) {
+        this.dom.find('[fnpjs-place=' + place + ']').append(view);
+        this.views[place] = view;
+        if(this.dom.is(':visible') && view.onShow) {
+            view.onShow();
+        }
+    };
+    
+    Layout.prototype.getAsView = function() {
+        return this.dom;
+    };
+    
+    return {Layout: Layout};
+});
\ No newline at end of file
diff --git a/src/fnpjs/runner.js b/src/fnpjs/runner.js
new file mode 100644 (file)
index 0000000..66e0b68
--- /dev/null
@@ -0,0 +1,77 @@
+define(['libs/jquery', 'libs/underscore'], function($, _) {
+
+var Runner = function(app, modules) {
+
+    function getModuleInstance(moduleName) {
+        var module = moduleInstances[moduleName] = (moduleInstances[moduleName] || modules[moduleName](new Sandbox(moduleName)));
+        return module;
+    }
+
+    var bootstrappedData = {},
+        options = {},
+        moduleInstances = {},
+        eventListeners = [];
+        
+    _.each(_.keys(modules || {}), function(moduleName) {
+        if(_.contains(app.permissions[moduleName] || [], 'handleEvents')) {
+            eventListeners.push(moduleName);
+        }
+    });
+
+    
+        
+    var Sandbox = function(moduleName) {
+        this.$ = $;
+        this._ = _;
+        
+        this.getBootstrappedData = function() {
+            return bootstrappedData[moduleName];
+        };
+        
+        this.getTemplate = function(templateName) {
+            return _.template($('[data-template-name="' + moduleName + '.' + templateName + '"]').html().trim());
+        };
+        
+        this.publish = function(eventName) {
+            console.log(moduleName + ': ' + eventName);
+            var eventArgs = Array.prototype.slice.call(arguments, 1);
+            _.each(eventListeners, function(listenerModuleName) {
+                var listener = moduleInstances[listenerModuleName];
+                if(listener) {
+                    listener.handleEvent(moduleName, eventName, eventArgs);
+                }
+            });
+        };
+        
+        var permissions = app.permissions[moduleName];
+        
+        this.getModule = _.contains(permissions, 'getModule') ? function(requestedModuleName) {
+            return getModuleInstance(requestedModuleName);
+        } : undefined;
+        
+        this.getDOM = _.contains(permissions, 'getDOM') ? function() {
+            return $(options.rootSelector);
+        } : undefined;
+        
+    };
+    
+    
+    this.setBootstrappedData = function(moduleName, data) {
+        bootstrappedData[moduleName] = data;
+    };
+    
+    this.start = function(_options) {
+        options = _.extend({
+            rootSelector: 'body'
+        }, _options);
+        app.initModules.forEach(function(moduleName) {
+            getModuleInstance(moduleName).start();
+        });
+    };
+};
+
+return {
+    Runner: Runner
+};
+
+});
\ No newline at end of file
diff --git a/src/fnpjs/vbox.js b/src/fnpjs/vbox.js
new file mode 100644 (file)
index 0000000..dfb5a9f
--- /dev/null
@@ -0,0 +1,13 @@
+define(['libs/jquery', './layout'], function($, layout) {
+    
+    var VBox = function() {};
+    
+    VBox.prototype = new layout.Layout('<div class="fnpjs-vbox"></div>');
+    VBox.prototype.appendView = function(view) {
+        var item = $('<div>').addClass('fnpjs-vbox-item').append(view);
+        this.dom.append(item);
+    };
+    
+    return {VBox: VBox};
+    
+});
\ No newline at end of file
diff --git a/styles/common.less b/styles/common.less
deleted file mode 100644 (file)
index 00e1525..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-body {
-    padding-top: 5px;
-}
-
-.rng-common-hoveredNode {
-    border-color: red !important;
-    border-style:solid;
-    border-width:1px;
-}
-
diff --git a/styles/main.less b/styles/main.less
deleted file mode 100644 (file)
index 48fcda9..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-@import 'mixins.less';
-@import 'common.less';
-
-@import '../modules/data/data.less';
-@import '../modules/rng/rng.less';
-@import '../modules/documentCanvas/documentCanvas.less';
-@import '../modules/sourceEditor/sourceEditor.less';
-@import '../modules/mainBar/mainBar.less';
-@import '../modules/documentToolbar/documentToolbar.less';
-@import '../modules/documentHistory/documentHistory.less';
-@import '../modules/indicator/indicator.less';
-@import '../modules/nodePane/nodePane.less';
-@import '../modules/nodeFamilyTree/nodeFamilyTree.less';
-@import '../modules/metadataEditor/metadataEditor.less';
-@import '../modules/diffViewer/diffViewer.less';
diff --git a/styles/mixins.less b/styles/mixins.less
deleted file mode 100644 (file)
index 3a14f31..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-.rng-mixin-scrollbar {
-    width: 9px;
-}
-
-.rng-mixin-scrollbar-track {
-    -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); 
-    -webkit-border-radius: 10px;
-    border-radius: 10px;
-}
-
-.rng-mixin-scrollbar-thumb {
-    -webkit-border-radius: 10px;
-    border-radius: 10px;
-    background: rgba(73,175,205,0.8); 
-    -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.5); 
-}
-
-.rng-mixin-scrollbar-thumb-window-inactive {
-    background: rgba(73,175,205,0.4); 
-}
\ No newline at end of file
index 16154b7..87eb6b2 100644 (file)
@@ -7,31 +7,43 @@
     });
 
     require({
     });
 
     require({
-      baseUrl: '/base/',
+      baseUrl: '/base/src/editor',
       deps: tests,
       callback: window.__karma__.start,
       deps: tests,
       callback: window.__karma__.start,
+
+        paths: {
+            'fnpjs': '../fnpjs',
+            'libs': '../../libs'
+        },
+
+        map: {
+            '*':
+                {
+                    'libs/jquery': '../../libs/jquery-1.9.1.min',
+                    'libs/underscore': '../../libs/underscore-min',
+                    'libs/bootstrap': '../../libs/bootstrap/js/bootstrap.min',
+                    'libs/backbone': '../../libs/backbone-min',
+                    'libs/sinon': '../../libs/sinon-1.7.3'
+                }
+        },
+
         shim: {
         shim: {
-            'libs/jquery-1.9.1.min': {
+            '../../libs/jquery-1.9.1.min': {
                 exports: '$',
             },
                 exports: '$',
             },
-            'libs/underscore': {
+            '../../libs/underscore-min': {
                 exports: '_'
             },
                 exports: '_'
             },
-            'libs/bootstrap/js/bootstrap.min': {
-                deps: ['libs/jquery-1.9.1.min']
+            '../../libs/bootstrap/js/bootstrap.min': {
+                deps: ['libs/jquery']
             },
             },
-            'libs/backbone': {
+            '../../libs/backbone-min': {
                 exports: 'Backbone',
                 exports: 'Backbone',
-                deps: ['libs/jquery-1.9.1.min', 'libs/underscore']
+                deps: ['libs/jquery', 'libs/underscore']
             },
             },
-            'libs/sinon-1.7.3': {
+            '../../libs/sinon-1.7.3': {
                 exports: 'sinon'
             }
                 exports: 'sinon'
             }
-        },
-        map: {
-            '*': {
-                'libs/sinon': 'libs/sinon-1.7.3'
-            }
         }
     });
 
         }
     });
 
diff --git a/utils/wlxml.js b/utils/wlxml.js
deleted file mode 100644 (file)
index 5f1b84d..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-define([
-
-], function() {
-    
-'use strict';
-
-
-
-return {
-    wlxmlTagNames: {
-        '': '',
-        section: 'sekcja',
-        header: 'nagłówek',
-        div: 'blok',
-        span: 'tekst',
-        aside: 'poboczny'
-    },
-    wlxmlClassNames: {
-        '': '',
-        author: 'autor',
-        title: 'tytuł',
-        cite: 'cytat',
-        'cite.code': 'cytat.kod',
-        'cite.code.xml': 'cytat.kod.xml',
-        'list.items': 'lista',
-        'list.items.enum': 'lista.numerowana',
-        item: 'element',
-        uri: 'uri',
-        p: 'paragraf',
-        footnote: 'przypis',
-        todo: 'todo',
-        emp: 'wyróżnienie'
-    }
-};
-
-});
\ No newline at end of file
diff --git a/views/tabs/tabs.js b/views/tabs/tabs.js
deleted file mode 100644 (file)
index b910b92..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-define([
-'libs/text!./templates/main.html',
-'libs/text!./templates/handle.html',
-'libs/underscore',
-'libs/backbone'
-], function(mainTemplate, handleTemplate, _, Backbone) {
-    'use strict';
-    
-    var View = Backbone.View.extend({
-        className: 'rng-view-tabs',
-        
-        events: {
-            'click ul a, i': '_onTabTitleClicked'
-        },
-        
-        initialize: function(options) {
-            this.options = options || {};
-            this.template = _.template(mainTemplate);
-            this.handleTemplate = _.template(handleTemplate);
-            this.contents = {};
-            this.selectedTab = null;
-        },
-        
-        render: function() {
-            this.$el.html(this.template());
-            this.nodes = {
-                tabBar: this.$('.rng-view-tabs-tabBar'),
-                content: this.$('.rng-view-tabs-content')
-            };
-            
-            if(this.options.stacked) {
-                this.nodes.tabBar.addClass('nav-stacked nav-pills').removeClass('nav-tabs');
-            }
-            if(this.options.position === 'right') {
-                this.$el.addClass('tabs-right');
-                this.nodes.content.addClass('tab-content');
-            }
-            return this;
-        },
-        
-        addTab: function(title, slug, content) {
-            if(this.contents[slug]) {
-                this.contents[slug].detach();
-            }
-            this.contents[slug] = content;
-            
-            var text = (typeof title === 'string') ? title : (title.text || '');
-            var icon = title.icon || null;
-            
-            if(!this.tabExists(slug))
-                this.nodes.tabBar.append(this.handleTemplate({text: text, icon: icon, slug: slug}));
-            if(!this.selectedTab)
-                this.selectTab(slug);
-        },
-        
-        selectTab: function(slug) {
-            if(slug !== this.selectedTab && this.contents[slug]) {
-                this.trigger('leaving', this.selectedTab);
-                
-                if(this.selectedTab) {
-                    var toDetach = this.contents[this.selectedTab];
-                    if(toDetach.onHide)
-                        toDetach.onHide();
-                    toDetach.detach();
-                }
-                this.nodes.content.append(this.contents[slug]);
-                if(this.contents[slug].onShow) {
-                    this.contents[slug].onShow();
-                }
-                this.nodes.tabBar.find('.active').removeClass('active');
-                this.nodes.tabBar.find('a[href="#'+slug+'"]').parent().addClass('active');
-                
-                var prevSlug = this.selectedTab;
-                this.selectedTab = slug;
-                this.trigger('tabSelected', {slug: slug, prevSlug: prevSlug});
-            }
-        },
-        
-        getAsView: function() {
-            return this.$el;
-        },
-        
-        getCurrentSlug: function() {
-            return this.selectedTab;
-        },
-        
-        tabExists: function(slug) {
-            return this.nodes.tabBar.find('a[href="#'+ slug + '"]').length > 0;
-        },
-        
-        /* Events */
-        
-        _onTabTitleClicked: function(e) {
-            e.preventDefault();
-            var target = $(e.target);
-            if(target.is('i'))
-                target = target.parent();
-            var slug = target.attr('href').substr(1);
-            this.selectTab(slug);
-        }
-    });
-
-    
-    return {
-        View: View
-    };
-    
-
-});
\ No newline at end of file
diff --git a/views/tabs/templates/handle.html b/views/tabs/templates/handle.html
deleted file mode 100644 (file)
index b828ce4..0000000
+++ /dev/null
@@ -1 +0,0 @@
-<li><a href="#<%= slug %>"><% if(icon) { %><i class="icon-<%= icon %>"></i><% } %><%= text %></a></li>
\ No newline at end of file
diff --git a/views/tabs/templates/main.html b/views/tabs/templates/main.html
deleted file mode 100644 (file)
index 66efc99..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<ul class="rng-view-tabs-tabBar nav nav-tabs">
-</ul>
-<div class="rng-view-tabs-content">
-</div>