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(htmlElement, canvas) {
 
  14     if(arguments.length === 0) {
 
  18     this._setupDOMHandler(htmlElement);
 
  22 $.extend(DocumentElement, {
 
  23     fromHTMLElement: function(htmlElement, canvas) {
 
  24         var $element = $(htmlElement);
 
  25         if(htmlElement.nodeType === Node.ELEMENT_NODE && $element.attr('document-node-element') !== undefined) {
 
  26             return DocumentNodeElement.fromHTMLElement(htmlElement, canvas);
 
  28         if($element.attr('document-text-element') !== undefined || (htmlElement.nodeType === Node.TEXT_NODE && $element.parent().attr('document-text-element') !== undefined)) {
 
  29             return DocumentTextElement.fromHTMLElement(htmlElement, canvas);
 
  35 $.extend(DocumentElement.prototype, {
 
  36     _setupDOMHandler: function(htmlElement) {
 
  37         this.$element = $(htmlElement);
 
  40         return $.contains(document.documentElement, this.dom()[0]);
 
  47             args = Array.prototype.slice.call(arguments, 0);
 
  48         if(args.length === 2 && args[1] === undefined) {
 
  49             return dom.removeData(args[0]);
 
  51         return dom.data.apply(dom, arguments);
 
  54         var parents = this.$element.parents('[document-node-element]');
 
  56             return DocumentElement.fromHTMLElement(parents[0], this.canvas);
 
  63             parent = this.parent();
 
  66             parent = parent.parent();
 
  71     sameNode: function(other) {
 
  72         return other && (typeof other === typeof this) && other.dom()[0] === this.dom()[0];
 
  75     markAsCurrent: function() {
 
  76         this.canvas.markAsCurrent(this);
 
  79     getVerticallyFirstTextElement: function() {
 
  81         this.children().some(function(child) {
 
  82             if(!child.isVisible()) {
 
  83                 return false; // continue
 
  85             if(child instanceof DocumentTextElement) {
 
  89                 toret = child.getVerticallyFirstTextElement();
 
  98     getPreviousTextElement: function(includeInvisible) {
 
  99         return this.getNearestTextElement('above', includeInvisible);
 
 102     getNextTextElement: function(includeInvisible) {
 
 103         return this.getNearestTextElement('below', includeInvisible);
 
 106     getNearestTextElement: function(direction, includeInvisible) {
 
 107         includeInvisible = includeInvisible !== undefined ? includeInvisible : false;
 
 108         var selector = '[document-text-element]' + (includeInvisible ? '' : ':visible');
 
 109         return this.canvas.getDocumentElement(utils.nearestInDocumentOrder(selector, direction, this.dom()[0]));
 
 112     isVisible: function() {
 
 113         return this instanceof DocumentTextElement || this.getWlxmlTag() !== 'metadata';
 
 116     isInsideList: function() {
 
 117         return this.parents().some(function(parent) {
 
 118             return parent.is('list');
 
 122     exec: function(method) {
 
 123         var manager = this.data('_wlxmlManager');
 
 124         if(manager[method]) {
 
 125             return manager[method].apply(manager, Array.prototype.slice.call(arguments, 1));
 
 131 // DocumentNodeElement represents an element node from WLXML document rendered inside Canvas
 
 132 var DocumentNodeElement = function(htmlElement, canvas) {
 
 133     DocumentElement.call(this, htmlElement, canvas);
 
 136 $.extend(DocumentNodeElement, {
 
 137     fromHTMLElement: function(htmlElement, canvas) {
 
 138         return new this(htmlElement, canvas);
 
 141     create: function(wlxmlNode, canvas) {
 
 143                 .attr('document-node-element', ''),
 
 144             widgetsContainer = $('<div>')
 
 145                 .addClass('canvas-widgets')
 
 146                 .attr('contenteditable', false),
 
 147             container = $('<div>')
 
 148                 .attr('document-element-content', '');
 
 150         dom.append(widgetsContainer, container);
 
 151         // Make sure widgets aren't navigable with arrow keys
 
 152         widgetsContainer.find('*').add(widgetsContainer).attr('tabindex', -1);
 
 154         var element = this.fromHTMLElement(dom[0], canvas);
 
 156         element.data('wlxmlNode', wlxmlNode);
 
 157         wlxmlNode.setData('canvasElement', element);
 
 159         element.setWlxml({tag: wlxmlNode.getTagName(), klass: wlxmlNode.getClass()});
 
 161         wlxmlNode.contents().forEach(function(node) {
 
 162             container.append(canvas.createElement(node).dom());
 
 169 var manipulate = function(e, params, action) {
 
 171     if(params instanceof DocumentElement) {
 
 174         element = e.canvas.createElement(params);
 
 176     var target = (action === 'append' || action === 'prepend') ? e._container() : e.dom();
 
 177     target[action](element.dom());
 
 181 DocumentNodeElement.prototype = new DocumentElement();
 
 184 $.extend(DocumentNodeElement.prototype, {
 
 185     _container: function() {
 
 186         return this.dom().children('[document-element-content]');
 
 193     append: function(params) {
 
 194         return manipulate(this, params, 'append');
 
 196     prepend: function(params) {
 
 197         return manipulate(this, params, 'prepend');
 
 199     before: function(params) {
 
 200         return manipulate(this, params, 'before');
 
 203     after: function(params) {
 
 204         return manipulate(this, params, 'after');
 
 206     children: function() {
 
 208         if(this instanceof DocumentTextElement) {
 
 213         var elementContent = this._container().contents();
 
 215         elementContent.each(function() {
 
 216             var childElement = DocumentElement.fromHTMLElement(this, element.canvas);
 
 217             if(childElement === undefined) {
 
 220             toret.push(childElement);
 
 224     childIndex: function(child) {
 
 225         var children = this.children(),
 
 227         children.forEach(function(c, idx) {
 
 228             if(c.sameNode(child)) {
 
 235     getWlxmlTag: function() {
 
 236         return this._container().attr('wlxml-tag');
 
 238     setWlxmlTag: function(tag) {
 
 239         if(tag === this.getWlxmlTag()) {
 
 243         this._container().attr('wlxml-tag', tag);
 
 244         if(!this.__updatingWlxml) {
 
 245             this._updateWlxmlManager();
 
 248     getWlxmlClass: function() {
 
 249         var klass = this._container().attr('wlxml-class');
 
 251             return klass.replace(/-/g, '.');
 
 255     setWlxmlClass: function(klass) {
 
 256         if(klass === this.getWlxmlClass()) {
 
 260             this._container().attr('wlxml-class', klass.replace(/\./g, '-'));
 
 263             this._container().removeAttr('wlxml-class');
 
 265         if(!this.__updatingWlxml) {
 
 266             this._updateWlxmlManager();
 
 269     setWlxml: function(params) {
 
 270         this.__updatingWlxml = true;
 
 271         if(params.tag !== undefined) {
 
 272             this.setWlxmlTag(params.tag);
 
 274         if(params.klass !== undefined) {
 
 275             this.setWlxmlClass(params.klass);
 
 277         this._updateWlxmlManager();
 
 278         this.__updatingWlxml = false;
 
 280     _updateWlxmlManager: function() {
 
 281         var manager = wlxmlManagers.getFor(this);
 
 282         this.data('_wlxmlManager', manager);
 
 286         if(what === 'list' && _.contains(['list.items', 'list.items.enum'], this.getWlxmlClass())) {
 
 291     toggleLabel: function(toggle) {
 
 292         var displayCss = toggle ? 'inline-block' : 'none';
 
 293         var label = this.dom().children('.canvas-widgets').find('.canvas-widget-label');
 
 294         label.css('display', displayCss);
 
 295         this.toggleHighlight(toggle);
 
 298     toggleHighlight: function(toggle) {
 
 299         this._container().toggleClass('highlighted-element', toggle);
 
 302     toggle: function(toggle) {
 
 303         var mng = this.data('_wlxmlManager');
 
 311 // DocumentNodeElement represents a text node from WLXML document rendered inside Canvas
 
 312 var DocumentTextElement = function(htmlElement, canvas) {
 
 313     DocumentElement.call(this, htmlElement, canvas);
 
 316 $.extend(DocumentTextElement, {
 
 317     create: function(wlxmlTextNode, canvas) {
 
 319             .attr('document-text-element', '')
 
 320             .text(wlxmlTextNode.getText() || utils.unicode.ZWS),
 
 321         element = this.fromHTMLElement(dom[0], canvas);
 
 322         element.data('wlxmlNode', wlxmlTextNode);
 
 326     fromHTMLElement: function(htmlElement, canvas) {
 
 327         return new this(htmlElement, canvas);
 
 330     isContentContainer: function(htmlElement) {
 
 331         return htmlElement.nodeType === Node.TEXT_NODE && $(htmlElement).parent().is('[document-text-element]');
 
 335 DocumentTextElement.prototype = new DocumentElement();
 
 337 $.extend(DocumentTextElement.prototype, {
 
 338     _setupDOMHandler: function(htmlElement) {
 
 339         var $element = $(htmlElement);
 
 340         if(htmlElement.nodeType === Node.TEXT_NODE) {
 
 341             this.$element = $element.parent();
 
 344             this.$element = $element;
 
 352     setText: function(text) {
 
 353         this.dom().contents()[0].data = text;
 
 355     getText: function(options) {
 
 356         options = _.extend({raw: false}, options || {});
 
 357         var toret = this.dom().text();
 
 359             toret = toret.replace(utils.unicode.ZWS, '');
 
 363     isEmpty: function() {
 
 364         // Having at least Zero Width Space is guaranteed be Content Observer
 
 365         return this.dom().contents()[0].data === utils.unicode.ZWS;
 
 367     after: function(params) {
 
 368         if(params instanceof DocumentTextElement || params.text) {
 
 372         if(params instanceof DocumentNodeElement) {
 
 375             element = this.canvas.createElement(params);
 
 377         this.dom().wrap('<div>');
 
 378         this.dom().parent().after(element.dom());
 
 382     before: function(params) {
 
 383         if(params instanceof DocumentTextElement || params.text) {
 
 387         if(params instanceof DocumentNodeElement) {
 
 390             element = this.canvas.createElement(params, this.canvas);
 
 392         this.dom().wrap('<div>');
 
 393         this.dom().parent().before(element.dom());
 
 398     toggleHighlight: function() {
 
 399         // do nothing for now
 
 404     DocumentElement: DocumentElement,
 
 405     DocumentNodeElement: DocumentNodeElement,
 
 406     DocumentTextElement: DocumentTextElement