4 'modules/documentCanvas/canvas/utils',
 
   5 'modules/documentCanvas/canvas/wlxmlManagers'
 
   6 ], function($, _, utils, wlxmlManagers) {
 
   9 /* global Node:false, document:false */
 
  12 // DocumentElement represents a text or an element node from WLXML document rendered inside Canvas
 
  13 var DocumentElement = function(wlxmlNode, canvas) {
 
  14     if(arguments.length === 0) {
 
  17     this.wlxmlNode = wlxmlNode;
 
  20     this.$element = this.createDOM();
 
  21     this.$element.data('canvas-element', this);
 
  24 $.extend(DocumentElement.prototype, {
 
  26         return $.contains(document.documentElement, this.dom()[0]);
 
  32         var parents = this.$element.parents('[document-node-element]');
 
  34             return this.canvas.getDocumentElement(parents[0]);
 
  41             parent = this.parent();
 
  44             parent = parent.parent();
 
  49     sameNode: function(other) {
 
  50         return other && (typeof other === typeof this) && other.dom()[0] === this.dom()[0];
 
  53     markAsCurrent: function() {
 
  54         this.canvas.markAsCurrent(this);
 
  57     getVerticallyFirstTextElement: function() {
 
  59         this.children().some(function(child) {
 
  60             if(!child.isVisible()) {
 
  61                 return false; // continue
 
  63             if(child instanceof DocumentTextElement) {
 
  67                 toret = child.getVerticallyFirstTextElement();
 
  76     getPreviousTextElement: function(includeInvisible) {
 
  77         return this.getNearestTextElement('above', includeInvisible);
 
  80     getNextTextElement: function(includeInvisible) {
 
  81         return this.getNearestTextElement('below', includeInvisible);
 
  84     getNearestTextElement: function(direction, includeInvisible) {
 
  85         includeInvisible = includeInvisible !== undefined ? includeInvisible : false;
 
  86         var selector = '[document-text-element]' + (includeInvisible ? '' : ':visible');
 
  87         return this.canvas.getDocumentElement(utils.nearestInDocumentOrder(selector, direction, this.dom()[0]));
 
  90     isVisible: function() {
 
  91         return this instanceof DocumentTextElement || this.getWlxmlTag() !== 'metadata';
 
  94     isInsideList: function() {
 
  95         return this.parents().some(function(parent) {
 
  96             return parent.is('list');
 
 100     exec: function(method) {
 
 101         if(this.manager && this.manager[method]) {
 
 102             return this.manager[method].apply(this.manager, Array.prototype.slice.call(arguments, 1));
 
 108 // DocumentNodeElement represents an element node from WLXML document rendered inside Canvas
 
 109 var DocumentNodeElement = function(wlxmlNode, canvas) {
 
 110     DocumentElement.call(this, wlxmlNode, canvas);
 
 111     wlxmlNode.setData('canvasElement', this);
 
 115 var manipulate = function(e, params, action) {
 
 117     if(params instanceof DocumentElement) {
 
 120         element = e.canvas.createElement(params);
 
 122     var target = (action === 'append' || action === 'prepend') ? e._container() : e.dom();
 
 123     target[action](element.dom());
 
 127 DocumentNodeElement.prototype = new DocumentElement();
 
 130 $.extend(DocumentNodeElement.prototype, {
 
 131     createDOM: function() {
 
 133                 .attr('document-node-element', ''),
 
 134             widgetsContainer = $('<div>')
 
 135                 .addClass('canvas-widgets')
 
 136                 .attr('contenteditable', false),
 
 137             container = $('<div>')
 
 138                 .attr('document-element-content', '');
 
 140         dom.append(widgetsContainer, container);
 
 141         // Make sure widgets aren't navigable with arrow keys
 
 142         widgetsContainer.find('*').add(widgetsContainer).attr('tabindex', -1);
 
 143         this.$element = dom; //@!!!
 
 144         this.setWlxml({tag: this.wlxmlNode.getTagName(), klass: this.wlxmlNode.getClass()});
 
 146         this.wlxmlNode.contents().forEach(function(node) {
 
 147             container.append(this.canvas.createElement(node).dom());
 
 151     _container: function() {
 
 152         return this.dom().children('[document-element-content]');
 
 159     append: function(params) {
 
 160         return manipulate(this, params, 'append');
 
 162     prepend: function(params) {
 
 163         return manipulate(this, params, 'prepend');
 
 165     before: function(params) {
 
 166         return manipulate(this, params, 'before');
 
 169     after: function(params) {
 
 170         return manipulate(this, params, 'after');
 
 172     children: function() {
 
 174         if(this instanceof DocumentTextElement) {
 
 179         var elementContent = this._container().contents();
 
 181         elementContent.each(function() {
 
 182             var childElement = element.canvas.getDocumentElement(this);
 
 183             if(childElement === undefined) {
 
 186             toret.push(childElement);
 
 190     childIndex: function(child) {
 
 191         var children = this.children(),
 
 193         children.forEach(function(c, idx) {
 
 194             if(c.sameNode(child)) {
 
 201     getWlxmlTag: function() {
 
 202         return this._container().attr('wlxml-tag');
 
 204     setWlxmlTag: function(tag) {
 
 205         if(tag === this.getWlxmlTag()) {
 
 209         this._container().attr('wlxml-tag', tag);
 
 210         if(!this.__updatingWlxml) {
 
 211             this._updateWlxmlManager();
 
 214     getWlxmlClass: function() {
 
 215         var klass = this._container().attr('wlxml-class');
 
 217             return klass.replace(/-/g, '.');
 
 221     setWlxmlClass: function(klass) {
 
 222         if(klass === this.getWlxmlClass()) {
 
 226             this._container().attr('wlxml-class', klass.replace(/\./g, '-'));
 
 229             this._container().removeAttr('wlxml-class');
 
 231         if(!this.__updatingWlxml) {
 
 232             this._updateWlxmlManager();
 
 235     setWlxml: function(params) {
 
 236         this.__updatingWlxml = true;
 
 237         if(params.tag !== undefined) {
 
 238             this.setWlxmlTag(params.tag);
 
 240         if(params.klass !== undefined) {
 
 241             this.setWlxmlClass(params.klass);
 
 243         this._updateWlxmlManager();
 
 244         this.__updatingWlxml = false;
 
 246     _updateWlxmlManager: function() {
 
 247         this.manager = wlxmlManagers.getFor(this);
 
 248         this.manager.setup();
 
 251         if(what === 'list' && _.contains(['list.items', 'list.items.enum'], this.getWlxmlClass())) {
 
 256     toggleLabel: function(toggle) {
 
 257         var displayCss = toggle ? 'inline-block' : 'none';
 
 258         var label = this.dom().children('.canvas-widgets').find('.canvas-widget-label');
 
 259         label.css('display', displayCss);
 
 260         this.toggleHighlight(toggle);
 
 263     toggleHighlight: function(toggle) {
 
 264         this._container().toggleClass('highlighted-element', toggle);
 
 267     toggle: function(toggle) {
 
 269             this.manager.toggle(toggle);
 
 275 // DocumentNodeElement represents a text node from WLXML document rendered inside Canvas
 
 276 var DocumentTextElement = function(wlxmlTextNode, canvas) {
 
 277     DocumentElement.call(this, wlxmlTextNode, canvas);
 
 280 $.extend(DocumentTextElement, {
 
 281     isContentContainer: function(htmlElement) {
 
 282         return htmlElement.nodeType === Node.TEXT_NODE && $(htmlElement).parent().is('[document-text-element]');
 
 286 DocumentTextElement.prototype = new DocumentElement();
 
 288 $.extend(DocumentTextElement.prototype, {
 
 289     createDOM: function() {
 
 291             .attr('document-text-element', '')
 
 292             .text(this.wlxmlNode.getText() || utils.unicode.ZWS);
 
 299     setText: function(text) {
 
 300         this.dom().contents()[0].data = text;
 
 302     getText: function(options) {
 
 303         options = _.extend({raw: false}, options || {});
 
 304         var toret = this.dom().text();
 
 306             toret = toret.replace(utils.unicode.ZWS, '');
 
 310     isEmpty: function() {
 
 311         // Having at least Zero Width Space is guaranteed be Content Observer
 
 312         return this.dom().contents()[0].data === utils.unicode.ZWS;
 
 314     after: function(params) {
 
 315         if(params instanceof DocumentTextElement || params.text) {
 
 319         if(params instanceof DocumentNodeElement) {
 
 322             element = this.canvas.createElement(params);
 
 324         this.dom().wrap('<div>');
 
 325         this.dom().parent().after(element.dom());
 
 329     before: function(params) {
 
 330         if(params instanceof DocumentTextElement || params.text) {
 
 334         if(params instanceof DocumentNodeElement) {
 
 337             element = this.canvas.createElement(params);
 
 339         this.dom().wrap('<div>');
 
 340         this.dom().parent().before(element.dom());
 
 345     toggleHighlight: function() {
 
 346         // do nothing for now
 
 351     DocumentElement: DocumentElement,
 
 352     DocumentNodeElement: DocumentNodeElement,
 
 353     DocumentTextElement: DocumentTextElement