From: Aleksander Ɓukasz Date: Thu, 10 Apr 2014 08:51:53 +0000 (+0200) Subject: second batch: works, but failing tests - ie blocking span X-Git-Url: https://git.mdrn.pl/fnpeditor.git/commitdiff_plain/0bea7025f60aa5cc2ed89eed72da0d616f3574ef?hp=fc54b1e72b7ed4992bbc4a2ef3ae7f02393d174d second batch: works, but failing tests - ie blocking span --- diff --git a/src/editor/modules/documentCanvas/canvas/canvas.js b/src/editor/modules/documentCanvas/canvas/canvas.js index c6a7c82..af1ed28 100644 --- a/src/editor/modules/documentCanvas/canvas/canvas.js +++ b/src/editor/modules/documentCanvas/canvas/canvas.js @@ -3,11 +3,13 @@ define([ 'libs/underscore', 'libs/backbone', 'fnpjs/logging/logging', +'wlxml/wlxml', 'modules/documentCanvas/canvas/documentElement', 'modules/documentCanvas/canvas/keyboard', 'modules/documentCanvas/canvas/utils', -'modules/documentCanvas/canvas/wlxmlListener' -], function($, _, Backbone, logging, documentElement, keyboard, utils, wlxmlListener) { +'modules/documentCanvas/canvas/wlxmlListener', +'modules/documentCanvas/canvas/genericElement' +], function($, _, Backbone, logging, wlxml, documentElement, keyboard, utils, wlxmlListener, genericElement) { 'use strict'; /* global document:false, window:false, Node:false, gettext */ @@ -54,8 +56,49 @@ $.extend(TextHandler.prototype, { }); +var ElementsRegister = function() { + this._register = { + '': ElementsRegister.createCanvasElementType(genericElement, documentElement.DocumentNodeElement) + }; + +} +_.extend(ElementsRegister, { + createCanvasElementType: function(elementPrototype, inheritFrom) { + var Constructor = function() { + if(!this.super) { + this.super = inheritFrom.prototype; + } + inheritFrom.apply(this, Array.prototype.slice.call(arguments, 0)); + + }; + Constructor.prototype = Object.create(inheritFrom.prototype); + _.extend(Constructor.prototype, elementPrototype); + return Constructor; + } +}); +_.extend(ElementsRegister.prototype, { + register: function(klass, elementPrototype) { + this._register[klass] = ElementsRegister.createCanvasElementType(elementPrototype, this.getFactoryFor('')); + }, + getFactoryFor: function(klass) { + var Factory; + wlxml.getClassHierarchy(klass).reverse().some(function(klass) { + Factory = this._register[klass]; + if(Factory) { + return true; + } + }.bind(this)); + return Factory; + } +}); + + -var Canvas = function(wlxmlDocument) { +var Canvas = function(wlxmlDocument, elements) { + this.elementsRegister = new ElementsRegister(); + (elements || []).forEach(function(elementDesc) { + this.elementsRegister.register(elementDesc.klass, elementDesc.element); + }.bind(this)); this.eventBus = _.extend({}, Backbone.Events); this.wrapper = $('
').addClass('canvas-wrapper').attr('contenteditable', true); this.wlxmlListener = wlxmlListener.create(this); @@ -77,7 +120,15 @@ $.extend(Canvas.prototype, Backbone.Events, { }, createElement: function(wlxmlNode) { - var Factory = wlxmlNode.nodeType === Node.TEXT_NODE ? documentElement.DocumentTextElement : documentElement.factoryForTag(wlxmlNode.getTagName()); + var Factory; + if(wlxmlNode.nodeType === Node.TEXT_NODE) { + Factory = documentElement.DocumentTextElement + } else { + if(wlxmlNode.getClass() === 'p') { + // debugger; + } + Factory = this.elementsRegister.getFactoryFor(wlxmlNode.getClass()); + } return new Factory(wlxmlNode, this); }, @@ -558,8 +609,8 @@ $.extend(Cursor.prototype, { }); return { - fromXMLDocument: function(wlxmlDocument) { - return new Canvas(wlxmlDocument); + fromXMLDocument: function(wlxmlDocument, elements) { + return new Canvas(wlxmlDocument, elements); } }; diff --git a/src/editor/modules/documentCanvas/canvas/canvas.test.js b/src/editor/modules/documentCanvas/canvas/canvas.test.js index 642438a..8e3c1c8 100644 --- a/src/editor/modules/documentCanvas/canvas/canvas.test.js +++ b/src/editor/modules/documentCanvas/canvas/canvas.test.js @@ -4,7 +4,7 @@ define([ 'libs/sinon', 'modules/documentCanvas/canvas/canvas', 'modules/documentCanvas/canvas/utils', -'wlxml/wlxml' +'wlxml/wlxml', ], function($, chai, sinon, canvas, utils, wlxml) { 'use strict'; @@ -13,7 +13,7 @@ define([ var expect = chai.expect; var getCanvasFromXML = function(xml) { - return canvas.fromXMLDocument(getDocumentFromXML(xml)); + return canvas.fromXMLDocument(getDocumentFromXML(xml), null); }; var getDocumentFromXML = function(xml) { @@ -26,6 +26,17 @@ var wait = function(callback, timeout) { }; +describe('wtf', function() { + it('wtf!', function() { + var c = getCanvasFromXML('
Alice
'), + doc = c.wlxmlDocument; + + var txtNode = doc.root.contents()[0]; + txtNode.wrapWith({tagName: 'header', start: 1, end: 2}); + expect(c.doc().children().length).to.equal(3); + }); +}) + describe('new Canvas', function() { it('abc', function() { var doc = wlxml.WLXMLDocumentFromXML('
Alice has a cat!
'), @@ -70,11 +81,12 @@ describe('Listening to document changes', function() { b = doc.root.contents()[1], c = canvas.fromXMLDocument(doc); + debugger; a.before(b); var sectionChildren = c.doc().children(); expect(sectionChildren.length).to.equal(2); - expect(sectionChildren[0].getWlxmlTag()).to.equal('b'); - expect(sectionChildren[1].getWlxmlTag()).to.equal('a'); + expect(sectionChildren[0].wlxmlNode.getTagName()).to.equal('b'); + expect(sectionChildren[1].wlxmlNode.getTagName()).to.equal('a'); }); it('Handling text node moved', function() { @@ -87,7 +99,7 @@ describe('Listening to document changes', function() { var sectionChildren = c.doc().children(); expect(sectionChildren.length).to.equal(2); expect(sectionChildren[0].getText()).to.equal('Alice'); - expect(sectionChildren[1].getWlxmlTag()).to.equal('a'); + expect(sectionChildren[1].wlxmlNode.getTagName()).to.equal('a'); }); it('Handles nodeTagChange event', function() { @@ -100,7 +112,7 @@ describe('Listening to document changes', function() { var headerNode = doc.root.contents()[0], headerElement = c.doc().children()[0]; - expect(headerElement.getWlxmlTag()).to.equal('header', 'element ok'); + expect(headerElement.wlxmlNode.getTagName()).to.equal('header', 'element ok'); /* Make sure we handle invalidation of reference to wlxmlNode after changing its tag */ expect(headerNode.getData('canvasElement').sameNode(headerElement)).to.equal(true, 'node->element'); @@ -122,7 +134,7 @@ describe('Listening to document changes', function() { expect(aTextElement.getText({raw:true})).to.equal(utils.unicode.ZWS, 'canvas represents this as empty node'); aTextElement.wlxmlNode.detach(); expect(parent.children().length).to.equal(1); - expect(parent.children()[0].getWlxmlTag()).to.equal('span'); + expect(parent.children()[0].wlxmlNode.getTagName()).to.equal('span'); done(); }); }); @@ -167,6 +179,104 @@ describe('Displaying span nodes', function() { }); }); + +describe('Default document changes handling', function() { + it('handles added node', function() { + var c = getCanvasFromXML('
'); + c.wlxmlDocument.root.append({tagName:'div'}); + expect(c.doc().children().length).to.equal(1); + c.wlxmlDocument.root.prepend({tagName:'div'}); + expect(c.doc().children().length).to.equal(2); + + var node = c.wlxmlDocument.root.contents()[1]; + node.before({tagName: 'div'}); + expect(c.doc().children().length).to.equal(3); + node.after({tagName: 'div'}); + expect(c.doc().children().length).to.equal(4); + }); + + it('handles attribute value change for a class attribute', function() { + var c = getCanvasFromXML('
'); + c.wlxmlDocument.root.setAttr('class', 'test'); + expect(c.doc().wlxmlNode.getClass()).to.equal('test'); + }); + + it('handles detached node', function() { + var c = getCanvasFromXML('
'); + c.wlxmlDocument.root.contents()[0].detach(); + expect(c.doc().children().length).to.equal(0); + }); + + it('handles moved node', function() { + var doc = getDocumentFromXML('
'), + a = doc.root.contents()[0], + b = doc.root.contents()[1], + c = canvas.fromXMLDocument(doc); + + a.before(b); + var sectionChildren = c.doc().children(); + expect(sectionChildren.length).to.equal(2); + expect(sectionChildren[0].wlxmlNode.getTagName()).to.equal('b'); + expect(sectionChildren[1].wlxmlNode.getTagName()).to.equal('a'); + }); + + it('handles change in a text node', function() { + var c = getCanvasFromXML('
Alice
'); + c.wlxmlDocument.root.contents()[0].setText('cat'); + expect(c.doc().children()[0].getText()).to.equal('cat'); + }); +}); + +describe('Custom elements based on wlxml class attribute', function() { + it('allows custom rendering', function() { + var c = getCanvasFromXML('
', { + testClass: { + init: function() { + debugger; + this.dom.append(''); + } + } + }); + expect(c.doc().children()[0]._container().children('test').length).to.equal(1); // @! + }); + + it('allows handling changes to internal structure of rendered node', function() { + var c = getCanvasFromXML('
', { + testClass: { + init: function() { + this.header = $('

'); + this.dom.append(this.header); + this.refresh2(); + }, + refresh2: function() { + this.header.text(this.el.wlxmlNode.contents().length); + }, + onNodeAdded: function(event) { + void(event); + this.refresh2(); + } + } + }); + + var node = c.wlxmlDocument.root.contents()[0], + element = node.getData('canvasElement'); + + var header = element.dom().find('h1'); + expect(header.text()).to.equal('1', 'just '); + + node.append({tagName: 'div'}); + + expect(header.text()).to.equal('2', 'added div'); + }); + + describe('Handling unknown class', function() { + it('Inherits default behavior', function() { + var c = getCanvasFromXML('
Hi!
'); + expect(c.doc().children()[0].children()[0].getText()).to.equal('Hi!'); + }); + }); +}); + describe('Cursor', function() { /* globals Node */ var getSelection; diff --git a/src/editor/modules/documentCanvas/canvas/documentElement.js b/src/editor/modules/documentCanvas/canvas/documentElement.js index 2265f92..283f873 100644 --- a/src/editor/modules/documentCanvas/canvas/documentElement.js +++ b/src/editor/modules/documentCanvas/canvas/documentElement.js @@ -1,20 +1,18 @@ define([ 'libs/jquery', 'libs/underscore', -'modules/documentCanvas/canvas/utils', -'modules/documentCanvas/canvas/wlxmlManagers' -], function($, _, utils, wlxmlManagers) { +'modules/documentCanvas/canvas/utils' +], function($, _, utils) { 'use strict'; /* global Node:false, document:false */ - // DocumentElement represents a text or an element node from WLXML document rendered inside Canvas var DocumentElement = function(wlxmlNode, canvas) { this.wlxmlNode = wlxmlNode; this.canvas = canvas; - this.$element = this.createDOM(); + this.createDOM(); this.$element.data('canvas-element', this); }; @@ -56,22 +54,6 @@ $.extend(DocumentElement.prototype, { return other && (typeof other === typeof this) && other.dom()[0] === this.dom()[0]; }, - getVerticallyFirstTextElement: function() { - var toret; - this.children().some(function(child) { - 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); }, @@ -86,11 +68,11 @@ $.extend(DocumentElement.prototype, { return this.canvas.getDocumentElement(utils.nearestInDocumentOrder(selector, direction, this.dom()[0])); }, - exec: function(method) { - if(this.manager && this.manager[method]) { - return this.manager[method].apply(this.manager, Array.prototype.slice.call(arguments, 1)); - } + trigger: function() { + //this.canvas.bus.trigger() } + + }); @@ -98,6 +80,10 @@ $.extend(DocumentElement.prototype, { var DocumentNodeElement = function(wlxmlNode, canvas) { DocumentElement.call(this, wlxmlNode, canvas); wlxmlNode.setData('canvasElement', this); + if(this.init) { + this.init(); + } + }; @@ -108,8 +94,7 @@ var manipulate = function(e, params, action) { } else { element = e.canvas.createElement(params); } - var target = (action === 'append' || action === 'prepend') ? e._container() : e.dom(); - target[action](element.dom()); + e.dom()[action](element.dom()); e.refreshPath(); return element; }; @@ -118,33 +103,31 @@ DocumentNodeElement.prototype = Object.create(DocumentElement.prototype); $.extend(DocumentNodeElement.prototype, { - init: function() { - // noo[] + defaultDisplayStyle: 'block', + addWidget: function(widget) { + this.$element.children('.canvas-widgets').append(widget.DOM ? widget.DOM : widget); + }, + clearWidgets: function() { + this.$element.children('.canvas-widgets').empty(); + }, + handle: function(event) { + var method = 'on' + event.type[0].toUpperCase() + event.type.substr(1); + if(this[method]) { + this[method](event); + } }, createDOM: function() { - var dom = $('
') - .attr('document-node-element', ''), + var wrapper = $('
').attr('document-node-element', ''), widgetsContainer = $('
') .addClass('canvas-widgets') .attr('contenteditable', false), - container = $('
') + contentContainer = $('
') .attr('document-element-content', ''); - dom.append(widgetsContainer, container); - // Make sure widgets aren't navigable with arrow keys + wrapper.append(widgetsContainer, contentContainer); widgetsContainer.find('*').add(widgetsContainer).attr('tabindex', -1); - this.$element = dom; //@!!! - - this.setWlxmlTag(this.wlxmlNode.getTagName()); - this.setWlxmlClass(this.wlxmlNode.getClass()); - - this.wlxmlNode.contents().forEach(function(node) { - container.append(this.canvas.createElement(node).dom()); - }.bind(this)); - - this.init(); - - return dom; + this.$element = wrapper; + this.displayAs(this.defaultDisplayStyle); }, _container: function() { return this.dom().children('[document-element-content]'); @@ -158,12 +141,6 @@ $.extend(DocumentNodeElement.prototype, { } return this; }, - append: function(params) { - return manipulate(this, params, 'append'); - }, - prepend: function(params) { - return manipulate(this, params, 'prepend'); - }, before: function(params) { return manipulate(this, params, 'before'); @@ -171,63 +148,7 @@ $.extend(DocumentNodeElement.prototype, { 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() { - var childElement = element.canvas.getDocumentElement(this); - if(childElement === undefined) { - 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) { - this._container().attr('wlxml-tag', tag); - }, - 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; - } - if(klass) { - this._container().attr('wlxml-class', klass.replace(/\./g, '-')); - } - else { - this._container().removeAttr('wlxml-class'); - } - this.manager = wlxmlManagers.getFor(this); - this.manager.setup(); - - this.refreshPath(); - }, toggleLabel: function(toggle) { var displayCss = toggle ? 'inline-block' : 'none'; var label = this.dom().children('.canvas-widgets').find('.canvas-widget-label'); @@ -239,30 +160,10 @@ $.extend(DocumentNodeElement.prototype, { this._container().toggleClass('highlighted-element', toggle); }, - toggle: function(toggle) { - if(this.manager) { - this.manager.toggle(toggle); - } - }, - isBlock: function() { return this.dom().css('display') === 'block'; }, - containsBlock: function() { - return this.children() - .filter(function(child) { - return child instanceof DocumentNodeElement; - }) - .some(function(child) { - if(child.isBlock()) { - return true; - } else { - return child.containsBlock(); - } - }); - }, - displayAsBlock: function() { this.dom().css('display', 'block'); this._container().css('display', 'block'); @@ -270,6 +171,18 @@ $.extend(DocumentNodeElement.prototype, { displayInline: function() { this.dom().css('display', 'inline'); this._container().css('display', 'inline'); + }, + displayAs: function(what) { + // [this.dom(), this._container()].forEach(e) { + // var isBlock = window.getComputedStyle(e).display === 'block'; + // if(!isBlock && what === 'block') { + // e.css('display', what); + // } else if(isBlock && what === 'inline') { + // e.css('display') + // } + // }) + this.dom().css('display', what); + this._container().css('display', what); } }); @@ -289,7 +202,7 @@ DocumentTextElement.prototype = Object.create(DocumentElement.prototype); $.extend(DocumentTextElement.prototype, { createDOM: function() { - return $('
') + this.$element = $('
') .attr('document-text-element', '') .text(this.wlxmlNode.getText() || utils.unicode.ZWS); }, @@ -348,6 +261,9 @@ $.extend(DocumentTextElement.prototype, { toggleHighlight: function() { // do nothing for now + }, + children: function() { + return []; } }); @@ -356,6 +272,7 @@ var SpanElement = function() { DocumentNodeElement.apply(this, Array.prototype.slice.call(arguments, 0)); }; SpanElement.prototype = $.extend(Object.create(DocumentNodeElement.prototype), { + defaultDisplayStyle: 'inline', init: function() { if(this.containsBlock()) { this.displayAsBlock(); diff --git a/src/editor/modules/documentCanvas/canvas/genericElement.js b/src/editor/modules/documentCanvas/canvas/genericElement.js new file mode 100644 index 0000000..7d5d986 --- /dev/null +++ b/src/editor/modules/documentCanvas/canvas/genericElement.js @@ -0,0 +1,182 @@ +define(function(require) { + +'use strict'; + +var documentElement = require('./documentElement'), + utils = require('./utils'), + wlxmlUtils = require('utils/wlxml'); + +var labelWidget = function(tag, klass) { + return $('') + .addClass('canvas-widget canvas-widget-label') + .text(wlxmlUtils.getTagLabel(tag) + (klass ? ' / ' + wlxmlUtils.getClassLabel(klass) : '')); +}; +void(labelWidget); // for linters; labelWidget is unused on purpose for now + + +var generic = { + onNodeAttrChange: function(event) { + if(event.meta.attr === 'class') { + this.setWlxmlClass(event.meta.newVal); // + } + }, + onNodeAdded: function(event) { + if(event.meta.node.isRoot()) { + this.canvas.reloadRoot(); + return; + } + + var nodeIndex = event.meta.node.getIndex(), + referenceElement, referenceAction, actionArg; + + if(nodeIndex === 0) { + referenceElement = this; + referenceAction = 'prepend'; + } else { + referenceElement = this.children()[nodeIndex-1]; + referenceAction = 'after'; + } + + actionArg = (event.type === 'nodeMoved' && utils.findCanvasElement(event.meta.node, event.meta.parent)) || event.meta.node; + referenceElement[referenceAction](actionArg); + }, + onNodeMoved: function(event) { + return this.onNodeAdded.call(this, event, true); + }, + onNodeDetached: function(event) { + if(event.meta.node.sameNode(this)) { + this.detach(); + } else { + this.children().some(function(child) { + if(child.wlxmlNode.sameNode(event.meta.node)) { + child.detach(); + return true; + } + }); + } + }, + onNodeTextChange: function(event) { + var toSet = event.meta.node.getText(); + this.children().some(function(child) { + if(child.wlxmlNode.sameNode(event.meta.node)) { + if(toSet === '') { + toSet = utils.unicode.ZWS; + } + if(toSet !== child.getText()) { + child.setText(toSet); + } + return true; + } + }); + }, + + prepend: function(param) { + var element; + if(param instanceof documentElement.DocumentElement) { + element = param; + } else { + element = this.canvas.createElement(param); + } + this._container().prepend(element.dom()); + this.refreshPath(); + return element; + }, + + children: function() { + var element = this, + toret = []; + this._container().contents().each(function() { + var childElement = element.canvas.getDocumentElement(this); + if(childElement === undefined) { + return true; + } + + toret.push(childElement); + }); + return toret; + }, + + getFirst: function(e1, e2) { + var idx1 = this.childIndex(e1), + idx2 = this.childIndex(e2); + if(e1 === null || e2 === null) { + return undefined; + } + return idx1 <= idx2 ? e1: e2; + }, + + childIndex: function(child) { + var children = this.children(), + toret = null; + children.forEach(function(c, idx) { + if(c.sameNode(child)) { + toret = idx; + return false; + } + }); + return toret; + }, + + 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; + } + if(klass) { + this._container().attr('wlxml-class', klass.replace(/\./g, '-')); + } + else { + this._container().removeAttr('wlxml-class'); + } + this.refreshPath(); + }, + init: function() { + this._container() + .attr('wlxml-tag', this.wlxmlNode.getTagName()); + this.setWlxmlClass(this.wlxmlNode.getClass()); + this.wlxmlNode.contents().forEach(function(node) { + this._container().append(this.canvas.createElement(node).dom()); + }.bind(this)); + }, + containsBlock: function() { + return this.children() + .filter(function(child) { + return child instanceof documentElement.DocumentNodeElement; + }) + .some(function(child) { + if(child.isBlock()) { + return true; + } else { + return child.containsBlock(); + } + }); + }, + getVerticallyFirstTextElement: function() { + var toret; + this.children().some(function(child) { + if(child instanceof documentElement.DocumentTextElement) { + toret = child; + return true; // break + } else { + toret = child.getVerticallyFirstTextElement(); + if(toret) { + return true; // break + } + } + }); + return toret; + }, +}; + + +return generic; + + + +}); \ No newline at end of file diff --git a/src/editor/modules/documentCanvas/canvas/utils.js b/src/editor/modules/documentCanvas/canvas/utils.js index 56093f6..89ed192 100644 --- a/src/editor/modules/documentCanvas/canvas/utils.js +++ b/src/editor/modules/documentCanvas/canvas/utils.js @@ -53,13 +53,35 @@ var findCanvasElementInParent = function(wlxmlChildNode, wlxmlParentNode) { return toret; }; +var getElementForNode = function(node) { + + var ptr = node.nodeType === Node.TEXT_NODE ? node.parent() : node; + while(!ptr.getData('canvasElement')) { + ptr = ptr.parent(); + } + return ptr.getData('canvasElement'); +}; + +var getElementForDetachedNode = function(node, originalParent) { + var ptr = originalParent; + if(ptr === null) { + return node.getData('canvasElement'); + } + while(!ptr.getData('canvasElement')) { + ptr = ptr.parent(); + } + return ptr.getData('canvasElement'); +}; + return { nearestInDocumentOrder: nearestInDocumentOrder, findCanvasElement: findCanvasElement, findCanvasElementInParent: findCanvasElementInParent, unicode: { ZWS: '\u200B' - } + }, + getElementForNode: getElementForNode, + getElementForDetachedNode: getElementForDetachedNode }; }); diff --git a/src/editor/modules/documentCanvas/canvas/wlxmlListener.js b/src/editor/modules/documentCanvas/canvas/wlxmlListener.js index 7d19e94..faee6c8 100644 --- a/src/editor/modules/documentCanvas/canvas/wlxmlListener.js +++ b/src/editor/modules/documentCanvas/canvas/wlxmlListener.js @@ -35,54 +35,45 @@ $.extend(Listener.prototype, { var _metadataEventHandler = function(event) { - var canvasNode = utils.findCanvasElement(event.meta.node); - canvasNode.exec('updateMetadata'); + var element = utils.getElementForNode(event.meta.node); + element.handle(event); }; + var handlers = { nodeAttrChange: function(event) { + var element = utils.getElementForNode(event.meta.node), + objectChanged; if(event.meta.attr === 'class') { - var canvasNode = utils.findCanvasElement(event.meta.node); - canvasNode.setWlxmlClass(event.meta.newVal); + objectChanged = element.updateObject(); + } + + if(!objectChanged) { + element.handle(event); } }, - nodeAdded: function(event, checkForExistence) { + nodeAdded: function(event) { if(event.meta.node.isRoot()) { this.canvas.reloadRoot(); return; } - var parentElement = utils.findCanvasElement(event.meta.node.parent()), - nodeIndex = event.meta.node.getIndex(), - referenceElement, referenceAction, actionArg; - if(nodeIndex === 0) { - referenceElement = parentElement; - referenceAction = 'prepend'; - } else { - referenceElement = parentElement.children()[nodeIndex-1]; - referenceAction = 'after'; - } + var containingNode = event.meta.node.parent(), + containingElement = utils.getElementForNode(containingNode); - actionArg = (checkForExistence && utils.findCanvasElement(event.meta.node, event.meta.parent)) || event.meta.node; - referenceElement[referenceAction](actionArg); + containingElement.handle(event); }, nodeMoved: function(event) { - return handlers.nodeAdded.call(this, event, true); + return handlers.nodeAdded.call(this, event, true); // + // }, nodeDetached: function(event) { - var canvasNode = utils.findCanvasElementInParent(event.meta.node, event.meta.parent); - canvasNode.detach(); + var element = utils.getElementForDetachedNode(event.meta.node, event.meta.parent); + element.handle(event); }, nodeTextChange: function(event) { - //console.log('wlxmlListener: ' + event.meta.node.getText()); - var canvasElement = utils.findCanvasElement(event.meta.node), - toSet = event.meta.node.getText(); - if(toSet === '') { - toSet = utils.unicode.ZWS; - } - if(toSet !== canvasElement.getText()) { - canvasElement.setText(toSet); - } + var element = utils.getElementForNode(event.meta.node.parent()); + element.handle(event); }, metadataChanged: _metadataEventHandler, diff --git a/src/editor/modules/documentCanvas/documentCanvas.js b/src/editor/modules/documentCanvas/documentCanvas.js index 03b0cf7..2cb3fa1 100644 --- a/src/editor/modules/documentCanvas/documentCanvas.js +++ b/src/editor/modules/documentCanvas/documentCanvas.js @@ -14,9 +14,9 @@ var logger = logging.getLogger('documentCanvas'); return function(sandbox) { - var canvasElements; + var canvasElements = []; - sandbox.getPlugins(function(plugin) { + sandbox.getPlugins().forEach(function(plugin) { canvasElements = canvasElements.concat(plugin.canvasElements || []); }); diff --git a/src/editor/plugins/core/canvasElements.js b/src/editor/plugins/core/canvasElements.js new file mode 100644 index 0000000..37eefb8 --- /dev/null +++ b/src/editor/plugins/core/canvasElements.js @@ -0,0 +1,114 @@ +define(function(require) { + +'use strict'; + + +var widgets = { + footnoteHandler: function(clickHandler) { + var mydom = $('') + .addClass('canvas-widget canvas-widget-footnote-handle') + .css('display', 'inline') + .show(); + + mydom.click(function(e) { + e.stopPropagation(); + clickHandler(); + }); + + return mydom; + }, + commentAdnotation: function(node) { + var widget = { + DOM: $('
').addClass('canvas-widget canvas-widget-comment-adnotation'), + update: function(node) { + var parts = [], + metadata = node.getMetadata(), + dt; + metadata.forEach(function(row) { + parts.push(row.getValue()); + }, 'creator'); + metadata.some(function(row) { + dt = row.getValue(); + return true; // break + }, 'date'); + if(dt) { + parts.push(dt); + } + this.DOM.text(parts.join(', ')); + } + }; + widget.update(node); + return widget; + }, + hideButton: function(clickHandler) { + var mydom = $('x') + .addClass('canvas-widget canvas-widget-hide-button'); + mydom.click(function(e) { + e.stopPropagation(); + clickHandler(); + }); + return mydom; + } +} + + +var comment = { + init: function() { + this.super.init.call(this); + this.commentAdnotation = widgets.commentAdnotation(this.wlxmlNode); + this.addWidget(this.commentAdnotation, 'show'); + this.commentAdnotation.DOM.show(); + }, + + onMetadataChanged: function(event) { + this.commentAdnotation.update(event.meta.node); + }, + onMetadataAdded: function(event) { + return this.onMetadataChanged(event); + }, + onMetadataRemoved: function(event) { + return this.onMetadataChanged(event); + } +}; + +var footnote = { + init: function() { + this.super.init.call(this); + var clickHandler = function() { + this.toggle(true); + }.bind(this); + this.footnoteHandler = widgets.footnoteHandler(clickHandler); + this.addWidget(this.footnoteHandler); + + var closeHandler = function() { + this.toggle(false); + }.bind(this); + this.hideButton = widgets.hideButton(closeHandler); + this.addWidget(this.hideButton); + this.toggle(false, {silent: true}); + }, + toggle: function(toggle, options) { + options = options || {}; + this.hideButton.toggle(toggle); + this.footnoteHandler.toggle(!toggle); + + if(toggle) { + this.displayAsBlock(); + } else { + this.displayInline(); + } + this._container().toggle(toggle); + if(!options.silent) { + this.trigger('elementToggled', toggle, this.documentElement); + } + } +} + + +return [ + {klass: 'comment', element: comment}, + {klass: 'footnote', element: footnote} +]; + + +}); \ No newline at end of file