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     this.wlxmlNode = wlxmlNode;
 
  17     this.$element = this.createDOM();
 
  18     this.$element.data('canvas-element', this);
 
  21 $.extend(DocumentElement.prototype, {
 
  22     refreshPath: function() {
 
  23         this.parents().forEach(function(parent) {
 
  32         return $.contains(document.documentElement, this.dom()[0]);
 
  38         var parents = this.$element.parents('[document-node-element]');
 
  40             return this.canvas.getDocumentElement(parents[0]);
 
  47             parent = this.parent();
 
  50             parent = parent.parent();
 
  55     sameNode: function(other) {
 
  56         return other && (typeof other === typeof this) && other.dom()[0] === this.dom()[0];
 
  59     getVerticallyFirstTextElement: function() {
 
  61         this.children().some(function(child) {
 
  62             if(child instanceof DocumentTextElement) {
 
  66                 toret = child.getVerticallyFirstTextElement();
 
  75     getPreviousTextElement: function(includeInvisible) {
 
  76         return this.getNearestTextElement('above', includeInvisible);
 
  79     getNextTextElement: function(includeInvisible) {
 
  80         return this.getNearestTextElement('below', includeInvisible);
 
  83     getNearestTextElement: function(direction, includeInvisible) {
 
  84         includeInvisible = includeInvisible !== undefined ? includeInvisible : false;
 
  85         var selector = '[document-text-element]' + (includeInvisible ? '' : ':visible');
 
  86         return this.canvas.getDocumentElement(utils.nearestInDocumentOrder(selector, direction, this.dom()[0]));
 
  89     exec: function(method) {
 
  90         if(this.manager && this.manager[method]) {
 
  91             return this.manager[method].apply(this.manager, Array.prototype.slice.call(arguments, 1));
 
  97 // DocumentNodeElement represents an element node from WLXML document rendered inside Canvas
 
  98 var DocumentNodeElement = function(wlxmlNode, canvas) {
 
  99     DocumentElement.call(this, wlxmlNode, canvas);
 
 100     wlxmlNode.setData('canvasElement', this);
 
 104 var manipulate = function(e, params, action) {
 
 106     if(params instanceof DocumentElement) {
 
 109         element = e.canvas.createElement(params);
 
 111     var target = (action === 'append' || action === 'prepend') ? e._container() : e.dom();
 
 112     target[action](element.dom());
 
 117 DocumentNodeElement.prototype = Object.create(DocumentElement.prototype);
 
 120 $.extend(DocumentNodeElement.prototype, {
 
 124     createDOM: function() {
 
 126                 .attr('document-node-element', ''),
 
 127             widgetsContainer = $('<div>')
 
 128                 .addClass('canvas-widgets')
 
 129                 .attr('contenteditable', false),
 
 130             container = $('<div>')
 
 131                 .attr('document-element-content', '');
 
 133         dom.append(widgetsContainer, container);
 
 134         // Make sure widgets aren't navigable with arrow keys
 
 135         widgetsContainer.find('*').add(widgetsContainer).attr('tabindex', -1);
 
 136         this.$element = dom; //@!!!
 
 138         this.setWlxmlTag(this.wlxmlNode.getTagName());
 
 139         this.setWlxmlClass(this.wlxmlNode.getClass());
 
 141         this.wlxmlNode.contents().forEach(function(node) {
 
 142             container.append(this.canvas.createElement(node).dom());
 
 149     _container: function() {
 
 150         return this.dom().children('[document-element-content]');
 
 153         var parents = this.parents();
 
 157             parents[0].refreshPath();
 
 161     append: function(params) {
 
 162         return manipulate(this, params, 'append');
 
 164     prepend: function(params) {
 
 165         return manipulate(this, params, 'prepend');
 
 167     before: function(params) {
 
 168         return manipulate(this, params, 'before');
 
 171     after: function(params) {
 
 172         return manipulate(this, params, 'after');
 
 174     children: function() {
 
 176         if(this instanceof DocumentTextElement) {
 
 181         var elementContent = this._container().contents();
 
 183         elementContent.each(function() {
 
 184             var childElement = element.canvas.getDocumentElement(this);
 
 185             if(childElement === undefined) {
 
 188             toret.push(childElement);
 
 192     childIndex: function(child) {
 
 193         var children = this.children(),
 
 195         children.forEach(function(c, idx) {
 
 196             if(c.sameNode(child)) {
 
 203     getWlxmlTag: function() {
 
 204         return this._container().attr('wlxml-tag');
 
 206     setWlxmlTag: function(tag) {
 
 207         this._container().attr('wlxml-tag', tag);
 
 209     getWlxmlClass: function() {
 
 210         var klass = this._container().attr('wlxml-class');
 
 212             return klass.replace(/-/g, '.');
 
 216     setWlxmlClass: function(klass) {
 
 217         if(klass === this.getWlxmlClass()) {
 
 221             this._container().attr('wlxml-class', klass.replace(/\./g, '-'));
 
 224             this._container().removeAttr('wlxml-class');
 
 226         this.manager = wlxmlManagers.getFor(this);
 
 227         this.manager.setup();
 
 231     toggleLabel: function(toggle) {
 
 232         var displayCss = toggle ? 'inline-block' : 'none';
 
 233         var label = this.dom().children('.canvas-widgets').find('.canvas-widget-label');
 
 234         label.css('display', displayCss);
 
 235         this.toggleHighlight(toggle);
 
 238     toggleHighlight: function(toggle) {
 
 239         this._container().toggleClass('highlighted-element', toggle);
 
 242     toggle: function(toggle) {
 
 244             this.manager.toggle(toggle);
 
 248     isBlock: function() {
 
 249         return this.dom().css('display') === 'block';
 
 252     containsBlock: function() {
 
 253         return this.children()
 
 254             .filter(function(child) {
 
 255                 return child instanceof DocumentNodeElement;
 
 257             .some(function(child) {
 
 258                 if(child.isBlock()) {
 
 261                     return child.containsBlock();
 
 266     displayAsBlock: function() {
 
 267         this.dom().css('display', 'block');
 
 268         this._container().css('display', 'block');
 
 270     displayInline: function() {
 
 271         this.dom().css('display', 'inline');
 
 272         this._container().css('display', 'inline');
 
 277 // DocumentNodeElement represents a text node from WLXML document rendered inside Canvas
 
 278 var DocumentTextElement = function(wlxmlTextNode, canvas) {
 
 279     DocumentElement.call(this, wlxmlTextNode, canvas);
 
 282 $.extend(DocumentTextElement, {
 
 283     isContentContainer: function(htmlElement) {
 
 284         return htmlElement.nodeType === Node.TEXT_NODE && $(htmlElement).parent().is('[document-text-element]');
 
 288 DocumentTextElement.prototype = Object.create(DocumentElement.prototype);
 
 290 $.extend(DocumentTextElement.prototype, {
 
 291     createDOM: function() {
 
 293             .attr('document-text-element', '')
 
 294             .text(this.wlxmlNode.getText() || utils.unicode.ZWS);
 
 301     setText: function(text) {
 
 302         this.dom().contents()[0].data = text;
 
 304     getText: function(options) {
 
 305         options = _.extend({raw: false}, options || {});
 
 306         var toret = this.dom().text();
 
 308             toret = toret.replace(utils.unicode.ZWS, '');
 
 312     isEmpty: function() {
 
 313         // Having at least Zero Width Space is guaranteed be Content Observer
 
 314         return this.dom().contents()[0].data === utils.unicode.ZWS;
 
 316     after: function(params) {
 
 317         if(params instanceof DocumentTextElement || params.text) {
 
 321         if(params instanceof DocumentNodeElement) {
 
 324             element = this.canvas.createElement(params);
 
 326         this.dom().wrap('<div>');
 
 327         this.dom().parent().after(element.dom());
 
 332     before: function(params) {
 
 333         if(params instanceof DocumentTextElement || params.text) {
 
 337         if(params instanceof DocumentNodeElement) {
 
 340             element = this.canvas.createElement(params);
 
 342         this.dom().wrap('<div>');
 
 343         this.dom().parent().before(element.dom());
 
 349     toggleHighlight: function() {
 
 350         // do nothing for now
 
 355 var SpanElement = function() {
 
 356     DocumentNodeElement.apply(this, Array.prototype.slice.call(arguments, 0));
 
 358 SpanElement.prototype = $.extend(Object.create(DocumentNodeElement.prototype), {
 
 360         if(this.containsBlock()) {
 
 361             this.displayAsBlock();
 
 363             this.displayInline();
 
 366     refresh: function() {
 
 377     DocumentElement: DocumentElement,
 
 378     DocumentNodeElement: DocumentNodeElement,
 
 379     DocumentTextElement: DocumentTextElement, //,
 
 380     factoryForTag: function(tagName) {
 
 381         return elements[tagName] || DocumentNodeElement;