6     'smartxml/transformations',
 
   8 ], function($, _, Backbone, events, transformations, coreTransformations) {
 
  13 var TEXT_NODE = Node.TEXT_NODE;
 
  17 var DocumentNode = function(nativeNode, document) {
 
  19         throw new Error('undefined document for a node');
 
  21     this.document = document;
 
  22     this._setNativeNode(nativeNode);
 
  26 $.extend(DocumentNode.prototype, {
 
  28     transform: function(Transformation, args) {
 
  29         var transformation = new Transformation(this.document, this, args);
 
  30         return this.document.transform(transformation);
 
  33     _setNativeNode: function(nativeNode) {
 
  34         this.nativeNode = nativeNode;
 
  35         this._$ = $(nativeNode);
 
  39         var clone = this._$.clone(true, true);
 
  40         // clone.find('*').addBack().each(function() {
 
  42         //     if(n.data('canvasElement')) {
 
  43         //         n.data('canvasElement', $.extend(true, {}, n.data('canvasElement')));
 
  44         //         n.data('canvasElement').$element = n.data('canvasElement').$element.clone(true, true);
 
  47         return this.document.createDocumentNode(clone[0]);
 
  50     getPath: function(ancestor) {
 
  51         var nodePath = [this].concat(this.parents()),
 
  53         ancestor = ancestor || this.document.root;
 
  55         nodePath.some(function(node, i) {
 
  56             if(node.sameNode(ancestor)) {
 
  62         if(idx !== 'undefined') {
 
  63             nodePath = nodePath.slice(0, idx);
 
  65         toret = nodePath.map(function(node) {return node.getIndex(); });
 
  71         return this.document.root.sameNode(this);
 
  74     sameNode: function(otherNode) {
 
  75         return !!(otherNode) && this.nativeNode === otherNode.nativeNode;
 
  79         var parentNode = this.nativeNode.parentNode;
 
  80         if(parentNode && parentNode.nodeType === Node.ELEMENT_NODE) {
 
  81             return this.document.createDocumentNode(parentNode);
 
  87         var parent = this.parent(),
 
  88             parents = parent ? parent.parents() : [];
 
  90             parents.unshift(parent);
 
  96         var myIdx = this.getIndex();
 
  97         return myIdx > 0 ? this.parent().contents()[myIdx-1] : null;
 
 104         var myIdx = this.getIndex(),
 
 105             parentContents = this.parent().contents();
 
 106         return myIdx < parentContents.length - 1 ? parentContents[myIdx+1] : null;
 
 109     isSurroundedByTextElements: function() {
 
 110         var prev = this.prev(),
 
 112         return prev && (prev.nodeType === Node.TEXT_NODE) && next && (next.nodeType === Node.TEXT_NODE);
 
 115     triggerChangeEvent: function(type, metaData, origParent, nodeWasContained) {
 
 116         var node = (metaData && metaData.node) ? metaData.node : this,
 
 117             event = new events.ChangeEvent(type, $.extend({node: node}, metaData || {}));
 
 118         if(type === 'nodeDetached' || this.document.containsNode(event.meta.node)) {
 
 119             this.document.trigger('change', event);
 
 121         if((type === 'nodeAdded' || type === 'nodeMoved') && !this.document.containsNode(this) && nodeWasContained) {
 
 122              event = new events.ChangeEvent('nodeDetached', {node: node, parent: origParent});
 
 123              this.document.trigger('change', event);
 
 127     getNodeInsertion: function(node) {
 
 128         return this.document.getNodeInsertion(node);
 
 131     getIndex: function() {
 
 135         return this.parent().indexOf(this);
 
 140 var ElementNode = function(nativeNode, document) {
 
 141     DocumentNode.call(this, nativeNode, document);
 
 143 ElementNode.prototype = Object.create(DocumentNode.prototype);
 
 145 $.extend(ElementNode.prototype, {
 
 146     nodeType: Node.ELEMENT_NODE,
 
 148     setData: function(key, value) {
 
 149         if(value !== undefined) {
 
 150             this._$.data(key, value);
 
 152             this._$.removeData(_.keys(this._$.data()));
 
 157     getData: function(key) {
 
 159             return this._$.data(key);
 
 161         return this._$.data();
 
 164     getTagName: function() {
 
 165         return this.nativeNode.tagName.toLowerCase();
 
 168     contents: function(selector) {
 
 170             document = this.document;
 
 172             this._$.children(selector).each(function() {
 
 173                 toret.push(document.createDocumentNode(this));
 
 176             this._$.contents().each(function() {
 
 177                 toret.push(document.createDocumentNode(this));
 
 183     indexOf: function(node) {
 
 184         return this._$.contents().index(node._$);
 
 187     getAttr: function(name) {
 
 188         return this._$.attr(name);
 
 191     getAttrs: function() {
 
 193         for(var i = 0; i < this.nativeNode.attributes.length; i++) {
 
 194             toret.push(this.nativeNode.attributes[i]);
 
 200         var wrapper = $('<div>');
 
 201         wrapper.append(this._getXMLDOMToDump());
 
 202         return wrapper.html();
 
 205     _getXMLDOMToDump: function() {
 
 211 var TextNode = function(nativeNode, document) {
 
 212     DocumentNode.call(this, nativeNode, document);
 
 214 TextNode.prototype = Object.create(DocumentNode.prototype);
 
 216 $.extend(TextNode.prototype, {
 
 217     nodeType: Node.TEXT_NODE,
 
 219     getText: function() {
 
 220         return this.nativeNode.data;
 
 223     triggerTextChangeEvent: function() {
 
 224         var event = new events.ChangeEvent('nodeTextChange', {node: this});
 
 225         this.document.trigger('change', event);
 
 230 var parseXML = function(xml) {
 
 231     return $($.trim(xml))[0];
 
 234 var registerTransformation = function(desc, name, target) {
 
 235     var Transformation = transformations.createContextTransformation(desc, name);
 
 236     //+ to sie powinna nazywac registerTransformationFromDesc or sth
 
 237     //+ ew. spr czy nie override (tylko jesli powyzej sa prototypy to trudno do nich dojsc)
 
 238     target[name] = function() {
 
 240             args = Array.prototype.slice.call(arguments, 0);
 
 241         return instance.transform(Transformation, args);
 
 245 var registerMethod = function(methodName, method, target) {
 
 246     if(target[methodName]) {
 
 247         throw new Error('Cannot extend {target} with method name {methodName}. Name already exists.'
 
 248             .replace('{target}', target)
 
 249             .replace('{methodName}', methodName)
 
 252     target[methodName] = method;
 
 256 var Document = function(xml) {
 
 260     this._transformationLevel = 0;
 
 262     this._nodeMethods = {};
 
 263     this._textNodeMethods = {};
 
 264     this._elementNodeMethods = {};
 
 265     this._nodeTransformations = {};
 
 266     this._textNodeTransformations = {};
 
 267     this._elementNodeTransformations = {};
 
 270 $.extend(Document.prototype, Backbone.Events, {
 
 271     ElementNodeFactory: ElementNode,
 
 272     TextNodeFactory: TextNode,
 
 274     createDocumentNode: function(from) {
 
 275         if(!(from instanceof Node)) {
 
 276             if(from.text !== undefined) {
 
 277                 /* globals document */
 
 278                 from = document.createTextNode(from.text);
 
 280                 var node = $('<' + from.tagName + '>');
 
 282                 _.keys(from.attrs || {}).forEach(function(key) {
 
 283                     node.attr(key, from.attrs[key]);
 
 289         var Factory, typeMethods, typeTransformations;
 
 290         if(from.nodeType === Node.TEXT_NODE) {
 
 291             Factory = this.TextNodeFactory;
 
 292             typeMethods = this._textNodeMethods;
 
 293             typeTransformations = this._textNodeTransformations;
 
 294         } else if(from.nodeType === Node.ELEMENT_NODE) {
 
 295             Factory = this.ElementNodeFactory;
 
 296             typeMethods = this._elementNodeMethods;
 
 297             typeTransformations = this._elementNodeTransformations;
 
 299         var toret = new Factory(from, this);
 
 300         _.extend(toret, this._nodeMethods);
 
 301         _.extend(toret, typeMethods);
 
 302         _.extend(toret, this._nodeTransformations);
 
 303         _.extend(toret, typeTransformations);
 
 307     loadXML: function(xml, options) {
 
 308         options = options || {};
 
 309         this._defineDocumentProperties($(parseXML(xml)));
 
 310         if(!options.silent) {
 
 311             this.trigger('contentSet');
 
 316         return this.root.toXML();
 
 319     containsNode: function(node) {
 
 320         return this.root && (node.nativeNode === this.root.nativeNode || node._$.parents().index(this.root._$) !== -1);
 
 323     getSiblingParents: function(params) {
 
 324         var parents1 = [params.node1].concat(params.node1.parents()).reverse(),
 
 325             parents2 = [params.node2].concat(params.node2.parents()).reverse(),
 
 326             noSiblingParents = null;
 
 328         if(parents1.length === 0 || parents2.length === 0 || !(parents1[0].sameNode(parents2[0]))) {
 
 329             return noSiblingParents;
 
 333         for(i = 0; i < Math.min(parents1.length, parents2.length); i++) {
 
 334             if(parents1[i].sameNode(parents2[i])) {
 
 339         return {node1: parents1[i], node2: parents2[i]};
 
 342     trigger: function() {
 
 343         //console.log('trigger: ' + arguments[0] + (arguments[1] ? ', ' + arguments[1].type : ''));
 
 344         Backbone.Events.trigger.apply(this, arguments);
 
 347     getNodeInsertion: function(node) {
 
 349         if(node instanceof DocumentNode) {
 
 350             insertion.ofNode = node;
 
 351             insertion.insertsNew = !this.containsNode(node);
 
 353           insertion.ofNode = this.createDocumentNode(node);
 
 354           insertion.insertsNew = true;
 
 359     registerMethod: function(methodName, method, dstName) {
 
 363             documentNode: doc._nodeMethods,
 
 364             textNode: doc._textNodeMethods,
 
 365             elementNode: doc._elementNodeMethods
 
 367         registerMethod(methodName, method, destination);
 
 370     registerTransformation: function(desc, name, dstName) {
 
 374             documentNode: doc._nodeTransformations,
 
 375             textNode: doc._textNodeTransformations,
 
 376             elementNode: doc._elementNodeTransformations
 
 378         registerTransformation(desc, name, destination);
 
 381     registerExtension: function(extension) {
 
 384             existingPropertyNames = _.values(this);
 
 386         ['document', 'documentNode', 'elementNode', 'textNode'].forEach(function(dstName) {
 
 387             var dstExtension = extension[dstName];
 
 389                 if(dstExtension.methods) {
 
 390                     _.pairs(dstExtension.methods).forEach(function(pair) {
 
 391                         var methodName = pair[0],
 
 394                         doc.registerMethod(methodName, method, dstName);
 
 399                 if(dstExtension.transformations) {
 
 400                     _.pairs(dstExtension.transformations).forEach(function(pair) {
 
 403                         doc.registerTransformation(desc, name, dstName);
 
 410     transform: function(Transformation, args) {
 
 411         //console.log('transform');
 
 412         var toret, transformation;
 
 414         // ref: odrebnie przygotowanie transformacji, odrebnie jej wykonanie (to pierwsze to analog transform z node)
 
 416         if(typeof Transformation === 'function') {
 
 417             transformation = new Transformation(this, this, args);
 
 419             transformation = Transformation;
 
 422             this._transformationLevel++;
 
 423             toret = transformation.run();
 
 424             if(this._transformationLevel === 1) {
 
 425                 this.undoStack.push(transformation);
 
 427             this._transformationLevel--;
 
 428             //console.log('clearing redo stack');
 
 432             throw new Error('Transformation ' + transformation + ' doesn\'t exist!');
 
 436         var transformation = this.undoStack.pop();
 
 438             transformation.undo();
 
 439             this.redoStack.push(transformation);
 
 443         var transformation = this.redoStack.pop();
 
 445             transformation.run();
 
 446             this.undoStack.push(transformation);
 
 450     getNodeByPath: function(path) {
 
 451         var toret = this.root;
 
 452         path.forEach(function(idx) {
 
 453             toret = toret.contents()[idx];
 
 458     _defineDocumentProperties: function($document) {
 
 460         Object.defineProperty(doc, 'root', {get: function() {
 
 461             return doc.createDocumentNode($document[0]);
 
 462         }, configurable: true});
 
 463         Object.defineProperty(doc, 'dom', {get: function() {
 
 465         }, configurable: true});
 
 471     documentFromXML: function(xml) {
 
 472         var doc = new Document(xml);
 
 473         doc.registerExtension(coreTransformations);
 
 477     elementNodeFromXML: function(xml) {
 
 478         return this.documentFromXML(xml).root;
 
 482     DocumentNode: DocumentNode,
 
 483     ElementNode: ElementNode,